Writing Custom DokuWiki Plugins — Syntax, Action, and Helper Plugin Development
In this tutorial, you'll learn how to write custom DokuWiki plugins, including syntax plugins for custom markup, action plugins for event handling, and helper plugins for shared functionality.
What You'll Learn
- Planning a custom plugin
- Writing a syntax plugin (custom markup)
- Writing an action plugin (event handling)
- Writing a helper plugin (shared functions)
- Plugin testing and debugging
- Packaging and distributing plugins
Why It Matters
Pre-built plugins cover common use cases, but every wiki has unique requirements. A custom syntax tag for your internal tools, an action plugin that integrates with your company's API, or a helper plugin that provides reusable functions — these are tasks that only you can implement. Learning to write DokuWiki plugins means you never hit a "DokuWiki cannot do that" wall.
Real-World Use
A company runs its internal tools dashboard on DokuWiki. They write a custom syntax plugin that renders [[jira:PROJ-123]] as a link to their Jira ticket, complete with the ticket status icon fetched from the Jira API. The plugin makes documentation more useful because every Jira reference is a live link. The entire plugin is 50 lines of PHP.
Learning Path
flowchart LR A[Essential Plugins] --> B[Custom Plugins] B --> C[Plugin Configuration] C --> D[Plugin Security] D --> E[Template Anatomy] E --> F[Template Variables]
Planning a Plugin
Before writing code, answer these questions:
- What type of plugin? Syntax (new markup), Action (event hook), Admin (admin page), or Helper (shared functions)?
- What is the plugin name? Short, lowercase, no spaces. Example:
jiralink - What events or syntax does it need? For syntax plugins, define the markup pattern. For action plugins, identify the events.
- Does a similar plugin already exist? Check the plugin Repository to avoid duplication.
Plugin File Structure
lib/plugins/myplugin/
├── plugin.info.txt # Manifest (required)
├── syntax.php # Syntax plugin class
├── action.php # Action plugin class
├── helper.php # Helper plugin class
├── admin.php # Admin plugin class
├── conf/
│ ├── default.php # Default configuration
│ └── metadata.php # Configuration metadata
├── lang/
│ └── en/
│ └── lang.php # Language strings
└── style.css # Plugin styles
Writing a Syntax Plugin
Syntax plugins add custom markup to DokuWiki's parser. They define a pattern to match and a replacement to render.
Example: Jira Link Plugin
This plugin converts [[jira:PROJ-123]] into a link to a Jira ticket.
plugin.info.txt:
base jiralink
author Your Name
email your@email.com
date 2026-06-28
name Jira Link Plugin
desc Converts [[jira:TICKET-ID]] to Jira links
url https://example.com/jiralink
syntax.php:
<?php
class syntax_plugin_jiralink extends DokuWiki_Syntax_Plugin {
public function getType() {
return 'substition';
}
public function getPType() {
return 'normal';
}
public function getSort() {
return 150;
}
public function connectTo($mode) {
$this->Lexer->addSpecialPattern('\[\[jira:[A-Z]+-\d+\]\]', $mode, 'plugin_jiralink');
}
public function handle($match, $state, $pos, Doku_Handler $handler) {
$ticketId = substr($match, 6, -2);
return array($ticketId);
}
public function render($mode, Doku_Renderer $renderer, $data) {
if ($mode != 'xhtml') return false;
$ticketId = $data[0];
$jiraUrl = 'https://your-jira-instance.atlassian.net/browse/' . $ticketId;
$renderer->doc .= '<a href="' . $jiraUrl . '" class="jira-link">' . $ticketId . '</a>';
return true;
}
}
Syntax Plugin Methods
- getType(): Returns the plugin type (
substition,container,baseonly, etc.) - getPType(): Paragraph handling (
normal,block,stack) - getSort(): Priority in Parsing order (lower numbers = earlier)
- connectTo(): Register patterns with the lexer
- handle(): Process the matched pattern
- render(): Generate output (XHTML, metadata, etc.)
Pattern Types
- addSpecialPattern(): Matches specific pattern, replaces entire string
- addEntryPattern(): Matches opening and closing patterns (container)
- addPattern(): General pattern matching
Writing an Action Plugin
Action plugins hook into events to modify behavior.
Example: Page Save Logger Plugin
This plugin logs every page save to a file.
plugin.info.txt:
base pagesavelog
author Your Name
email your@email.com
date 2026-06-28
name Page Save Log
desc Logs all page saves to data/log/pagesaves.log
url https://example.com/pagesavelog
action.php:
<?php
class action_plugin_pagesavelog extends DokuWiki_Action_Plugin {
public function register(Doku_Event_Handler $controller) {
$controller->register_hook(
'COMMON_WIKIPAGE_SAVE',
'AFTER',
$this,
'log_page_save'
);
}
public function log_page_save($event, $param) {
$data = $event->data;
$logFile = DOKU_CONF . '../data/log/pagesaves.log';
$logEntry = date('Y-m-d H:i:s') . "\t" .
$data['id'] . "\t" .
$data['oldRevision'] . "\t" .
$data['newRevision'] . "\t" .
$_SERVER['REMOTE_ADDR'] . "\n";
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
}
Common Events for Action Plugins
| Event | Timing | Use Case |
|---|---|---|
| COMMON_WIKIPAGE_SAVE | AFTER | Logging, notifications |
| COMMON_PAGE_SAVE | BEFORE | Content filtering, validation |
| TPL_ACT_RENDER | BEFORE | Modifying page output |
| HTML_EDITFORM_OUTPUT | BEFORE | Adding form fields |
| ACTION_ACT_PREPROCESS | BEFORE | Custom action handling |
Writing a Helper Plugin
Helper plugins provide reusable functions for other plugins.
Example: API Client Helper
<?php
class helper_plugin_apiclient extends DokuWiki_Plugin {
public function get($url, $headers = array()) {
$http = new DokuHTTPClient();
$http->headers = $headers;
$response = $http->get($url);
return $response;
}
public function post($url, $data, $headers = array()) {
$http = new DokuHTTPClient();
$http->headers = $headers;
$response = $http->post($url, $data);
return $response;
}
public function jsonGet($url) {
$response = $this->get($url);
return json_decode($response, true);
}
}
Other plugins access the helper:
<?php
$helper = $this->loadHelper('apiclient');
$data = $helper->jsonGet('https://api.example.com/data');
Testing Plugins
Enable Debug Mode
<?php
// conf/local.php
$conf['plugin']['myplugin']['debug'] = 1;
Check PHP Error Logs
tail -f /var/log/apache2/error.log
Test with Minimal Pages
Create a test page with only the plugin feature to isolate issues.
Test on a Staging Wiki
Never test new plugins on a production wiki. Use a development copy.
Plugin Packaging and Distribution
Naming Convention
- Plugin directory: lowercase, no spaces, no underscores
- Plugin base name: matches directory name
Distribution File
# Create a distributable ZIP file
cd lib/plugins/
zip -r myplugin.zip myplugin/
README
Include a README with:
- Plugin name and description
- Requirements (PHP version, DokuWiki version)
- Installation instructions
- Configuration options
- Usage examples
- Changelog
Common Mistakes
- Not checking the event data structure: Each event passes different data. Read the DokuWiki source or documentation to understand what
$event->datacontains. - Hard-coding paths: Use DokuWiki constants like
DOKU_INC,DOKU_CONF, andDOKU_PLUGINinstead of hard-coded paths. - Forgetting to return
falsefrom render() for unsupported modes: If your plugin cannot render in the requested mode (e.g.,textinstead ofxhtml), returnfalseto let other renderers handle it. - Not escaping output: Plugin output should be escaped to prevent XSS. Use
hsc()or$renderer->doc .= hsc($data). - Making plugins too complex: A plugin should do one thing well. If you need multiple features, create multiple small plugins.
Practice Questions
- What are the three main plugin types in DokuWiki, and when would you use each?
- How does a syntax plugin register a custom pattern with the DokuWiki lexer?
- What event would you hook into to modify page content before it is saved?
- Challenge: Write a custom syntax plugin that implements a
[[weather:city]]tag. The plugin should: fetch the current temperature for the given city from a free weather API, cache the result for 1 hour (to avoid excessive API calls), display the temperature with a weather icon, and gracefully fall back to a link if the API is unavailable. Include plugin.info.txt, syntax.php, and conf/default.php files. Test the plugin with at least 3 cities.
FAQ
Mini Project
Goal: Write and test a complete syntax plugin.
- Plan a syntax plugin that adds a
[[server:hostname]]tag linking to a server monitoring dashboard - Create the
plugin.info.txtmanifest - Write the
syntax.phpfile with pattern matching and rendering - Add configuration (
conf/default.php) for the monitoring dashboard URL - Install the plugin on your testing wiki
- Create a test page using the syntax tag
- Verify the link renders correctly
- Add error handling for invalid server names
What's Next
Custom plugins extend functionality. Learn about plugin configuration options and how to make your plugins configurable.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro