1<?php
2/**
3 * DokuWiki Plugin autonumbering (Syntax Component)
4 *
5 * @description         :   This plugin allows the use of multiples counters
6 *                          with multiples levels, within the same page.
7 *
8 * @syntax (Base)       :   ~~#~~
9 *                              --> Where ~~#~~ will be replaced by a number,
10 *                                  auto incremented, and saved in a common
11 *                                  counter.
12 * @syntax (ID)         :   ~~#@COUNTERID~~
13 *                              --> Where COUNTERID is an alphanumeric
14 *                                  identificator, including unserscore,
15 *                                  and starting with a @. This allows
16 *                                  the use of multiple counters.
17 * @syntax (forced)     :   ~~#NUM~~
18 *                              --> Where NUM is a positive number that will
19 *                                  be the begining of the auto incrementation
20 *                                  from there.
21 * @syntax (multilevel) :   ~~#.#~~
22 *                              --> Where .# represent a sublevel and can be
23 *                                  repeated as much as needed.
24 *
25 * @syntax (text)       :   ~~REPORT.EXAMPLE.#~~
26 *                              --> Where only the third level will be an auto
27 *                                  incremented number. The first level will
28 *                                  be a repeated text. Here it will be REPORT.
29 *                                  Samething for the second level with EXAMPLE.
30 *                                  When using text in a level, it will be
31 *                                  implicitly used as counter ID if no counter
32 *                                  ID have been set with @COUNTERID.
33 *
34 * @example             :   ~~Test.#4.#6@CTR_ONE~~
35 *                              --> Where the number will have three levels.
36 *                                  First level will be the text « Test ».
37 *                                  Second level will be an auto incremented
38 *                                  number starting at 4. Third level will be
39 *                                  an auto incremented number starting at 6.
40 *                                  All this will be save in the counter
41 *                                  « CTR_ONE ».
42 *
43 * @license             :   GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
44 */
45
46
47// must be run within DokuWiki
48if (!defined('DOKU_INC')) die();
49
50if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
51
52require_once(DOKU_PLUGIN.'syntax.php');
53
54class syntax_plugin_autonumbering extends DokuWiki_Syntax_Plugin {
55
56    var $PLUGIN_PATTERN = "~~[a-zA-Z0-9_\.#@]*#[a-zA-Z0-9_\.#@]*~~";
57    var $NUMBER_PATTERN = "[0-9]+";
58    var $COUNTER_ID_PATTERN = "[a-zA-Z0-9_]+";
59
60
61    public function getType() {
62        return 'substition';
63    }
64
65    public function getPType() {
66        return 'normal';
67    }
68
69    public function getSort() {
70        return 45;
71    }
72
73    public function connectTo($mode) {
74        $this->Lexer->addSpecialPattern($this->PLUGIN_PATTERN, $mode, 'plugin_autonumbering');
75    }
76
77    public function handle($match, $state, $pos, Doku_Handler $handler){
78        global $COUNTER;
79        $counterID = '';
80        $class = 'autonumberingAll autonumbering';
81        switch ($state) {
82            case DOKU_LEXER_SPECIAL :
83
84                if (preg_match('/~~(.*?)~~/', $match, $matches)) {
85                    $data = $matches[1];
86
87                    if (!empty($data)) {
88                        // Search for EXPLICIT counter ID
89                        if (preg_match('/@(' . $this->COUNTER_ID_PATTERN . ')/', $data, $matches)) {
90                            $counterID = $matches[1];
91                            // Remove counter ID from $data
92                            $data = str_replace('@' . $counterID, '', $data);
93                        } else {    // Search for IMPLICIT counter ID
94                            $alpha = preg_replace('/[^a-zA-Z]/', '', $data);
95                            if (!empty($alpha))
96                                $counterID = $alpha;
97                        }
98
99                        // Separate levels
100                        $dataTab = explode('.', $data);
101
102                        // Get levels quantity
103                        $levelsQty = count($dataTab);
104                        $currentLevel = $levelsQty - 1;
105
106                        // Check if parent level exist
107                        for ($i = 0; $i < $levelsQty; ++$i) {
108                            // Check if level contain text
109                            if (ctype_alpha($dataTab[$i])) {
110                                $COUNTER[$counterID][$i] = $dataTab[$i];
111                                $class .= '_' . $dataTab[$i];
112                            // Search for a forced value
113                            } else if (preg_match('/(' . $this->NUMBER_PATTERN . ')/', $dataTab[$i], $matches))
114                                if ($i == $currentLevel)
115                                    $COUNTER[$counterID][$i] = $matches[1]-1;
116                                else
117                                    $COUNTER[$counterID][$i] = $matches[1];
118                            // initialize if needed
119                            else if ((!isset($COUNTER[$counterID][$i])) || ($COUNTER[$counterID][$i] == 0))
120                                if ($i == $currentLevel)
121                                    $COUNTER[$counterID][$i] = 0;
122                                else
123                                    $COUNTER[$counterID][$i] = 1;
124                        }
125
126                        // Check if child level exist, and initialize
127                        $counter_levelsQty = count($COUNTER[$counterID]);
128                        for ($i = $currentLevel+1; $i < $counter_levelsQty; ++$i)
129                            $COUNTER[$counterID][$i] = 0;
130
131                        // Increment current level
132                        ++$COUNTER[$counterID][$currentLevel];
133
134                        // Return the number, according the level asked
135                        $number = "<span class=\"$class\">";
136                        $period = '';
137                        for ($i = 0; $i < $levelsQty; ++$i) {
138                            $number .= $period . $COUNTER[$counterID][$i];
139                            if (ctype_alpha($COUNTER[$counterID][$i])) {
140                                if ($period != '.')
141                                    $period = ' ';
142                            } else
143                                $period = '.';
144                        }
145                        $number .= '</span>';
146                        return array($number, NULL);
147                    } else {
148                        return array($match, NULL);
149                    }
150                }
151            break;
152        }
153        return array();
154    }
155
156    public function render($mode, Doku_Renderer $renderer, $data) {
157        if(($mode == 'xhtml') && (!empty($data))) {
158            list($number, $null) = $data;
159            $renderer->doc .= $number;
160            return true;
161        }
162        return false;
163    }
164
165
166    // To work with the plugin « reproduce », who needs to do
167    // the numbering prior to reproducing the code.
168    public function doNumbering($pageContent){
169        $qtyOccurrences = preg_match_all('/~~(.*?)~~/', $pageContent, $matches);
170        if ($qtyOccurrences > 0) {
171            for ($i = 0; $i < $qtyOccurrences; $i++) {
172                list($number, $null) = $this->handle($matches[0][$i], DOKU_LEXER_SPECIAL, NULL, $handler);
173                $pageContent = preg_replace('(' . $matches[0][$i] . ')', $number, $pageContent, 1);
174            }
175        }
176        return $pageContent;
177    }
178}
179