xref: /plugin/siteexport/inc/toc.php (revision 34b0ea04de60b735856aca2edf9255b07c640713)
1<?php
2
3if(!defined('DOKU_PLUGIN')) die('meh');
4
5class siteexport_toc
6{
7    private $emptyNSToc = true;
8    private $functions = null;
9    public $translation = null;
10
11    public function siteexport_toc($functions)
12    {
13        $this->emptyNSToc = !empty($_REQUEST['emptyTocElem']);
14        $this->functions = $functions;
15    }
16
17    private function shortenByTranslation(&$inputURL, $deepSearch = false)
18    {
19        if ( $this->translation )
20        {
21            $inputURL = str_replace(':', '/', $inputURL);
22            $url = explode('/', $inputURL);
23
24            for( $i=0; $i<count($url); $i++ )
25            {
26                if ( in_array($url[$i], $this->translation->trans ) )
27                {
28                    // Rauswerfen und weg
29                    $url[$i] = '';
30                    break;
31                }
32
33                if ( !$deepSearch )
34                {
35                    break;
36                }
37
38                // Ok, remove anyway
39                $url[$i] = '';
40            }
41
42            $inputURL = implode('/', $url);
43            $inputURL = preg_replace("$\/+$", "/", $inputURL);
44        }
45
46        if ( strlen($inputURL) > 0 && substr($inputURL, 0, 1) == '/' )
47        {
48            $inputURL = substr($inputURL, 1);
49        }
50
51        return $inputURL;
52    }
53
54    /**
55     * Build the Java Documentation TOC XML
56     **/
57    public function __getJavaHelpTOCXML($DATA) {
58
59        if ( count ( $DATA) == 0 ) {
60            return false;
61        }
62
63        $TOCXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<toc>";
64        $MAPXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<map version=\"1.0\">";
65        // usort($DATA, array($this, 'sortFunction'));
66
67        // Go through the pages
68        $CHECKDATA = array();
69        $nData = $DATA;
70        $DATA = array();
71        $check = array();
72        $startPageID = null;
73
74        foreach ( $nData as $elem )
75        {
76			// Check if available
77            $anchor = ( !empty($elem['anchor']) ? '#' . $elem['anchor'] : '' );
78            $elem['url'] = $this->functions->getSiteName($elem['id'], true); // Override - we need a clean name
79            $elem['mapURL'] = $elem['url'];
80            $this->shortenByTranslation($elem['url']);
81
82            // only add an url once
83            if ( in_array($elem['url'], $CHECKDATA) ) { continue; }
84
85            if ( !isset($elem['exists']) ) {
86                resolve_pageid(getNS($elem['id']),$elem['id'],$elem['exists']);
87                $this->functions->debug->message("EXISTS previously not set.", $elem, 1);
88            }
89
90			// if not there, no map ids will be generated
91            $elem['mapID'] = intval($elem['exists']) == 1 ? $this->getMapID($elem, $check) : array();
92
93            if ( empty($elem['depth']) ) $elem['depth'] = count(explode('/', $elem['url']));
94            $CHECKDATA[] = $elem['url'];
95
96            if ( $startPageID == null )
97            {
98                $startPageID = $elem['mapID'][0];
99            }
100
101            if ( empty( $elem['name'] ) || $elem['name'] == NoNs($elem['id']) ) {
102	            $elem['name'] = $this->functions->getSiteTitle($elem['id']);
103            }
104
105            // Go on building mapXML
106            $this->shortenByTranslation($elem['mapURL'], true); // true to already remove all language stuff - false if not
107            foreach ( $elem['mapID'] as $VIEWID ) {
108                $MAPXML .= "\n\t<mapID target=\"$VIEWID\" url=\"" . $elem['mapURL'] . $anchor . "\"/>";
109            }
110
111            $elem['tocNS'] = getNS(cleanID($elem['url']));
112            $elem['tocNS'] = explode('/', $this->shortenByTranslation($elem['tocNS'], true));
113            $this->functions->debug->message("This will be the TOC elements data:", $elem, 1);
114
115            $this->__buildTOCTree($DATA, $elem['tocNS'], $elem);
116        }
117
118        $TOCXML .= $this->__writeTOCTree($DATA) . "\n</toc>";
119        $MAPXML .= "\n</map>";
120/*
121        // http://documentation:81/documentation/clear-reports/remote-interface-help/configuration/configuration/index?JavaHelpDocZip=1&depthType=1&diInv=1&do=siteexport&ens=documentation%3Aclear-reports%3Aremote-interface-help%3Aconfiguration&renderer=&template=clearreports-setup&useTocFile=1
122        print "<html><pre>";
123        print_r($DATA);
124        $TOCXML = str_replace("<", "&lt;", str_replace(">", "&gt;", $TOCXML));
125        print "$TOCXML";
126
127        $MAPXML = str_replace("<", "&lt;", str_replace(">", "&gt;", $MAPXML));
128        print "$MAPXML";
129
130        print "</pre></html>";
131        exit;
132/*/
133        return array($TOCXML, $MAPXML, $startPageID);
134//*/
135    }
136
137    /**
138     * Prepare the TOC Tree
139     **/
140    private function __buildTOCTree(&$DATA, $currentNSArray, $elemToAdd)
141    {
142        global $conf;
143
144        if ( empty($currentNSArray) )
145        {
146            // In Depth, let go!
147            $DATA[noNS($elemToAdd['id'])] = $elemToAdd;
148            return;
149        } else if (count($currentNSArray) == 1 && $currentNSArray[0] == '' && noNS($elemToAdd['id']) == $conf['start'] )
150        {
151            // Wird gebraucht um die erste Ebene sauber zu bauen … kann aber irgendwelche Nebeneffekte haben
152            $DATA[noNS($elemToAdd['id'])] = $elemToAdd;
153            return;
154        }
155
156        $currentLevel = array_shift($currentNSArray);
157        if ( empty($DATA[$currentLevel]) ) {
158            $DATA[$currentLevel] = array();
159        }
160
161        $this->__buildTOCTree($DATA[$currentLevel], $currentNSArray, $elemToAdd);
162    }
163
164    /**
165     * Create a single TOC Item
166     **/
167    private function __TOCItem($item, $depth, $selfClosed=true)
168    {
169        $targetID = $item['mapID'][0];
170        if ( empty($targetID) ) {
171            $targetID = strtolower($item['name']);
172        }
173        return "\n" . str_repeat("\t", max($depth, 0)+1) . "<tocitem target=\"$targetID\"" . (intval($item['exists']) == 1 ? " text=\"" . $item['name'] . "\"" : "") . ($selfClosed?'/':'') . ">";
174    }
175
176    /**
177     * Create a single TOC Item
178     **/
179    private function __TOCItemClose($depth)
180    {
181        return "\n" . str_repeat("\t", max($depth, 0)+1) . "</tocitem>";
182    }
183
184    /**
185     * Write the whole TOC TREE
186     **/
187    private function __writeTOCTree($CURRENTNODE, $CURRENTNODENAME = null, $DEPTH=0) {
188        global $conf;
189
190        $XML = '';
191        $didOpenItem = false;
192        if ( !is_array($CURRENTNODE) || empty($CURRENTNODE) )
193        {
194            // errr … no.
195            return $XML;
196        }
197
198        // This is an element!
199        if ( !empty($CURRENTNODE['id']) )
200        {
201            // This has to be an item!
202            return $this->__TOCItem($CURRENTNODE, $DEPTH);
203        }
204
205        // Look for start page
206        if ( !empty($CURRENTNODE[$conf['start']]) )
207        {
208            // YAY! StartPage found.
209            $didOpenItem = !(count($CURRENTNODE) == 1);
210            $XML .= $this->__TOCItem($CURRENTNODE[$conf['start']], $DEPTH, !$didOpenItem );
211            unset($CURRENTNODE[$conf['start']]);
212        } else if ($CURRENTNODENAME != null) {
213            // We have a parent node for what is comming … lets honor that
214            $didOpenItem = !(count($CURRENTNODE) == 0);
215            $XML .= $this->__TOCItem(array('name' => ucwords($CURRENTNODENAME)), $DEPTH, !$didOpenItem );
216        } else {
217            // Woohoo … empty node? do not count up!
218            $DEPTH --;
219        }
220
221        // Circle through the entries
222        foreach ( $CURRENTNODE as $NODENAME => $ELEM )
223        {
224            // a node should have more than only one entry … otherwise we will not tell our name!
225            $XML .= $this->__writeTOCTree($ELEM, count($ELEM) >= 1 ? $NODENAME : null, $DEPTH+1);
226        }
227
228        // Close and return
229        return $XML . ($didOpenItem?$this->__TOCItemClose($DEPTH):'');
230    }
231
232
233    function post(&$value, $key, array $additional){
234        $inner_glue = $additional[0];
235        $prefix = isset($additional[1])? $additional[1] : false;
236        if($prefix === false) $prefix = $key;
237
238        $value = $value.$inner_glue.$prefix;
239    }
240
241    function mapIDWithAnchor(&$n, $key, $postfix)
242    {
243        if ( empty($postfix) ) return;
244        $n .= '-' . $postfix;
245    }
246
247    function getMapID($elem, &$check)
248    {
249        $meta = p_get_metadata($elem['id'], 'context', true);
250        $mapID = explode('|', empty( $meta['id'] ) ? sectionID($elem['name'], $check) : $meta['id']);
251
252        array_walk($mapID, array($this, 'mapIDWithAnchor'), $elem['anchor']);
253
254        return $mapID;
255    }
256
257    /**
258     * internal Sort function
259     * @param unknown_type $a
260     * @param unknown_type $b
261     */
262    private function sortFunction($a, $b)
263    {
264        $idA = $a['id'];
265        $idB = $b['id'];
266
267        $depthA = explode(':', getNS($idA));
268        $depthB = explode(':', getNS($idB));
269
270        for ( $i=0; $i < min(count($depthA), count($depthB)); $i++ )
271        {
272            $NSCMP = strcmp($depthA[$i], $depthB[$i]);
273            if ( $NSCMP != 0 )
274            {
275                // Something is different!
276                return $NSCMP;
277            }
278        }
279
280        // There is mor in B, than in A!
281        if ( count($depthA) < count($depthB) )
282        {
283            return -1;
284        } else if ( count($depthA) > count($depthB) )
285        {
286            // there is more in A than in B
287            return 1;
288        }
289
290        if ( $NSCMP == 0 )
291        {
292            // Something is different!
293            return strcmp(noNS($idA), noNS($idB));
294        }
295
296        return 0;
297    }
298
299    /**
300     * Build the Eclipse Documentation TOC XML
301     **/
302    public function __getTOCXML($DATA, $XML="<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.toc\"?>\n") {
303
304        $pagesArray = array();
305
306        // Go through the pages
307        foreach ( $DATA as $elem ) {
308
309            $site = $elem['id'];
310            $elems = explode('/', $this->functions->getSiteName($site));
311
312            // Strip Site
313            array_pop( $elems );
314
315            // build the topic Tree
316            $this->__buildTopicTree($pagesArray, $elems, $site);
317        }
318
319        $XML .= $this->__addXMLTopic($pagesArray, 'toc');
320
321        return $XML;
322
323    }
324
325    /**
326     * Load the topic Tree for the TOC - recursive
327     **/
328    private function __buildTopicTree( &$PAGES, $DATA, $SITE, $INSERTDATA = null ) {
329
330        if ( empty( $DATA ) || !is_array($DATA) ) {
331
332            if ( $INSERTDATA == null )
333            {
334                $INSERTDATA = $SITE;
335            }
336
337            // This is already a namespace
338            if ( is_array($PAGES[noNS($SITE)]) ) {
339                // The root already exists!
340                if ( !empty($PAGES[noNS($SITE)][noNS($SITE)]) ) {
341                    if ( strstr($PAGES[noNS($SITE)][noNS($SITE)], $SITE) ) {
342                        // The SITE is in the parent Namespace, and the current Namespace has an index with same name
343                        $PAGES['__' . noNS($SITE)] = $INSERTDATA;
344                    } else {
345                        $PAGES['__' . noNS($SITE)] = $PAGES[noNS($SITE)][noNS($SITE)];
346                        $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA;
347                    }
348                } else {
349                    $PAGES[noNS($SITE)][noNS($SITE)] = $INSERTDATA;
350                }
351            } else {
352                // just a Page
353                $PAGES[noNS($SITE)] = $INSERTDATA;
354            }
355            return;
356        }
357
358        $NS = array_shift($DATA);
359        if ( !is_array( $PAGES[$NS] ) ) $PAGES[$NS] = empty($PAGES[$NS]) ? array() : array($PAGES[$NS]);
360        $this->__buildTopicTree( $PAGES[$NS], $DATA, $SITE, $INSERTDATA );
361
362        return;
363    }
364
365    /**
366     * Build the Topic Tree for TOC.xml
367     **/
368    private function __addXMLTopic($DATA, $ITEM='topic', $LEVEL=0, $NODENAME='') {
369        global $conf;
370
371        $DEPTH = str_repeat("\t", $LEVEL);
372
373        if ( !is_array($DATA) ) {
374            return $DEPTH . '<' . $ITEM . ' label="' . $this->functions->getSiteTitle($DATA) . '" ' . ($ITEM != 'topic' ? 'topic' : 'href' ) . '="' . $this->functions->getSiteName($DATA) . "\" />\n";
375        }
376        // Is array from this point on
377        list($indexTitle, $indexFile) = $this->__getIndexItem($DATA, $NODENAME);
378
379        if ( empty( $indexTitle) ) $indexTitle = $this->functions->getSiteTitle( $conf['start'] );
380        if ( !empty( $indexFile) ) $indexFile = ($ITEM != 'topic' ? 'topic' : 'href' ) . "=\"$indexFile\"";
381
382        $isEmptyNode = count($DATA) == 1 && empty($indexFile);
383
384        if ( !$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0) )
385        $XML = "$DEPTH<$ITEM label=\"$indexTitle\" $indexFile>";
386
387        if ( !$isEmptyNode && count ($DATA) > 0 ) $XML .= "\n";
388
389        foreach( $DATA as $NODENAME => $NS ) {
390
391            $XML .= $this->__addXMLTopic($NS, ( !($this->emptyNSToc || count($DATA) > 1) && $ITEM != 'topic' ? $ITEM : 'topic' ), $LEVEL+(!$isEmptyNode ? 1 : 0), $NODENAME);
392
393        }
394
395        if ( !$isEmptyNode && count ($DATA) > 0 ) $XML .= "$DEPTH";
396        if ( !$isEmptyNode && ($this->emptyNSToc || count($DATA) > 0) ) {
397            $XML .= "</$ITEM>\n";
398        }
399
400        return $XML;
401    }
402
403
404    /**
405     * Get the context XML
406     **/
407    public function __getContextXML($DATA) {
408
409        $XML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<?NLS TYPE=\"org.eclipse.help.context\"?>\n<contexts>\n";
410
411        $check = array();
412        foreach ( $DATA as $elem )
413        {
414            $ID = $elem['id'];
415            $meta = p_get_metadata($ID, 'context', true);
416            if ( empty( $meta['id'] ) ) { continue; }
417
418            $TITLE = empty( $meta['title'] ) ? $this->functions->getSiteTitle($ID) : $meta['title'];
419
420            // support more than one view IDs ... for more than one reference
421            $VIEWIDs = $this->getMapID($elem, $check);
422
423            $DESCRIPTION = $this->functions->xmlEntities(p_get_metadata($ID, 'description abstract'));
424
425            // Build topic Links
426            $url = $this->functions->getSiteName($ID);
427            $this->shortenByTranslation($url);
428
429            $TOPICS = array( $url => $TITLE . " (Details)" );
430            $REFS = p_get_metadata($ID, 'relation references', true);
431            if ( is_array($REFS) )
432            foreach ( $REFS as $REL => $EXISTS ) {
433                if ( !$EXISTS ) { continue; }
434                $TOPICS[$this->functions->getSiteName($REL)] = $this->functions->getSiteTitle($REL);
435            }
436
437            // build XML - include multi view IDs
438            foreach ( $VIEWIDs as $VIEWID ) {
439                $XML .= "\t<context id=\"$VIEWID\" title=\"$TITLE\">\n";
440                $XML .= "\t\t<description>$DESCRIPTION</description>\n";
441
442                foreach ( $TOPICS as $URL => $LABEL ) {
443                    $XML .= "\t\t<topic label=\"$LABEL\" href=\"$URL\" />\n";
444                }
445
446                $XML .= "\t</context>\n";
447            }
448        }
449
450        $XML .= "</contexts>";
451        return $XML;
452
453    }
454
455    /**
456     * Determine if this is an index - and if so, find its Title
457     **/
458    private function __getIndexItem(&$DATA, $NODENAME='') {
459        global $conf;
460
461        if ( !is_array($DATA) ) { return; }
462
463        $indexTitle = '';
464        $indexFile = '';
465        foreach ( $DATA as $NODE => $indexSearch) {
466            // Skip next Namespaces
467            if ( is_array($indexSearch) ) { continue; }
468
469            // Skip if this is not a start
470            if ( $NODE != $conf['start'] ) { continue; }
471
472            $indexTitle = $this->functions->getSiteTitle( $indexSearch );
473            $indexFile = $indexSearch;
474            unset($DATA[$NODE]);
475            break;
476        }
477
478        if ( empty($indexFile) && !empty($DATA[$NODENAME]) ) {
479            $indexTitle = $this->functions->getSiteTitle( $DATA[$NODENAME] );
480            $indexFile = $DATA[$NODENAME];
481            unset($DATA[$NODENAME]);
482        }
483
484        return array($indexTitle, $this->functions->getSiteName($indexFile));
485    }
486}
487
488?>