1<?php
2
3/**
4 * DokuWiki Plugin autonumbering (Action Component)
5 *
6 * Adds autonumbering support for DokuWiki headings.
7 *
8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9 */
10
11// phpcs:disable PSR1.Files.SideEffects.FoundWithSymbols
12use dokuwiki\Extension\ActionPlugin;
13use dokuwiki\Extension\Event;
14use dokuwiki\Extension\EventHandler;
15
16// must be run within DokuWiki
17if (!defined('DOKU_INC')) {
18    die();
19}
20// phpcs:enable
21
22class action_plugin_autonumbering extends ActionPlugin
23{
24    /**
25     * @inheritDoc
26     */
27    public function register(EventHandler $controller)
28    {
29        $controller->register_hook('PARSER_HANDLER_DONE', 'AFTER', $this, 'numberHeaders');
30    }
31
32    /**
33     * Rebuild autonumbering calls in document order and replace heading markers.
34     *
35     * DokuWiki headings are parsed as plain header calls, so syntax plugins are
36     * not evaluated inside them. Walking the final instruction list keeps heading
37     * and body counters in the same order.
38     *
39     * @param Event $event Event object
40     * @param mixed $param optional parameter passed when the event was registered
41     * @return void
42     */
43    public function numberHeaders(Event $event, $param)
44    {
45        if (!$event->data instanceof Doku_Handler) {
46            return;
47        }
48
49        global $COUNTER;
50        $COUNTER = [];
51
52        foreach ($event->data->calls as &$call) {
53            if ($call[0] === 'header') {
54                $call[1][0] = $this->replaceHeaderMarkers($call[1][0], $event->data);
55                continue;
56            }
57
58            if ($call[0] !== 'plugin' || $call[1][0] !== 'autonumbering') {
59                continue;
60            }
61
62            $syntax = plugin_load('syntax', 'autonumbering');
63            if (!$syntax instanceof syntax_plugin_autonumbering) {
64                continue;
65            }
66
67            $call[1][1] = $syntax->handle($call[1][3], $call[1][2], $call[2], $event->data);
68        }
69        unset($call);
70    }
71
72    /**
73     * Replace autonumbering markers in a heading title with plain heading text.
74     *
75     * @param string $title heading title
76     * @param Doku_Handler $handler parser handler
77     * @return string
78     */
79    protected function replaceHeaderMarkers($title, Doku_Handler $handler)
80    {
81        $syntax = plugin_load('syntax', 'autonumbering');
82        if (!$syntax instanceof syntax_plugin_autonumbering) {
83            return $title;
84        }
85
86        return preg_replace_callback(
87            '/~~[a-zA-Z0-9_\.#@]*#[a-zA-Z0-9_\.#@]*~~/',
88            function ($matches) use ($syntax, $handler) {
89                $data = $syntax->handle($matches[0], DOKU_LEXER_SPECIAL, null, $handler);
90                if (empty($data)) {
91                    return $matches[0];
92                }
93
94                return strip_tags($data[0]);
95            },
96            $title
97        );
98    }
99}
100