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