xref: /plugin/pagecss/syntax.php (revision f2a25b8dc0d66b25b74db7510495b2d5d6a554d7)
16a458d92SdWiGhT<?php
26a458d92SdWiGhT/**
36a458d92SdWiGhT * DokuWiki Plugin pagecss
46a458d92SdWiGhT *
5*f2a25b8dSdwightmulcahy * This file defines the syntax component of the 'pagecss' DokuWiki plugin.
6*f2a25b8dSdwightmulcahy * It is responsible for recognizing and parsing custom CSS blocks embedded
7*f2a25b8dSdwightmulcahy * directly within DokuWiki pages using the `<pagecss>...</pagecss>` tags.
8*f2a25b8dSdwightmulcahy *
9*f2a25b8dSdwightmulcahy * While this syntax plugin captures the CSS content, it does NOT directly
10*f2a25b8dSdwightmulcahy * output it to the HTML. The actual injection of this CSS into the
11*f2a25b8dSdwightmulcahy * `<head>` section of the DokuWiki page is handled by the `action.php`
12*f2a25b8dSdwightmulcahy * component of this plugin (specifically, `action_plugin_pagecss`).
13*f2a25b8dSdwightmulcahy *
146a458d92SdWiGhT * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
156a458d92SdWiGhT * @author  dWiGhT Mulcahy <dWiGhT.Codes@gmail.com>
166a458d92SdWiGhT */
176a458d92SdWiGhT
186a458d92SdWiGhT// Ensure DokuWiki is loaded and prevent direct access to the plugin file.
196a458d92SdWiGhT// DOKU_INC is a constant defined by DokuWiki, representing its installation directory.
20*f2a25b8dSdwightmulcahy// If DOKU_INC is not defined, it means the script is being accessed directly,
21*f2a25b8dSdwightmulcahy// which is not allowed for DokuWiki plugins.
226a458d92SdWiGhTif (!defined('DOKU_INC')) {
236a458d92SdWiGhT    die();
246a458d92SdWiGhT}
256a458d92SdWiGhT
266a458d92SdWiGhT/**
276a458d92SdWiGhT * Class syntax_plugin_pagecss
286a458d92SdWiGhT *
29*f2a25b8dSdwightmulcahy * This class extends DokuWiki_Syntax_Plugin, providing the necessary
30*f2a25b8dSdwightmulcahy * methods to define a custom syntax for embedding CSS within DokuWiki pages.
31*f2a25b8dSdwightmulcahy * It's responsible for:
32*f2a25b8dSdwightmulcahy * 1. Defining the type of syntax (container).
33*f2a25b8dSdwightmulcahy * 2. Specifying the block-level nature of the syntax.
34*f2a25b8dSdwightmulcahy * 3. Setting its processing priority.
35*f2a25b8dSdwightmulcahy * 4. Connecting its entry and exit patterns to the DokuWiki lexer.
36*f2a25b8dSdwightmulcahy * 5. Handling the data parsed by the lexer (capturing the CSS content).
37*f2a25b8dSdwightmulcahy * 6. (Optionally) Rendering output, though for this plugin, rendering is minimal
38*f2a25b8dSdwightmulcahy * as the CSS is passed to an action plugin for actual injection.
396a458d92SdWiGhT */
406a458d92SdWiGhTclass syntax_plugin_pagecss extends DokuWiki_Syntax_Plugin {
416a458d92SdWiGhT
426a458d92SdWiGhT    /**
436a458d92SdWiGhT     * Defines the type of syntax plugin.
446a458d92SdWiGhT     *
45*f2a25b8dSdwightmulcahy     * 'container' means this plugin has distinct opening and closing tags,
46*f2a25b8dSdwightmulcahy     * and it encapsulates content between them (e.g., `<pagecss>...</pagecss>`).
47*f2a25b8dSdwightmulcahy     * Other common types include 'substition' (self-closing tags), 'protected'
48*f2a25b8dSdwightmulcahy     * (content not parsed by DokuWiki), 'formatting' (inline text formatting), etc.
496a458d92SdWiGhT     *
506a458d92SdWiGhT     * @return string 'container'
516a458d92SdWiGhT     */
526a458d92SdWiGhT    public function getType() {
536a458d92SdWiGhT        return 'container';
546a458d92SdWiGhT    }
556a458d92SdWiGhT
566a458d92SdWiGhT    /**
576a458d92SdWiGhT     * Defines the paragraph type.
586a458d92SdWiGhT     *
59*f2a25b8dSdwightmulcahy     * 'block' indicates that this syntax element creates a block-level element.
60*f2a25b8dSdwightmulcahy     * This means it cannot be part of an existing paragraph and will typically
61*f2a25b8dSdwightmulcahy     * force a new line before and after its content in the parsed output.
62*f2a25b8dSdwightmulcahy     * This is appropriate for CSS blocks which are not meant to be inline text.
636a458d92SdWiGhT     *
646a458d92SdWiGhT     * @return string 'block'
656a458d92SdWiGhT     */
666a458d92SdWiGhT    public function getPType() {
676a458d92SdWiGhT        return 'block';
686a458d92SdWiGhT    }
696a458d92SdWiGhT
706a458d92SdWiGhT    /**
71*f2a25b8dSdwightmulcahy     * Defines the sort order (priority) for syntax plugins.
726a458d92SdWiGhT     *
73*f2a25b8dSdwightmulcahy     * DokuWiki processes syntax plugins in ascending order of their sort value.
74*f2a25b8dSdwightmulcahy     * A higher number means the plugin is processed later. This plugin is given
75*f2a25b8dSdwightmulcahy     * a relatively high number (199). This ensures that it runs after most
76*f2a25b8dSdwightmulcahy     * other common DokuWiki syntax elements have been processed, allowing it
77*f2a25b8dSdwightmulcahy     * to capture raw text that might otherwise be interpreted by other plugins.
786a458d92SdWiGhT     *
79*f2a25b8dSdwightmulcahy     * @return int The sort order value (e.g., 199).
806a458d92SdWiGhT     */
816a458d92SdWiGhT    public function getSort() {
826a458d92SdWiGhT        return 199;
836a458d92SdWiGhT    }
846a458d92SdWiGhT
856a458d92SdWiGhT    /**
866a458d92SdWiGhT     * Connects the plugin's syntax patterns to the DokuWiki lexer.
876a458d92SdWiGhT     *
88*f2a25b8dSdwightmulcahy     * This method is crucial for telling DokuWiki how to identify the start
89*f2a25b8dSdwightmulcahy     * of this plugin's custom syntax within the wiki text.
906a458d92SdWiGhT     *
916a458d92SdWiGhT     * @param string $mode The current DokuWiki lexer mode (e.g., 'base', 'p_wiki').
92*f2a25b8dSdwightmulcahy     * Plugins often connect to 'base' mode to be available everywhere.
936a458d92SdWiGhT     */
946a458d92SdWiGhT    public function connectTo($mode) {
95*f2a25b8dSdwightmulcahy        // Adds an entry pattern for the '<pagecss>' opening tag.
96*f2a25b8dSdwightmulcahy        // The regular expression '<pagecss>(?=.*?</pagecss>)' is used:
976a458d92SdWiGhT        // - '<pagecss>': Matches the literal string "<pagecss>".
986a458d92SdWiGhT        // - (?=.*?</pagecss>): This is a positive lookahead assertion.
99*f2a25b8dSdwightmulcahy        //   - `(?=...)`: Asserts that the characters immediately following the current position
100*f2a25b8dSdwightmulcahy        //                  match the pattern inside the lookahead, but these characters are NOT
101*f2a25b8dSdwightmulcahy        //                  consumed by this match. This means the lexer won't advance past `<pagecss>`
102*f2a25b8dSdwightmulcahy        //                  based on the lookahead part.
103*f2a25b8dSdwightmulcahy        //   - `.*?`: Matches any character (`.`) zero or more times (`*`) in a non-greedy way (`?`).
104*f2a25b8dSdwightmulcahy        //            This ensures it matches the shortest possible string until the next part.
105*f2a25b8dSdwightmulcahy        //   - `</pagecss>`: Matches the literal closing tag.
106*f2a25b8dSdwightmulcahy        // This lookahead ensures that the '<pagecss>' tag is only recognized as a valid entry
107*f2a25b8dSdwightmulcahy        // pattern if it is eventually followed by a matching closing '</pagecss>' tag.
108*f2a25b8dSdwightmulcahy        // 'plugin_pagecss' is the name of the new lexer state (or mode) that DokuWiki enters
109*f2a25b8dSdwightmulcahy        // when this pattern is matched. All content until the exit pattern will be processed
110*f2a25b8dSdwightmulcahy        // within this 'plugin_pagecss' state.
1116a458d92SdWiGhT        $this->Lexer->addEntryPattern('<pagecss>(?=.*?</pagecss>)',$mode,'plugin_pagecss');
1126a458d92SdWiGhT    }
1136a458d92SdWiGhT
1146a458d92SdWiGhT    /**
1156a458d92SdWiGhT     * Defines how the plugin's syntax pattern exits the current state.
1166a458d92SdWiGhT     *
117*f2a25b8dSdwightmulcahy     * This method specifies the closing tag for this 'container' type plugin.
118*f2a25b8dSdwightmulcahy     * When the lexer is in the 'plugin_pagecss' state and encounters this pattern,
119*f2a25b8dSdwightmulcahy     * it will exit that state and return to the previous lexer mode.
1206a458d92SdWiGhT     */
1216a458d92SdWiGhT    public function postConnect() {
1226a458d92SdWiGhT        // Adds an exit pattern for '</pagecss>'.
123*f2a25b8dSdwightmulcahy        // When this pattern is encountered while the lexer is in the 'plugin_pagecss' state,
124*f2a25b8dSdwightmulcahy        // it signifies the end of the custom CSS block.
1256a458d92SdWiGhT        $this->Lexer->addExitPattern('</pagecss>', 'plugin_pagecss');
1266a458d92SdWiGhT    }
1276a458d92SdWiGhT
1286a458d92SdWiGhT    /**
1296a458d92SdWiGhT     * Handles the matched syntax.
1306a458d92SdWiGhT     *
131*f2a25b8dSdwightmulcahy     * This method is called by the DokuWiki parser when the lexer identifies
132*f2a25b8dSdwightmulcahy     * content related to this plugin's defined syntax patterns. Its primary
133*f2a25b8dSdwightmulcahy     * role is to process the raw matched text and transform it into a
134*f2a25b8dSdwightmulcahy     * structured data representation that can be used by the renderer or other
135*f2a25b8dSdwightmulcahy     * parts of the plugin (like the action plugin).
1366a458d92SdWiGhT     *
137*f2a25b8dSdwightmulcahy     * @param string        $match   The raw text that was matched by the lexer (e.g., "<pagecss>", "body { color: red; }", "</pagecss>").
138*f2a25b8dSdwightmulcahy     * @param int           $state   The current state of the lexer when the match occurred.
139*f2a25b8dSdwightmulcahy     * Possible states include:
140*f2a25b8dSdwightmulcahy     * - DOKU_LEXER_ENTER: The opening tag was matched (e.g., '<pagecss>').
141*f2a25b8dSdwightmulcahy     * - DOKU_LEXER_UNMATCHED: Content between the entry and exit patterns (the actual CSS).
142*f2a25b8dSdwightmulcahy     * - DOKU_LEXER_EXIT: The closing tag was matched (e.g., '</pagecss>').
1436a458d92SdWiGhT     * @param int           $pos     The byte position of the match within the original text.
144*f2a25b8dSdwightmulcahy     * @param Doku_Handler  $handler The DokuWiki handler object. This object is used to
145*f2a25b8dSdwightmulcahy     * manipulate the parser's instruction stream (e.g., adding instructions).
146*f2a25b8dSdwightmulcahy     * @return array|bool   Returns an array containing the captured CSS content if the state is
147*f2a25b8dSdwightmulcahy     * DOKU_LEXER_UNMATCHED, otherwise returns true to indicate successful handling
148*f2a25b8dSdwightmulcahy     * of the enter/exit states (no specific data needed for those).
1496a458d92SdWiGhT     */
1506a458d92SdWiGhT    public function handle($match, $state, $pos, Doku_Handler $handler){
151*f2a25b8dSdwightmulcahy        // This condition checks if the lexer is processing the actual content
152*f2a25b8dSdwightmulcahy        // *between* the opening and closing tags. This is where the user's CSS code resides.
1536a458d92SdWiGhT        if ($state === DOKU_LEXER_UNMATCHED) {
154*f2a25b8dSdwightmulcahy            // Return an associative array where the key 'css' holds the matched content.
155*f2a25b8dSdwightmulcahy            // This data will then be passed to the `render` method of this plugin.
1566a458d92SdWiGhT            return ['css' => $match];
1576a458d92SdWiGhT        }
158*f2a25b8dSdwightmulcahy        // For the DOKU_LEXER_ENTER (opening tag) and DOKU_LEXER_EXIT (closing tag) states,
159*f2a25b8dSdwightmulcahy        // no specific data needs to be captured by the syntax plugin itself,
160*f2a25b8dSdwightmulcahy        // so we simply return `true` to acknowledge that the state transition was handled.
1616a458d92SdWiGhT        return true;
1626a458d92SdWiGhT    }
1636a458d92SdWiGhT
1646a458d92SdWiGhT    /**
1656a458d92SdWiGhT     * Renders the handled data into the DokuWiki output.
1666a458d92SdWiGhT     *
167*f2a25b8dSdwightmulcahy     * This method is called by the DokuWiki renderer to generate the final HTML
168*f2a25b8dSdwightmulcahy     * or other output format (e.g., XHTML, ODT).
169*f2a25b8dSdwightmulcahy     *
170*f2a25b8dSdwightmulcahy     * For the 'pagecss' plugin, this `render` method primarily serves for
171*f2a25b8dSdwightmulcahy     * debugging purposes. The actual injection of the CSS into the page's
172*f2a25b8dSdwightmulcahy     * `<head>` section is NOT done here. Instead, the CSS is captured by
173*f2a25b8dSdwightmulcahy     * the `handle()` method and then typically retrieved and processed by an
174*f2a25b8dSdwightmulcahy     * accompanying action plugin (defined in `action.php`) which listens to
175*f2a25b8dSdwightmulcahy     * DokuWiki events (e.g., `TPL_METAHEADER_OUTPUT`) to insert the CSS
176*f2a25b8dSdwightmulcahy     * at the correct location in the HTML document.
1776a458d92SdWiGhT     *
1786a458d92SdWiGhT     * @param string        $mode     The rendering mode (e.g., 'xhtml', 'odt').
179*f2a25b8dSdwightmulcahy     * @param Doku_Renderer $renderer The DokuWiki renderer object, used to output HTML, etc.
180*f2a25b8dSdwightmulcahy     * @param mixed         $data     The data returned by the `handle()` method. For this plugin,
181*f2a25b8dSdwightmulcahy     * it's an array like `['css' => '...']` when processing the CSS content.
182*f2a25b8dSdwightmulcahy     * @return bool     Always returns true, indicating that the rendering process for this
183*f2a25b8dSdwightmulcahy     * syntax element has completed successfully, even if no visible output is produced.
1846a458d92SdWiGhT     */
1856a458d92SdWiGhT    public function render($mode, Doku_Renderer $renderer, $data) {
186*f2a25b8dSdwightmulcahy        // Check if the received data is an array, which signifies that it contains
187*f2a25b8dSdwightmulcahy        // the CSS content captured during the DOKU_LEXER_UNMATCHED state in `handle()`.
1886a458d92SdWiGhT        if (is_array($data)) {
189*f2a25b8dSdwightmulcahy            // Log the captured CSS content to the DokuWiki debug log.
190*f2a25b8dSdwightmulcahy            // This is useful for verifying that the plugin correctly extracts the CSS.
191*f2a25b8dSdwightmulcahy            // `substr($data['css'], 0, 200)` truncates the CSS string to the first
192*f2a25b8dSdwightmulcahy            // 200 characters to prevent excessively long entries in the debug log.
193*f2a25b8dSdwightmulcahy            dbglog("pagecss plugin: Captured CSS (first 200 chars): " . substr($data['css'], 0, 200));
1946a458d92SdWiGhT
195*f2a25b8dSdwightmulcahy            // IMPORTANT NOTE: This `render` method DOES NOT output the CSS directly
196*f2a25b8dSdwightmulcahy            // to the HTML page's `<body>`. If it did, the CSS would not be applied
197*f2a25b8dSdwightmulcahy            // correctly and would likely be visible as text on the page.
198*f2a25b8dSdwightmulcahy            // The sole purpose of this `syntax.php` file is to identify, parse,
199*f2a25b8dSdwightmulcahy            // and capture the CSS content. The `action.php` file (the action plugin)
200*f2a25b8dSdwightmulcahy            // is responsible for retrieving this captured CSS and injecting it into
201*f2a25b8dSdwightmulcahy            // the `<head>` section of the HTML.
2026a458d92SdWiGhT        }
203*f2a25b8dSdwightmulcahy        // Always return true to signal that the rendering process for this syntax element
204*f2a25b8dSdwightmulcahy        // has finished successfully.
2056a458d92SdWiGhT        return true;
2066a458d92SdWiGhT    }
2076a458d92SdWiGhT}
208