xref: /plugin/combo/syntax/minimap.php (revision 32b85071e019dd3646a67c17fac4051338e495eb)
10356c661Sgerardnico<?php
20356c661Sgerardnico/**
30356c661Sgerardnico * Plugin minimap : Displays mini-map for namespace
40356c661Sgerardnico *
50356c661Sgerardnico * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
60356c661Sgerardnico * @author  Nicolas GERARD
70356c661Sgerardnico */
80356c661Sgerardnico
95f891b7eSNickeauuse ComboStrap\SnippetManager;
100356c661Sgerardnicouse ComboStrap\LinkUtility;
110356c661Sgerardnicouse ComboStrap\PluginUtility;
120356c661Sgerardnico
130356c661Sgerardnicoif (!defined('DOKU_INC')) die();
140356c661Sgerardnico
150356c661Sgerardnico
160356c661Sgerardnicoclass syntax_plugin_combo_minimap extends DokuWiki_Syntax_Plugin
170356c661Sgerardnico{
180356c661Sgerardnico
190356c661Sgerardnico    const MINIMAP_TAG_NAME = 'minimap';
200356c661Sgerardnico    const INCLUDE_DIRECTORY_PARAMETERS = 'includedirectory';
210356c661Sgerardnico    const SHOW_HEADER = 'showheader';
220356c661Sgerardnico    const NAMESPACE_KEY_ATT = 'namespace';
230356c661Sgerardnico
240356c661Sgerardnico
250356c661Sgerardnico
260356c661Sgerardnico    function connectTo($aMode)
270356c661Sgerardnico    {
280356c661Sgerardnico        $pattern = '<' . self::MINIMAP_TAG_NAME . '[^>]*>';
290356c661Sgerardnico        $this->Lexer->addSpecialPattern($pattern, $aMode, PluginUtility::getModeForComponent($this->getPluginComponent()));
300356c661Sgerardnico    }
310356c661Sgerardnico
320356c661Sgerardnico    function getSort()
330356c661Sgerardnico    {
340356c661Sgerardnico        /**
350356c661Sgerardnico         * One less than the old one
360356c661Sgerardnico         */
370356c661Sgerardnico        return 149;
380356c661Sgerardnico    }
390356c661Sgerardnico
400356c661Sgerardnico    /**
410356c661Sgerardnico     * No p element please
420356c661Sgerardnico     * @return string
430356c661Sgerardnico     */
440356c661Sgerardnico    function getPType()
450356c661Sgerardnico    {
460356c661Sgerardnico        return 'block';
470356c661Sgerardnico    }
480356c661Sgerardnico
490356c661Sgerardnico    function getType()
500356c661Sgerardnico    {
510356c661Sgerardnico        // The spelling is wrong but this is a correct value
520356c661Sgerardnico        // https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
530356c661Sgerardnico        return 'substition';
540356c661Sgerardnico    }
550356c661Sgerardnico
560356c661Sgerardnico    /**
570356c661Sgerardnico     *
580356c661Sgerardnico     * The handle function goal is to parse the matched syntax through the pattern function
590356c661Sgerardnico     * and to return the result for use in the renderer
600356c661Sgerardnico     * This result is always cached until the page is modified.
610356c661Sgerardnico     * @param string $match
620356c661Sgerardnico     * @param int $state
630356c661Sgerardnico     * @param int $pos
640356c661Sgerardnico     * @param Doku_Handler $handler
650356c661Sgerardnico     * @return array|bool
660356c661Sgerardnico     * @see DokuWiki_Syntax_Plugin::handle()
670356c661Sgerardnico     *
680356c661Sgerardnico     */
690356c661Sgerardnico    function handle($match, $state, $pos, Doku_Handler $handler)
700356c661Sgerardnico    {
710356c661Sgerardnico
720356c661Sgerardnico        switch ($state) {
730356c661Sgerardnico
740356c661Sgerardnico            // As there is only one call to connect to in order to a add a pattern,
750356c661Sgerardnico            // there is only one state entering the function
760356c661Sgerardnico            // but I leave it for better understanding of the process flow
770356c661Sgerardnico            case DOKU_LEXER_SPECIAL :
780356c661Sgerardnico
790356c661Sgerardnico                // Parse the parameters
800356c661Sgerardnico                $match = substr($match, 8, -1); //9 = strlen("<minimap")
810356c661Sgerardnico
820356c661Sgerardnico                // Init
830356c661Sgerardnico                $parameters = array();
840356c661Sgerardnico                $parameters['substr'] = 1;
850356c661Sgerardnico                $parameters[self::INCLUDE_DIRECTORY_PARAMETERS] = $this->getConf(self::INCLUDE_DIRECTORY_PARAMETERS);
860356c661Sgerardnico                $parameters[self::SHOW_HEADER] = $this->getConf(self::SHOW_HEADER);
870356c661Sgerardnico
880356c661Sgerardnico
890356c661Sgerardnico                // /i not case sensitive
900356c661Sgerardnico                $attributePattern = "\\s*(\w+)\\s*=\\s*[\'\"]{1}([^\`\"]*)[\'\"]{1}\\s*";
910356c661Sgerardnico                $result = preg_match_all('/' . $attributePattern . '/i', $match, $matches);
920356c661Sgerardnico                if ($result != 0) {
930356c661Sgerardnico                    foreach ($matches[1] as $key => $parameterKey) {
940356c661Sgerardnico                        $parameter = strtolower($parameterKey);
950356c661Sgerardnico                        $value = $matches[2][$key];
960356c661Sgerardnico                        if (in_array($parameter, [self::SHOW_HEADER, self::INCLUDE_DIRECTORY_PARAMETERS])) {
970356c661Sgerardnico                            $value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
980356c661Sgerardnico                        }
990356c661Sgerardnico                        $parameters[$parameter] = $value;
1000356c661Sgerardnico                    }
1010356c661Sgerardnico                }
1020356c661Sgerardnico                // Cache the values
103*32b85071SNickeau                return array(
104*32b85071SNickeau                    PluginUtility::STATE => $state,
105*32b85071SNickeau                    PluginUtility::ATTRIBUTES=> $parameters
106*32b85071SNickeau                );
1070356c661Sgerardnico
1080356c661Sgerardnico        }
1090356c661Sgerardnico
1100356c661Sgerardnico        return false;
1110356c661Sgerardnico    }
1120356c661Sgerardnico
1130356c661Sgerardnico
1140356c661Sgerardnico    function render($mode, Doku_Renderer $renderer, $data)
1150356c661Sgerardnico    {
1160356c661Sgerardnico
1170356c661Sgerardnico        // The $data variable comes from the handle() function
1180356c661Sgerardnico        //
1190356c661Sgerardnico        // $mode = 'xhtml' means that we output html
1200356c661Sgerardnico        // There is other mode such as metadata where you can output data for the headers (Not 100% sure)
1210356c661Sgerardnico        if ($mode == 'xhtml') {
1220356c661Sgerardnico
1230356c661Sgerardnico            /** @var Doku_Renderer_xhtml $renderer */
1240356c661Sgerardnico
125*32b85071SNickeau
126*32b85071SNickeau            $state=$data[PluginUtility::STATE];
1270356c661Sgerardnico
1280356c661Sgerardnico            // As there is only one call to connect to in order to a add a pattern,
1290356c661Sgerardnico            // there is only one state entering the function
1300356c661Sgerardnico            // but I leave it for better understanding of the process flow
1310356c661Sgerardnico            switch ($state) {
1320356c661Sgerardnico
1330356c661Sgerardnico                case DOKU_LEXER_SPECIAL :
1340356c661Sgerardnico
1355f891b7eSNickeau
1368aa9d0e6Sgerardnico                    PluginUtility::getSnippetManager()->upsertCssSnippetForBar(self::MINIMAP_TAG_NAME);
1375f891b7eSNickeau
1380356c661Sgerardnico
1390356c661Sgerardnico                    global $ID;
1400356c661Sgerardnico                    global $INFO;
1410356c661Sgerardnico                    $callingId = $ID;
1420356c661Sgerardnico                    // If mini-map is in a sidebar, we don't want the ID of the sidebar
1430356c661Sgerardnico                    // but the ID of the page.
1440356c661Sgerardnico                    if ($INFO != null) {
1450356c661Sgerardnico                        $callingId = $INFO['id'];
1460356c661Sgerardnico                    }
1470356c661Sgerardnico
148*32b85071SNickeau                    $attributes = $data[PluginUtility::ATTRIBUTES];
1490356c661Sgerardnico                    $nameSpacePath = getNS($callingId); // The complete path to the directory
150*32b85071SNickeau                    if (array_key_exists(self::NAMESPACE_KEY_ATT, $attributes)) {
151*32b85071SNickeau                        $nameSpacePath = $attributes[self::NAMESPACE_KEY_ATT];
1520356c661Sgerardnico                    }
1530356c661Sgerardnico                    $currentNameSpace = curNS($callingId); // The name of the container directory
154*32b85071SNickeau                    $includeDirectory = $attributes[self::INCLUDE_DIRECTORY_PARAMETERS];
1550356c661Sgerardnico                    $pagesOfNamespace = $this->getNamespaceChildren($nameSpacePath, $sort = 'natural', $listdirs = $includeDirectory);
1560356c661Sgerardnico
1570356c661Sgerardnico                    // Set the two possible home page for the namespace ie:
1580356c661Sgerardnico                    //   - the name of the containing map ($homePageWithContainingMapName)
1590356c661Sgerardnico                    //   - the start conf parameters ($homePageWithStartConf)
1600356c661Sgerardnico                    global $conf;
1610356c661Sgerardnico                    $parts = explode(':', $nameSpacePath);
1620356c661Sgerardnico                    $lastContainingNameSpace = $parts[count($parts) - 1];
1630356c661Sgerardnico                    $homePageWithContainingMapName = $nameSpacePath . ':' . $lastContainingNameSpace;
1640356c661Sgerardnico                    $startConf = $conf['start'];
1650356c661Sgerardnico                    $homePageWithStartConf = $nameSpacePath . ':' . $startConf;
1660356c661Sgerardnico
1670356c661Sgerardnico                    // Build the list of page
1680356c661Sgerardnico                    $miniMapList = '<ul class="list-group">';
1690356c661Sgerardnico                    $pageNum = 0;
1700356c661Sgerardnico                    $startPageFound = false;
1710356c661Sgerardnico                    $homePageFound = false;
1720356c661Sgerardnico                    //$pagesCount = count($pagesOfNamespace); // number of pages in the namespace
1730356c661Sgerardnico                    foreach ($pagesOfNamespace as $pageArray) {
1740356c661Sgerardnico
1750356c661Sgerardnico                        // The title of the page
1760356c661Sgerardnico                        $title = '';
1770356c661Sgerardnico
1780356c661Sgerardnico                        // If it's a directory
1790356c661Sgerardnico                        if ($pageArray['type'] == "d") {
1800356c661Sgerardnico
1810356c661Sgerardnico                            $pageId = $this->getNamespaceStartId($pageArray['id']);
1820356c661Sgerardnico
1830356c661Sgerardnico                        } else {
1840356c661Sgerardnico
1850356c661Sgerardnico                            $pageNum++;
1860356c661Sgerardnico                            $pageId = $pageArray['id'];
1870356c661Sgerardnico
1880356c661Sgerardnico                        }
1890356c661Sgerardnico                        $link = new LinkUtility($pageId);
1900356c661Sgerardnico
1910356c661Sgerardnico
1920356c661Sgerardnico                        /**
1935f891b7eSNickeau                         * Set special name and title
1940356c661Sgerardnico                         */
1950356c661Sgerardnico                        // If debug mode
196*32b85071SNickeau                        if ($attributes['debug']) {
1970356c661Sgerardnico                            $link->setTitle($link->getTitle().' (' . $pageId . ')');
1980356c661Sgerardnico                        }
1990356c661Sgerardnico
2000356c661Sgerardnico                        // Add the page number in the URL title
2010356c661Sgerardnico                        $link->setTitle($link->getTitle() .' (' . $pageNum . ')');
2020356c661Sgerardnico
2030356c661Sgerardnico                        // Suppress the parts in the name with the regexp defines in the 'suppress' params
204*32b85071SNickeau                        if ($attributes['suppress']) {
205*32b85071SNickeau                            $substrPattern = '/' . $attributes['suppress'] . '/i';
2060356c661Sgerardnico                            $replacement = '';
2070356c661Sgerardnico                            $name = preg_replace($substrPattern, $replacement, $link->getName());
2080356c661Sgerardnico                            $link->setName($name);
2090356c661Sgerardnico                        }
2100356c661Sgerardnico
2110356c661Sgerardnico                        // See in which page we are
2120356c661Sgerardnico                        // The style will then change
2130356c661Sgerardnico                        $active = '';
2140356c661Sgerardnico                        if ($callingId == $pageId) {
2150356c661Sgerardnico                            $active = 'active';
2160356c661Sgerardnico                        }
2170356c661Sgerardnico
2180356c661Sgerardnico                        // Not all page are printed
2190356c661Sgerardnico                        // sidebar are not for instance
2200356c661Sgerardnico
2210356c661Sgerardnico                        // Are we in the root ?
2220356c661Sgerardnico                        if ($pageArray['ns']) {
2230356c661Sgerardnico                            $nameSpacePathPrefix = $pageArray['ns'] . ':';
2240356c661Sgerardnico                        } else {
2250356c661Sgerardnico                            $nameSpacePathPrefix = '';
2260356c661Sgerardnico                        }
2270356c661Sgerardnico                        $print = true;
2280356c661Sgerardnico                        if ($pageArray['id'] == $nameSpacePathPrefix . $currentNameSpace) {
2290356c661Sgerardnico                            // If the start page exists, the page with the same name
2300356c661Sgerardnico                            // than the namespace must be shown
2310356c661Sgerardnico                            if (page_exists($nameSpacePathPrefix . $startConf)) {
2320356c661Sgerardnico                                $print = true;
2330356c661Sgerardnico                            } else {
2340356c661Sgerardnico                                $print = false;
2350356c661Sgerardnico                            }
2360356c661Sgerardnico                            $homePageFound = true;
2370356c661Sgerardnico                        } else if ($pageArray['id'] == $nameSpacePathPrefix . $startConf) {
2380356c661Sgerardnico                            $print = false;
2390356c661Sgerardnico                            $startPageFound = true;
2400356c661Sgerardnico                        } else if ($pageArray['id'] == $nameSpacePathPrefix . $conf['sidebar']) {
2410356c661Sgerardnico                            $pageNum -= 1;
2420356c661Sgerardnico                            $print = false;
2430356c661Sgerardnico                        };
2440356c661Sgerardnico
2450356c661Sgerardnico
2460356c661Sgerardnico                        // If the page must be printed, build the link
2470356c661Sgerardnico                        if ($print) {
2480356c661Sgerardnico
2490356c661Sgerardnico                            // Open the item tag
2500356c661Sgerardnico                            $miniMapList .= "<li class=\"list-group-item " . $active . "\">";
2510356c661Sgerardnico
2520356c661Sgerardnico                            // Add a glyphicon if it's a directory
2530356c661Sgerardnico                            if ($pageArray['type'] == "d") {
2545f891b7eSNickeau                                $miniMapList .= "<span class=\"nicon_folder_open\" aria-hidden=\"true\"></span> ";
2550356c661Sgerardnico                            }
2560356c661Sgerardnico
2575f891b7eSNickeau                            $miniMapList .= $link->renderOpenTag($renderer);
2585f891b7eSNickeau                            $miniMapList .= $link->getName();
2595f891b7eSNickeau                            $miniMapList .= $link->renderClosingTag();
2600356c661Sgerardnico
2610356c661Sgerardnico
2620356c661Sgerardnico                            // Close the item
2630356c661Sgerardnico                            $miniMapList .= "</li>";
2640356c661Sgerardnico
2650356c661Sgerardnico                        }
2660356c661Sgerardnico
2670356c661Sgerardnico                    }
2680356c661Sgerardnico                    $miniMapList .= '</ul>'; // End list-group
2690356c661Sgerardnico
2700356c661Sgerardnico
2710356c661Sgerardnico                    // Build the panel header
2720356c661Sgerardnico                    $miniMapHeader = "";
2730356c661Sgerardnico                    $startId = "";
2740356c661Sgerardnico                    if ($startPageFound) {
2750356c661Sgerardnico                        $startId = $homePageWithStartConf;
2760356c661Sgerardnico                    } else {
2770356c661Sgerardnico                        if ($homePageFound) {
2780356c661Sgerardnico                            $startId = $homePageWithContainingMapName;
2790356c661Sgerardnico                        }
2800356c661Sgerardnico                    }
2810356c661Sgerardnico
2820356c661Sgerardnico                    $panelHeaderContent = "";
2830356c661Sgerardnico                    if ($startId == "") {
284*32b85071SNickeau                        if ($attributes[self::SHOW_HEADER] == true) {
2850356c661Sgerardnico                            $panelHeaderContent = 'No Home Page found';
2860356c661Sgerardnico                        }
2870356c661Sgerardnico                    } else {
2880356c661Sgerardnico                        $startLink = new LinkUtility($startId);
2895f891b7eSNickeau                        $panelHeaderContent = $startLink->renderOpenTag($renderer);
2905f891b7eSNickeau                        $panelHeaderContent .= $startLink->getName();
2915f891b7eSNickeau                        $panelHeaderContent .= $startLink->renderClosingTag();
2920356c661Sgerardnico                        // We are not counting the header page
2930356c661Sgerardnico                        $pageNum--;
2940356c661Sgerardnico                    }
2950356c661Sgerardnico
2960356c661Sgerardnico                    if ($panelHeaderContent != "") {
2970356c661Sgerardnico                        $miniMapHeader .= '<div class="panel-heading">' . $panelHeaderContent . '  <span class="label label-primary">' . $pageNum . ' pages</span></div>';
2980356c661Sgerardnico                    }
2990356c661Sgerardnico
300*32b85071SNickeau                    if ($attributes['debug']) {
3010356c661Sgerardnico                        $miniMapHeader .= '<div class="panel-body">' .
3020356c661Sgerardnico                            '<B>Debug Information:</B><BR>' .
3030356c661Sgerardnico                            'CallingId: (' . $callingId . ')<BR>' .
304*32b85071SNickeau                            'Suppress Option: (' . $attributes['suppress'] . ')<BR>' .
3050356c661Sgerardnico                            '</div>';
3060356c661Sgerardnico                    }
3070356c661Sgerardnico
3080356c661Sgerardnico                    // Header + list
3090356c661Sgerardnico                    $renderer->doc .= '<div id="minimap__plugin"><div class="panel panel-default">'
3100356c661Sgerardnico                        . $miniMapHeader
3110356c661Sgerardnico                        . $miniMapList
3120356c661Sgerardnico                        . '</div></div>';
3130356c661Sgerardnico                    break;
3140356c661Sgerardnico            }
3150356c661Sgerardnico
3160356c661Sgerardnico            return true;
3170356c661Sgerardnico        }
3180356c661Sgerardnico        return false;
3190356c661Sgerardnico
3200356c661Sgerardnico    }
3210356c661Sgerardnico
3220356c661Sgerardnico    /**
3230356c661Sgerardnico     * Return all pages and/of sub-namespaces (subdirectory) of a namespace (ie directory)
3240356c661Sgerardnico     * Adapted from feed.php
3250356c661Sgerardnico     *
3260356c661Sgerardnico     * @param $namespace The container of the pages
3270356c661Sgerardnico     * @param string $sort 'natural' to use natural order sorting (default); 'date' to sort by filemtime
3280356c661Sgerardnico     * @param $listdirs - Add the directory to the list of files
3290356c661Sgerardnico     * @return array An array of the pages for the namespace
3300356c661Sgerardnico     */
3310356c661Sgerardnico    function getNamespaceChildren($namespace, $sort = 'natural', $listdirs = false)
3320356c661Sgerardnico    {
3330356c661Sgerardnico        require_once(DOKU_INC . 'inc/search.php');
3340356c661Sgerardnico        global $conf;
3350356c661Sgerardnico
3360356c661Sgerardnico        $ns = ':' . cleanID($namespace);
3370356c661Sgerardnico        // ns as a path
3380356c661Sgerardnico        $ns = utf8_encodeFN(str_replace(':', '/', $ns));
3390356c661Sgerardnico
3400356c661Sgerardnico        $data = array();
3410356c661Sgerardnico
3420356c661Sgerardnico        // Options of the callback function search_universal
3430356c661Sgerardnico        // in the search.php file
3440356c661Sgerardnico        $search_opts = array(
3450356c661Sgerardnico            'depth' => 1,
3460356c661Sgerardnico            'pagesonly' => true,
3470356c661Sgerardnico            'listfiles' => true,
3480356c661Sgerardnico            'listdirs' => $listdirs,
3490356c661Sgerardnico            'firsthead' => true
3500356c661Sgerardnico        );
3510356c661Sgerardnico        // search_universal is a function in inc/search.php that accepts the $search_opts parameters
3520356c661Sgerardnico        search($data, $conf['datadir'], 'search_universal', $search_opts, $ns, $lvl = 1, $sort);
3530356c661Sgerardnico
3540356c661Sgerardnico        return $data;
3550356c661Sgerardnico    }
3560356c661Sgerardnico
3570356c661Sgerardnico    /**
3580356c661Sgerardnico     * Return the id of the start page of a namespace
3590356c661Sgerardnico     *
3600356c661Sgerardnico     * @param $id an id of a namespace (directory)
3610356c661Sgerardnico     * @return string the id of the home page
3620356c661Sgerardnico     */
3630356c661Sgerardnico    function getNamespaceStartId($id)
3640356c661Sgerardnico    {
3650356c661Sgerardnico
3660356c661Sgerardnico        global $conf;
3670356c661Sgerardnico
3680356c661Sgerardnico        $id = $id . ":";
3690356c661Sgerardnico
3700356c661Sgerardnico        if (page_exists($id . $conf['start'])) {
3710356c661Sgerardnico            // start page inside namespace
3720356c661Sgerardnico            $homePageId = $id . $conf['start'];
3730356c661Sgerardnico        } elseif (page_exists($id . noNS(cleanID($id)))) {
3740356c661Sgerardnico            // page named like the NS inside the NS
3750356c661Sgerardnico            $homePageId = $id . noNS(cleanID($id));
3760356c661Sgerardnico        } elseif (page_exists($id)) {
3770356c661Sgerardnico            // page like namespace exists
3780356c661Sgerardnico            $homePageId = substr($id, 0, -1);
3790356c661Sgerardnico        } else {
3800356c661Sgerardnico            // fall back to default
3810356c661Sgerardnico            $homePageId = $id . $conf['start'];
3820356c661Sgerardnico        }
3830356c661Sgerardnico        return $homePageId;
3840356c661Sgerardnico    }
3850356c661Sgerardnico
3860356c661Sgerardnico    /**
3870356c661Sgerardnico     * @param $get_called_class
3880356c661Sgerardnico     * @return string
3890356c661Sgerardnico     */
3900356c661Sgerardnico    public static function getTagName($get_called_class)
3910356c661Sgerardnico    {
3920356c661Sgerardnico        list(/* $t */, /* $p */, $c) = explode('_', $get_called_class, 3);
3930356c661Sgerardnico        return (isset($c) ? $c : '');
3940356c661Sgerardnico    }
3950356c661Sgerardnico
3960356c661Sgerardnico    /**
3970356c661Sgerardnico     * @return string - the tag
3980356c661Sgerardnico     */
3990356c661Sgerardnico    public static function getTag()
4000356c661Sgerardnico    {
4010356c661Sgerardnico        return self::getTagName(get_called_class());
4020356c661Sgerardnico    }
4030356c661Sgerardnico
4040356c661Sgerardnico
4050356c661Sgerardnico}
406