xref: /plugin/pagecss/syntax.php (revision f2a25b8dc0d66b25b74db7510495b2d5d6a554d7)
1<?php
2/**
3 * DokuWiki Plugin pagecss
4 *
5 * This file defines the syntax component of the 'pagecss' DokuWiki plugin.
6 * It is responsible for recognizing and parsing custom CSS blocks embedded
7 * directly within DokuWiki pages using the `<pagecss>...</pagecss>` tags.
8 *
9 * While this syntax plugin captures the CSS content, it does NOT directly
10 * output it to the HTML. The actual injection of this CSS into the
11 * `<head>` section of the DokuWiki page is handled by the `action.php`
12 * component of this plugin (specifically, `action_plugin_pagecss`).
13 *
14 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
15 * @author  dWiGhT Mulcahy <dWiGhT.Codes@gmail.com>
16 */
17
18// Ensure DokuWiki is loaded and prevent direct access to the plugin file.
19// DOKU_INC is a constant defined by DokuWiki, representing its installation directory.
20// If DOKU_INC is not defined, it means the script is being accessed directly,
21// which is not allowed for DokuWiki plugins.
22if (!defined('DOKU_INC')) {
23    die();
24}
25
26/**
27 * Class syntax_plugin_pagecss
28 *
29 * This class extends DokuWiki_Syntax_Plugin, providing the necessary
30 * methods to define a custom syntax for embedding CSS within DokuWiki pages.
31 * It's responsible for:
32 * 1. Defining the type of syntax (container).
33 * 2. Specifying the block-level nature of the syntax.
34 * 3. Setting its processing priority.
35 * 4. Connecting its entry and exit patterns to the DokuWiki lexer.
36 * 5. Handling the data parsed by the lexer (capturing the CSS content).
37 * 6. (Optionally) Rendering output, though for this plugin, rendering is minimal
38 * as the CSS is passed to an action plugin for actual injection.
39 */
40class syntax_plugin_pagecss extends DokuWiki_Syntax_Plugin {
41
42    /**
43     * Defines the type of syntax plugin.
44     *
45     * 'container' means this plugin has distinct opening and closing tags,
46     * and it encapsulates content between them (e.g., `<pagecss>...</pagecss>`).
47     * Other common types include 'substition' (self-closing tags), 'protected'
48     * (content not parsed by DokuWiki), 'formatting' (inline text formatting), etc.
49     *
50     * @return string 'container'
51     */
52    public function getType() {
53        return 'container';
54    }
55
56    /**
57     * Defines the paragraph type.
58     *
59     * 'block' indicates that this syntax element creates a block-level element.
60     * This means it cannot be part of an existing paragraph and will typically
61     * force a new line before and after its content in the parsed output.
62     * This is appropriate for CSS blocks which are not meant to be inline text.
63     *
64     * @return string 'block'
65     */
66    public function getPType() {
67        return 'block';
68    }
69
70    /**
71     * Defines the sort order (priority) for syntax plugins.
72     *
73     * DokuWiki processes syntax plugins in ascending order of their sort value.
74     * A higher number means the plugin is processed later. This plugin is given
75     * a relatively high number (199). This ensures that it runs after most
76     * other common DokuWiki syntax elements have been processed, allowing it
77     * to capture raw text that might otherwise be interpreted by other plugins.
78     *
79     * @return int The sort order value (e.g., 199).
80     */
81    public function getSort() {
82        return 199;
83    }
84
85    /**
86     * Connects the plugin's syntax patterns to the DokuWiki lexer.
87     *
88     * This method is crucial for telling DokuWiki how to identify the start
89     * of this plugin's custom syntax within the wiki text.
90     *
91     * @param string $mode The current DokuWiki lexer mode (e.g., 'base', 'p_wiki').
92     * Plugins often connect to 'base' mode to be available everywhere.
93     */
94    public function connectTo($mode) {
95        // Adds an entry pattern for the '<pagecss>' opening tag.
96        // The regular expression '<pagecss>(?=.*?</pagecss>)' is used:
97        // - '<pagecss>': Matches the literal string "<pagecss>".
98        // - (?=.*?</pagecss>): This is a positive lookahead assertion.
99        //   - `(?=...)`: Asserts that the characters immediately following the current position
100        //                  match the pattern inside the lookahead, but these characters are NOT
101        //                  consumed by this match. This means the lexer won't advance past `<pagecss>`
102        //                  based on the lookahead part.
103        //   - `.*?`: Matches any character (`.`) zero or more times (`*`) in a non-greedy way (`?`).
104        //            This ensures it matches the shortest possible string until the next part.
105        //   - `</pagecss>`: Matches the literal closing tag.
106        // This lookahead ensures that the '<pagecss>' tag is only recognized as a valid entry
107        // pattern if it is eventually followed by a matching closing '</pagecss>' tag.
108        // 'plugin_pagecss' is the name of the new lexer state (or mode) that DokuWiki enters
109        // when this pattern is matched. All content until the exit pattern will be processed
110        // within this 'plugin_pagecss' state.
111        $this->Lexer->addEntryPattern('<pagecss>(?=.*?</pagecss>)',$mode,'plugin_pagecss');
112    }
113
114    /**
115     * Defines how the plugin's syntax pattern exits the current state.
116     *
117     * This method specifies the closing tag for this 'container' type plugin.
118     * When the lexer is in the 'plugin_pagecss' state and encounters this pattern,
119     * it will exit that state and return to the previous lexer mode.
120     */
121    public function postConnect() {
122        // Adds an exit pattern for '</pagecss>'.
123        // When this pattern is encountered while the lexer is in the 'plugin_pagecss' state,
124        // it signifies the end of the custom CSS block.
125        $this->Lexer->addExitPattern('</pagecss>', 'plugin_pagecss');
126    }
127
128    /**
129     * Handles the matched syntax.
130     *
131     * This method is called by the DokuWiki parser when the lexer identifies
132     * content related to this plugin's defined syntax patterns. Its primary
133     * role is to process the raw matched text and transform it into a
134     * structured data representation that can be used by the renderer or other
135     * parts of the plugin (like the action plugin).
136     *
137     * @param string        $match   The raw text that was matched by the lexer (e.g., "<pagecss>", "body { color: red; }", "</pagecss>").
138     * @param int           $state   The current state of the lexer when the match occurred.
139     * Possible states include:
140     * - DOKU_LEXER_ENTER: The opening tag was matched (e.g., '<pagecss>').
141     * - DOKU_LEXER_UNMATCHED: Content between the entry and exit patterns (the actual CSS).
142     * - DOKU_LEXER_EXIT: The closing tag was matched (e.g., '</pagecss>').
143     * @param int           $pos     The byte position of the match within the original text.
144     * @param Doku_Handler  $handler The DokuWiki handler object. This object is used to
145     * manipulate the parser's instruction stream (e.g., adding instructions).
146     * @return array|bool   Returns an array containing the captured CSS content if the state is
147     * DOKU_LEXER_UNMATCHED, otherwise returns true to indicate successful handling
148     * of the enter/exit states (no specific data needed for those).
149     */
150    public function handle($match, $state, $pos, Doku_Handler $handler){
151        // This condition checks if the lexer is processing the actual content
152        // *between* the opening and closing tags. This is where the user's CSS code resides.
153        if ($state === DOKU_LEXER_UNMATCHED) {
154            // Return an associative array where the key 'css' holds the matched content.
155            // This data will then be passed to the `render` method of this plugin.
156            return ['css' => $match];
157        }
158        // For the DOKU_LEXER_ENTER (opening tag) and DOKU_LEXER_EXIT (closing tag) states,
159        // no specific data needs to be captured by the syntax plugin itself,
160        // so we simply return `true` to acknowledge that the state transition was handled.
161        return true;
162    }
163
164    /**
165     * Renders the handled data into the DokuWiki output.
166     *
167     * This method is called by the DokuWiki renderer to generate the final HTML
168     * or other output format (e.g., XHTML, ODT).
169     *
170     * For the 'pagecss' plugin, this `render` method primarily serves for
171     * debugging purposes. The actual injection of the CSS into the page's
172     * `<head>` section is NOT done here. Instead, the CSS is captured by
173     * the `handle()` method and then typically retrieved and processed by an
174     * accompanying action plugin (defined in `action.php`) which listens to
175     * DokuWiki events (e.g., `TPL_METAHEADER_OUTPUT`) to insert the CSS
176     * at the correct location in the HTML document.
177     *
178     * @param string        $mode     The rendering mode (e.g., 'xhtml', 'odt').
179     * @param Doku_Renderer $renderer The DokuWiki renderer object, used to output HTML, etc.
180     * @param mixed         $data     The data returned by the `handle()` method. For this plugin,
181     * it's an array like `['css' => '...']` when processing the CSS content.
182     * @return bool     Always returns true, indicating that the rendering process for this
183     * syntax element has completed successfully, even if no visible output is produced.
184     */
185    public function render($mode, Doku_Renderer $renderer, $data) {
186        // Check if the received data is an array, which signifies that it contains
187        // the CSS content captured during the DOKU_LEXER_UNMATCHED state in `handle()`.
188        if (is_array($data)) {
189            // Log the captured CSS content to the DokuWiki debug log.
190            // This is useful for verifying that the plugin correctly extracts the CSS.
191            // `substr($data['css'], 0, 200)` truncates the CSS string to the first
192            // 200 characters to prevent excessively long entries in the debug log.
193            dbglog("pagecss plugin: Captured CSS (first 200 chars): " . substr($data['css'], 0, 200));
194
195            // IMPORTANT NOTE: This `render` method DOES NOT output the CSS directly
196            // to the HTML page's `<body>`. If it did, the CSS would not be applied
197            // correctly and would likely be visible as text on the page.
198            // The sole purpose of this `syntax.php` file is to identify, parse,
199            // and capture the CSS content. The `action.php` file (the action plugin)
200            // is responsible for retrieving this captured CSS and injecting it into
201            // the `<head>` section of the HTML.
202        }
203        // Always return true to signal that the rendering process for this syntax element
204        // has finished successfully.
205        return true;
206    }
207}
208