Creating a Plugin

Plugins allow you to extend Traq's functionality by hooking into various parts of the system. This guide will walk you through creating your first plugin using the Markdown plugin as an example.

Plugin Structure

All plugins must extend the \traq\libraries\Plugin base class and implement the required methods. Here's the basic structure:

namespace traq\plugins;

use \FishHook;
use \ParsedownExtra;

class Markdown extends \traq\libraries\Plugin
{
    protected static $parser;

    protected static $info = array(
        'name'    => 'Markdown',
        'version' => '3.0.0',
        'author'  => 'Jack P.'
    );

    /**
     * Handles the startup of the plugin.
     */
    public static function init()
    {
        FishHook::add('function:format_text', array(get_called_class(), 'format_text'));
    }

    /**
     * Handles the format_text function hook.
     */
    public static function format_text(&$text, $strip_html)
    {
        // If HTML is being converted to text, undo it.
        if ($strip_html) {
            $text = htmlspecialchars_decode($text);
        }

        // Initialise parser
        if (!isset(static::$parser)) {
            static::$parser = new ParsedownExtra();
            static::$parser->setSafeMode(true);
        }

        // Parse the text
        $text = static::$parser->text($text);
    }
}

Required Components

Plugin Information

Every plugin must define a $info static property containing metadata about the plugin:

protected static $info = array(
    'name'    => 'Markdown',      // Plugin name
    'version' => '3.0.0',         // Plugin version
    'author'  => 'Jack P.'        // Plugin author
);

Fields: - name (required): The display name of your plugin - version (required): The version number of your plugin (e.g., "1.0.0", "2.1.3") - author (required): The name of the plugin author

Initialization Method

The init() method is called when the plugin is loaded. This is where you register hooks and perform any setup tasks:

public static function init()
{
    // Register hooks here
    FishHook::add('function:format_text', array(get_called_class(), 'format_text'));
}

Key Points: - Must be a public static method - Use FishHook::add() to register hooks - The first parameter is the hook name (see Plugin Hooks for available hooks) - The second parameter is a callable (typically an array with the class and method name) - Use get_called_class() to reference the current class for better inheritance support

Hook Handlers

Hook handlers are methods that respond to specific events in the Traq system. The method signature depends on the hook type you're using.

Function Hooks

Function hooks allow you to modify data before it's processed. Parameters are passed by reference, allowing you to modify them:

public static function format_text(&$text, $strip_html)
{
    // Modify $text (passed by reference)
    $text = static::$parser->text($text);
}

Important: - Parameters passed by reference (&$param) can be modified - Changes to referenced parameters affect the original data - Check the Plugin Hooks documentation for specific parameter requirements

Template Hooks

Template hooks allow you to inject HTML into specific template locations:

public static function my_template_hook($project = false)
{
    echo '<div class="my-plugin-content">Custom content here</div>';
}

Important: - Template hooks output HTML directly using echo or print - Parameters are optional and depend on the specific hook - Always escape user-generated content using htmlspecialchars()

Controller Hooks

Controller hooks allow you to intercept or modify controller actions:

public static function my_controller_hook(&$user)
{
    // Modify $user before validation
    $user->set('custom_field', 'value');
}

Important: - Parameters are passed by reference - Modifications affect the data before it's processed by the core code - Be careful not to break expected data structures

Example: Markdown Plugin

The Markdown plugin demonstrates a complete plugin implementation:

  1. Plugin Info: Defines the plugin metadata
  2. Parser Property: Stores the parser instance to avoid re-initialization
  3. Init Method: Registers the format_text hook
  4. Hook Handler: Processes text through the Markdown parser

Key Features: - Uses a static property to cache the parser instance - Handles HTML decoding when needed - Enables safe mode for security - Modifies the text by reference

Best Practices

Code Organisation

  • Keep your plugin class in a single file named after your plugin (e.g., markdown.php)
  • Use proper namespacing if needed
  • Follow PSR coding standards

Error Handling

  • Always validate input data
  • Handle exceptions gracefully
  • Don't let plugin errors break the entire system

Security

  • Escape output when injecting HTML
  • Validate and sanitize user input
  • Use safe modes for parsers when available
  • Be cautious when modifying data passed by reference

Performance

  • Cache expensive operations (like parser initialization)
  • Use static properties for shared resources
  • Avoid unnecessary database queries

Documentation

  • Document your plugin's purpose and functionality
  • Include usage instructions
  • List any dependencies or requirements
  • Provide examples of how to use your plugin

Next Steps

  • Review the Plugin Hooks documentation to see all available hooks
  • Test your plugin thoroughly before deploying
  • Consider versioning your plugin for easier updates
  • Share your plugin with the community!

Notes

  • Plugin Location: Plugins are typically placed in the vendor/traq/plugins/ directory
  • Activation: Plugins must be activated in the admin panel before they're loaded
  • Hook Execution: Multiple plugins can register hooks for the same event; they execute in registration order
  • Static Methods: All plugin methods should be static, as plugins are not instantiated
  • Class Naming: Plugin class names should be unique to avoid conflicts