DokuWiki

It's better when it's simple

User Tools

Site Tools


devel:plugin_programming_tips

Tips for programming plugins

Please write down tips you've discovered making it easier for others to make plugins. I actually had to sit down and fgrep myself to this info, and I hope that it will help others ;-)

On this page are some tips (see ToC right). Useful resources elsewhere in the wiki are:

Customized Section editing

When you like to change only small pieces of the wiki text via your plugin, it is recommended to look for section editing. This let you provide your interface elements that can edit a specific marked piece of wiki text.

Please refer to section editor for details on the implementation in your plugin.

User lists and info

You can access user-lists/info and more for internal use by declaring the following in a function that needs it:

global $auth;
if ($auth->canDo('getUsers')) {  // is this feature available?
    $auth->retrieveUsers(0, 0, $filter);
}

Where $filter is an array with one or more of the following keys user, name, mail, or grps. Several values in each using | as a separator.

For example, to retrieve all users in the group 'admin', one would use:

$filter['grps'] = 'admin';
$array_of_matches = $auth->retrieveUsers(0, 0, $filter);

Be aware that the authentication plugin needs to implement this function. Otherwise it returns always an empty array.

See also authentication plugins mention of retrieveUsers() and other functions.

DokuWiki Global Variables

DokuWiki uses a number of global variables to hold information about the current page, current user and the actions being performed.
Details of these can be found on the environment page.

Making your plugins more secure

Clean all input and output

Be aware that every one can do 'requests' on your forms and pages with all data they think off. People can send the same kind of data as a brave one using your form, without using your form itself. So everything can be included. So check always whether the given data is that what you allowed.

And when you output data, especially in the HTML of your interface, your plugin should ensure any data output has all HTML special characters converted to HTML entities using the htmlspecialchars() function. DokuWiki provides a convenient shortcut called hsc() for the function. URLs values should be escaped using rawurlencode(). This makes sure input is always outputted as text and not as executable code. Malicious users could otherwise introduce what ever JavaScript and HTML code they want on your site and can manipulate and change everything.

Protect forms and action urls

If you use forms in your plugins or urls that initiate actions, you should include a hidden form field with the session-based security token. In the current version of DokuWiki you can generate this field by calling the function formSecurityToken(). Before you process the form input, call checkSecurityToken(). This function checks if the sent security token is correct.

Scenario If you wonder, why this will make your plugins more secure, consider the following scenario:

You have written a plugin that displays a form to delete several pages at once.
An attacker knows you regularly log in to your wiki and you use a site that is under his control.
He places an images tag on his page that links to your doku.php and has all the form parameters for deleting pages in the URL.
Each time you see the page form the attacker, your browser requests the image from your DokuWiki installation, thereby deleting pages.

This attack is called Cross Site Request forgery.

Other security tips are listed and explained on the dedicated page.

Use correct regular expressions

Use correct regular expressions for syntax search patterns. If the search pattern is incorrect, it can produce unwanted effects in combination with other plugins.

Use reasonable tag names to avoid conflicts with other plugins. For example don't use a name like test but pluginname_test instead. Maybe check existing search patterns here syntax, but don't use them as example because many of them are incorrect regular expressions.

Correct regular expression

<tag\b.*?>.*?</tag>

<tag\b[^>\r\n]*?>.*?</tag>
<tag\b[^>\r\n]*?>[^\r\n]*?</tag>

<tag\b.*?>(?:.*?</tag>)

<tag\b(?:\s+(?:par1|par2)="[^">\r\n]*")*\s*>(?:.*?</tag>)
~~tag\b.*?~~
~~tag>.+?~~
~~tag>[^\r\n]+?~~

False regular expression

Example 1:

<tag.*>.*?</tag>
<tag ?.*>.*?</tag>
<tag *.*>.*?</tag>

Start tag is not a word, the end-of-word marker \b is missing, any pattern for example tagmore or taged is found too.

This produces a wrong result in this case:

<tagmore>
Text
</tagmore>

<tag>
Text
</tag>

Example 2:

<tag\b.*>.*?</tag>
<tag\b.*?>.*</tag>

The search pattern is “greedy”, the not-greedy marker ? is missing, to long pieces are included in one search match.

This produces a wrong result in this case:

<tag>
Text
</tag>

<tag>
Text
</tag>

Spam prevention

When you offer a form in your plugin and this can be also used by public users of a wiki, it's recommended to use CAPTCHA to defeat spambots. There is already a CAPTCHA plugin available, which provides different formats, visible and invisible CAPTCHAs.

See the plugin page for description of the integration and the configuration options.

Example implementation

The Bureaucracy plugin supports this plugin. The CAPTCHA is integrated in the submit button, see submit.php.

Adding JavaScript

Modify your wiki via local JavaScripts

If you need to enhance DokuWiki's capabilities, you can consider JavaScript beside creating a new plugin.
Just put the JavaScript code into conf/userscript.js (create this file if it doesn't exists).

Examples JavaScripts additions: wordcounter or copy_section_link

Distribute JavaScript and CSS files by pseudo plugins

If you want to add some JavaScript and CSS at the same time and make it easier to distribute, you can create a 'pseudo' plugin. Create a new folder and add a script.js and/or a style.css file to it. Add this folder to lib/plugins/ and complete it by adding the plugin.info.txt.

Examples pseudo plugins: searchjump or ipa

More about JavaScript and CSS styles in plugins.

Using cookies

When you like to store some preferences, you can add to and retrieve of DokuWikis preferences cookie by:

For other usages you use a separated cookie. Next snippet shows how you set cookies with the correct path in DokuWiki.

In PHP:

global $conf;
$cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
setCookie("yourCookieName", $value, $expire, $cookieDir, '', ($conf['securecookie'] && is_ssl()));

and in javascript:

jQuery.cookie("yourCookieName", value, { 
    expires: 7, //days
    path: DOKU_COOKIE_PARAM.path,
    secure: DOKU_COOKIE_PARAM.secure 
});

More general info about cookies: DokuWiki's cookies, cookiedir config, securecookie config.

Handle JSON ajax request

An action plugin that register the AJAX_CALL_UNKNOWN event, you can handle your own ajax requests. Here a sample how you can return JSON to your javascript. Plugin name is example.

Create an Action Plugin which should contain:

lib/plugin/example/action.php
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\EventHandler;
use dokuwiki\Extension\Event;
 
class action_plugin_example extends ActionPlugin {
 
    /**
     * plugin should use this method to register its handlers 
     * with the dokuwiki's event controller
     */
    public function register(EventHandler $controller) {
        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajaxCall');
    }
 
    /**
     * handle ajax requests
     */
    public function ajaxCall(Event $event) {
        if ($event->data !== 'plugin_example') {
            return;
        }
        //no other ajax call handlers needed
        $event->stopPropagation();
        $event->preventDefault();
 
        //e.g. access additional request variables
        global $INPUT;
        $name = $INPUT->str('name');
 
        //data
        $data = [];
 
        //set content type
        header('Content-Type: application/json');
        echo json_encode($data);
    }
}

You can request the data in javascript, for example:

lib/plugins/example/script.js
jQuery.post(
    DOKU_BASE + 'lib/exe/ajax.php',
    {
        call: 'plugin_example', 
        name: 'local'
    },
    function(data) {
        alert('Received response');
        // data is array you returned with action.php
    },
    'json'
);

Checking user-input text against the DokuWiki blacklist

You have some user input you want to validate using the internal blacklist (e.g. as an anti-spam measure for comments or anything like that).

Make a temporary backup of the current content of the internal $TEXT variable (which usually contains the wiki text of the displayed page), then put your text into $TEXT. A call to checkwordblock() should then yield a result of true if the processed text contains any blocked words, and false otherwise. Don't forget to restore the original content of $TEXT after you are done checking your user input by copying it back from the backup you made.

Example:

// backup and reset the current $TEXT variable
$backupText = $TEXT;
$TEXT = $userinput;
 
if (checkwordblock()) {
    // text contains blocked words ...
}
 
// restore $TEXT
$TEXT = $backupText;

Disabling syntax plugins in user comments

You have written a syntax plugin which will be used in a restricted environment where anonymous users are not allowed to edit pages but allowed to comment using the discussion plugin. You do not want to disable wiki syntax in comments completely, but want to disable the syntax provided by your plugin as it can become a risk in the wrong hands.

In your plugin's handle method, check whether $_REQUEST['comment'] is set – this indicates that the parsing process is working on a discussion comment.

Example:

global $INPUT;
 
// we are parsing a submitted comment...
if ($INPUT->has('comment')) {
    return false;
}

Sending popularity data

Since release 2015-08-10 “Detritus”

:!: As a plugin developer, beware: since popularity data is public, you must not send sensitive information with this feature.

The popularity plugin already gather the number of time a plugin is installed on an instance of Dokuwiki.

It also let the possibility to developers, to send more data about usage. It can be used by plugins developers to know if a given obsolete feature is still used. To do it, you need to subscribe to the PLUGIN_POPULARITY_DATA_SETUP event. This event contains a key-value array. You should add a key which is the name of your plugin. The value should be either a string, or a key-value array itself (in this latter case, the data will be sent with the key <plugin-name>_<key>)

Example:

action.php
use dokuwiki\Extension\ActionPlugin;
use dokuwiki\Extension\EventHandler;
use dokuwiki\Extension\Event;
 
class action_plugin_example extends ActionPlugin {
    public function register(EventHandler $controller) {
        $controller->register_hook('PLUGIN_POPULARITY_DATA_SETUP', 'AFTER', $this, 'usageData');
    }
 
    public function usageData(Event $event){
        $event->data['my_plugin_name'] = 'my usage data';
 
    /* or: 
        $event->data['my_plugin_name'] = [
            'k1' => 'v1', 
            'k2' => 'v2'
        ];
    */
    }
}

A plugin which uses this feature is the nspages Plugin.

devel/plugin_programming_tips.txt · Last modified: 2023-09-20 23:34 by Klap-in

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki