xref: /plugin/include/helper.php (revision 6f0ad9d79afa4a195e1eb82c755f75097a6d5540)
1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Esther Brunner <wikidesign@gmail.com>
5 * @author     Christopher Smith <chris@jalakai.co.uk>
6 * @author     Gina Häußge, Michael Klier <dokuwiki@chimeric.de>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12if (!defined('DOKU_LF')) define('DOKU_LF', "\n");
13if (!defined('DOKU_TAB')) define('DOKU_TAB', "\t");
14if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
15
16class helper_plugin_include extends DokuWiki_Plugin { // DokuWiki_Helper_Plugin
17
18    var $includes     = array();
19    var $toplevel_id  = NULL;
20    var $defaults     = array();
21
22    /**
23     * Constructor loads default config settings once
24     */
25    function helper_plugin_include() {
26        $this->defaults['firstsec']  = $this->getConf('firstseconly');
27        $this->defaults['editbtn']   = $this->getConf('showeditbtn');
28        $this->defaults['taglogos']  = $this->getConf('showtaglogos');
29        $this->defaults['footer']    = $this->getConf('showfooter');
30        $this->defaults['redirect']  = $this->getConf('doredirect');
31        $this->defaults['date']      = $this->getConf('showdate');
32        $this->defaults['user']      = $this->getConf('showuser');
33        $this->defaults['comments']  = $this->getConf('showcomments');
34        $this->defaults['linkbacks'] = $this->getConf('linkbacks');
35        $this->defaults['tags']      = $this->getConf('tags');
36        $this->defaults['link']      = $this->getConf('showlink');
37    }
38
39    function getInfo() {
40        return array(
41                'author' => 'Gina Häußge, Michael Klier, Esther Brunner',
42                'email'  => 'dokuwiki@chimeric.de',
43                'date'   => @file_get_contents(DOKU_PLUGIN . 'blog/VERSION'),
44                'name'   => 'Include Plugin (helper class)',
45                'desc'   => 'Functions to include another page in a wiki page',
46                'url'    => 'http://wiki.splitbrain.org/plugin:include',
47                );
48    }
49
50    /**
51     * Available methods for other plugins
52     */
53    function getMethods() {
54        $result = array();
55        $result[] = array(
56                'name'   => 'get_flags',
57                'desc'   => 'overrides standard values for showfooter and firstseconly settings',
58                'params' => array('flags' => 'array'),
59                );
60        return $result;
61    }
62
63    /**
64     * Overrides standard values for showfooter and firstseconly settings
65     */
66    function get_flags($setflags) {
67        // load defaults
68        $flags = array();
69        $flags = $this->defaults;
70        foreach ($setflags as $flag) {
71            switch ($flag) {
72                case 'footer':
73                    $flags['footer'] = 1;
74                    break;
75                case 'nofooter':
76                    $flags['footer'] = 0;
77                    break;
78                case 'firstseconly':
79                case 'firstsectiononly':
80                    $flags['firstsec'] = 1;
81                    break;
82                case 'fullpage':
83                    $flags['firstsec'] = 0;
84                    break;
85                case 'noheader':
86                    $flags['noheader'] = 1;
87                    break;
88                case 'editbtn':
89                case 'editbutton':
90                    $flags['editbtn'] = 1;
91                    break;
92                case 'noeditbtn':
93                case 'noeditbutton':
94                    $flags['editbtn'] = 0;
95                    break;
96                case 'permalink':
97                    $flags['permalink'] = 1;
98                    break;
99                case 'nopermalink':
100                    $flags['permalink'] = 0;
101                    break;
102                case 'redirect':
103                    $flags['redirect'] = 1;
104                    break;
105                case 'noredirect':
106                    $flags['redirect'] = 0;
107                    break;
108            }
109        }
110        return $flags;
111    }
112
113    /**
114     * Parses the instructions list
115     *
116     * called by the action plugin component, this function is called
117     * recursively for the p_cached_instructions call (when the instructions
118     * need to be updated)
119     *
120     * @author Michael Klier <chi@chimeric.de>
121     */
122    function parse_instructions($id, &$ins) {
123        $num = count($ins);
124
125        $lvl   = 0;
126        $mode  = '';
127        $page  = '';
128        $flags = array();
129
130        for($i=0; $i<$num; $i++) {
131            // set current level
132            if($ins[$i][0] == 'section_open') {
133                $lvl = $ins[$i][1][0];
134            }
135            if($ins[$i][0] == 'plugin' && $ins[$i][1][0] == 'include_include' ) {
136                $mode  = $ins[$i][1][1][0];
137                $page  = $ins[$i][1][1][1];
138                $sect  = $ins[$i][1][1][2];
139                $flags = $ins[$i][1][1][3];
140
141                $page = $this->_apply_macro($page);
142                resolve_pageid(getNS($id), $page, $exists); // resolve shortcuts
143                $flags   = $this->get_flags($flags);
144                $ins_inc = $this->_get_instructions($page, $sect, $mode, $lvl, $flags);
145
146                if(!empty($ins_inc)) {
147                    // combine instructions and reset counter
148                    $ins_start = array_slice($ins, 0, $i+1);
149                    $ins_end   = array_slice($ins, $i+1);
150                    $ins = array_merge($ins_start, $ins_inc, $ins_end);
151                    $num = count($ins);
152                }
153            }
154        }
155    }
156
157    /**
158     * Returns the converted instructions of a give page/section
159     *
160     * @author Michael Klier <chi@chimeric.de>
161     */
162    function _get_instructions($page, $sect, $mode, $lvl, $flags) {
163        global $ID;
164
165        if($ID == $page || !page_exists($page) || (page_exists($page) && auth_quickaclcheck($page) < AUTH_READ)) return array();
166        $key = (!$sect) ? $page . '#' . $sect : $page;
167
168        // prevent recursion
169        if(!$this->includes[$key]) {
170            $ins = p_cached_instructions(wikiFN($page));
171            $this->includes[$key] = true;
172            $this->_convert_instructions($ins, $lvl, $page, $sect, $flags);
173            return $ins;
174        }
175    }
176
177    /**
178     * Converts instructions of the included page
179     *
180     * @author Michael Klier <chi@chimeric.de>
181     */
182    function _convert_instructions(&$ins, $lvl, $page, $sect, $flags) {
183
184        if(!empty($sect)) {
185            $this->_get_section($ins, $sect);   // section required
186        } elseif($flags['firstsec']) {
187            $this->_get_firstsec($ins, $page);  // only first section
188        }
189
190        $has_permalink = false;
191        $footer_lvl    = false;
192        $ns  = getNS($page);
193        $num = count($ins);
194        $top_lvl = $lvl; // save toplevel for later use
195        for($i=0; $i<$num; $i++) {
196            switch($ins[$i][0]) {
197                case 'document_start':
198                case 'document_end':
199                case 'section_edit':
200                    unset($ins[$i]);
201                    break;
202                case 'header':
203                    $ins[$i][1][1] = $this->_get_level($lvl, $ins[$i][1][1]);
204                    $lvl = $ins[$i][1][1];
205                    if(!$footer_lvl) $footer_lvl = $lvl;
206                    if($sect && !$sect_title) {
207                        $sect_title = $ins[$i][1][0];
208                    }
209                    if($flags['link'] && !$has_permalink) {
210                        $this->_permalink($ins[$i], $page, $sect);
211                        $has_permalink = true;
212                    }
213                    break;
214                case 'section_open':
215                    $ins[$i][1][0] = $this->_get_level($lvl, $ins[$i][1][0]);
216                    $lvl = $ins[$i][1][0];
217                    break;
218                case 'internallink':
219                case 'internalmedia':
220                    if($ins[$i][1][0]{0} == '.') {
221                        if($ins[$i][1][0]{1} == '.') {
222                            $ins[$i][1][0] = getNS($ns) . ':' . substr($ins[$i][1][0], 2); // parent namespace
223                        } else {
224                            $ins[$i][1][0] = $ns . ':' . substr($ins[$i][1][0], 1); // current namespace
225                        }
226                    } elseif (strpos($ins[$i][1][0], ':') === false) {
227                        $ins[$i][1][0] = $ns . ':' . $ins[$i][1][0]; // relative links
228                    }
229                    break;
230                case 'plugin':
231                    // FIXME skip others?
232                    if($ins[$i][1][0] == 'tag_tag') unset($ins[$i]);                // skip tags
233                    if($ins[$i][1][0] == 'discussion_comments') unset($ins[$i]);    // skip comments
234                    break;
235                default:
236                    break;
237            }
238        }
239
240        if($flags['footer']) $this->_footer($ins, $page, $sect, $sect_title, $flags, $footer_lvl);
241
242        // close previous section if any and re-open after inclusion
243        if($top_lvl != 0) {
244            array_unshift($ins, array('section_close'));
245            $ins[] = array('section_open', array($top_lvl));
246        }
247    }
248
249    /**
250     * Appends instruction item for the include plugin footer
251     *
252     * @author Michael Klier <chi@chimeric.de>
253     */
254    function _footer(&$ins, $page, $sect, $sect_title, $flags, $footer_lvl) {
255        $footer = array();
256        $footer[0] = 'plugin';
257        $footer[1] = array('include_footer', array($page, $sect, $sect_title, $flags, $this->toplevel_id, $footer_lvl));
258        $ins[] = $footer;
259    }
260
261    /**
262     * Return the correct level
263     *
264     * @author Michael Klier <chi@chimeric.de>
265     */
266    function _get_level($lvl, $curr_lvl) {
267        if($curr_lvl == $lvl) {
268            // current level equals inclusion level
269            // return current level increased by 1
270            return (($curr_lvl + 1) <= 5) ? ($curr_lvl + 1) : 5;
271
272        } elseif(($curr_lvl < $lvl) && (($lvl - $curr_lvl) <= 1)) {
273            // if current level is small than inclusion level and difference
274            // between inclusion level and current level is less than 1
275            // return current level increased by 1
276            return (($curr_lvl + 1) <= 5) ? ($curr_lvl + 1) : 5;
277
278        } elseif(($curr_lvl < $lvl) && (($lvl - $curr_lvl) > 1)) {
279            // if current level is less than inclusion level and
280            // difference between inclusion level and the curren level is
281            // greater than 1 return inclusion level increased by 1
282            return (($lvl + 1) <= 5) ? ($lvl + 1) : 5;
283        }
284    }
285
286    /**
287     * Get a section including its subsections
288     *
289     * @author Michael Klier <chi@chimeric.de>
290     */
291    function _get_section(&$ins, $sect) {
292        $num = count($ins);
293        $offset = false;
294        $lvl    = false;
295
296        for($i=0; $i<$num; $i++) {
297            if ($ins[$i][0] == 'header') {
298
299                // found the right header
300                if (cleanID($ins[$i][1][0]) == $sect) {
301                    $offset = $i;
302                    $lvl    = $ins[$i][1][1];
303                } elseif ($offset && $lvl && ($ins[$i][1][1] <= $lvl)) {
304                    $ins = array_slice($ins, $offset, ($i - $offset));
305                }
306            }
307        }
308    }
309
310    /**
311     * Only display the first section of a page and a readmore link
312     *
313     * @author Michael Klier <chi@chimeric.de>
314     */
315    function _get_firstsec(&$ins, $page) {
316        $num = count($ins);
317        for($i=0; $i<$num; $i++) {
318            if($ins[$i][0] == 'section_close') {
319                $ins = array_slice($ins, 0, $i);
320                $ins[] = array('p_open', array());
321                $ins[] = array('internallink',array($page, $this->getLang('readmore')));
322                $ins[] = array('p_close', array());
323                $ins[] = array('section_close');
324                return;
325            }
326        }
327    }
328
329    /**
330     * Makes user or date dependent includes possible
331     */
332    function _apply_macro($id) {
333        global $INFO;
334        global $auth;
335
336        // if we don't have an auth object, do nothing
337        if (!$auth) return $id;
338
339        $user     = $_SERVER['REMOTE_USER'];
340        $userdata = $auth->getUserData($user);
341        $group    = $userdata['grps'][0];
342
343        $replace = array(
344                '@USER@'  => cleanID($user),
345                '@NAME@'  => cleanID($INFO['userinfo']['name']),
346                '@GROUP@' => cleanID($group),
347                '@YEAR@'  => date('Y'),
348                '@MONTH@' => date('m'),
349                '@DAY@'   => date('d'),
350                );
351        return str_replace(array_keys($replace), array_values($replace), $id);
352    }
353
354    /**
355     * Create instruction item for a permalink header
356     *
357     * @param   string  $text: Headline text
358     * @param   integer $level: Headline level
359     * @param   integer $pos: I wish I knew what this is for... (me too ;-))
360     *
361     * @author Gina Haeussge <osd@foosel.net>
362     * @author Michael Klier <chi@chimeric.de>
363     */
364    function _permalink(&$ins, $page, $sect) {
365        $ins[0] = 'plugin';
366        $ins[1] = array('include_header', array($ins[1][0], $ins[1][1], $page, $sect));
367    }
368
369    /**
370     * Optionally display logo for the first tag found in the included page
371     *
372     * FIXME erm what was this for again?
373     */
374    function _showTagLogos() {
375        if ((!$this->getConf('showtaglogos'))
376                || (plugin_isdisabled('tag'))
377                || (!$taghelper =& plugin_load('helper', 'tag')))
378            return '';
379
380        $subject = p_get_metadata($this->page['id'], 'subject');
381        if (is_array($subject)) $tag = $subject[0];
382        else list($tag, $rest) = explode(' ', $subject, 2);
383        $title = str_replace('_', ' ', noNS($tag));
384        resolve_pageid($taghelper->namespace, $tag, $exists); // resolve shortcuts
385
386        $logosrc = mediaFN($logoID);
387        $types = array('.png', '.jpg', '.gif'); // auto-detect filetype
388        foreach ($types as $type) {
389            if (!@file_exists($logosrc.$type)) continue;
390            $logoID   = $tag.$type;
391            $logosrc .= $type;
392            list($w, $h, $t, $a) = getimagesize($logosrc);
393            return ' style="min-height: '.$h.'px">'.
394                '<img class="mediaright" src="'.ml($logoID).'" alt="'.$title.'"/';
395        }
396        return '';
397    }
398}
399//vim:ts=4:sw=4:et:enc=utf-8:
400