Plugin Development

Extend Scriptlog's functionality with custom plugins using the hook-based architecture.

Home / Documentation / Plugin Development

Overview

Scriptlog plugin system uses a hook-based architecture. Plugins register hooks using the clip() function, and those hooks are executed at specific points in the application.

Plugin Lifecycle

Understanding the plugin lifecycle helps you build plugins that integrate seamlessly with scriptLog.

1
Installation
ZIP Upload

Plugin is uploaded and installed through the admin panel.

PluginController::installPlugin()
  • Validates ZIP structure
  • Extracts to admin/plugins/
  • Saves info to tbl_plugin
  • Status: Disabled
2
Activation
Enable Plugin

Plugin is activated and ready to use.

PluginController::enablePlugin($id)
  • Executes schema.sql if exists
  • Loads plugin PHP class
  • Registers hooks via clip()
  • Sets status = 'Y'
3
Deactivation
Disable Plugin

Plugin is deactivated but files remain.

PluginController::disablePlugin($id)
  • Hooks are unregistered
  • Files remain on disk
  • Database record preserved
  • Sets status = 'N'
4
Deletion
Remove Plugin

Plugin is completely removed from the system.

PluginController::remove($id)
  • Deletes plugin directory
  • Removes database record
  • Cleanup complete

Quick Reference

Activation Methods
  • activate() - Called on activation
  • deactivate() - Called on deactivation
  • uninstall() - Called before deletion
Database Status
  • plugin_status = 'Y' - Enabled
  • plugin_status = 'N' - Disabled
  • Record in tbl_plugin

Creating a Plugin

Required Directory Structure

TEXT directory structure
admin/plugins/[plugin-name]/
├── plugin.ini           # REQUIRED - Plugin configuration
├── YourClassFile.php    # REQUIRED - Main plugin class
├── functions.php        # OPTIONAL - Helper functions
└── schema.sql          # OPTIONAL - Database schema

Step 1: Create plugin.ini

INI plugin.ini
[INFO]
plugin_name = "My Plugin Name"
plugin_description = "Description of what the plugin does"
plugin_level = "administrator"
plugin_version = "1.0.0"
plugin_author = "Your Name"
plugin_loader = "MyPlugin"
plugin_action = "my-plugin"
Field Required Description
plugin_nameYesDisplay name
plugin_descriptionYesWhat the plugin does
plugin_levelYes"administrator" or "manager"
plugin_versionYesSemantic version (e.g., 1.0.0)
plugin_authorYesAuthor name
plugin_loaderYesPHP class filename (without .php)
plugin_actionYesAction identifier for routing

Step 2: Create Main Plugin Class

PHP YourClassFile.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");

class MyPlugin
{
    private $pluginDir;
    
    public function __construct()
    {
        $this->pluginDir = dirname(__FILE__);
        $this->registerHooks();
    }
    
    private function registerHooks()
    {
        clip('clip_my_plugin', null, function($content = '') {
            return $this->frontendContent($content);
        });
        
        clip('clip_my_plugin_admin', null, function() {
            return $this->adminPage();
        });
    }
    
    public function activate()
    {
        // Runs on activation - create tables, set options
        return true;
    }
    
    public function deactivate()
    {
        // Runs on deactivation - cleanup temporary data
        return true;
    }
    
    public function uninstall()
    {
        // Runs on deletion - remove all plugin data
        return true;
    }
    
    public function adminPage()
    {
        return '<div class="box">
            <div class="box-header"><h3>My Plugin</h3></div>
            <div class="box-body">
                <p>Plugin is active!</p>
            </div>
        </div>';
    }
    
    public function frontendContent($content = '')
    {
        return $content . '<div class="my-plugin">Hello from my plugin!</div>';
    }
}

Step 3: Create Helper Functions (Optional)

Helper functions provide a cleaner API for your plugin:

PHP functions.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");

function my_plugin_instance()
{
    static $instance = null;
    if (null === $instance) {
        $instance = new MyPlugin();
    }
    return $instance;
}

function my_plugin_display($content = '')
{
    return my_plugin_instance()->frontendContent($content);
}

Step 4: Create Database Schema (Optional)

If your plugin needs database tables, create schema.sql:

SQL schema.sql
-- Create tables for your plugin
CREATE TABLE IF NOT EXISTS tbl_my_plugin (
    ID BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Add DROP statement for uninstall (commented by default)
-- DROP TABLE IF EXISTS tbl_my_plugin;

Hook System Reference

The clip() Function

PHP usage
clip('hook_name', $value, $callback);

Usage:

PHP example
// Register a hook (attach callback)
clip('my_hook', null, function($value) {
    return $value . ' modified';
});

// Execute all callbacks attached to hook
$result = clip('my_hook', 'original');

Available Hooks

Frontend Hooks

Hook Name Description Value Passed
clip_[plugin_name]Main plugin hookContent string
clip_contentContent filterPost content
clip_footerFooter section-
clip_headerHeader section-

Admin Hooks

Hook Name Description
clip_[plugin_name]_adminAdmin page content
clip_plugin_menuPlugin navigation

Invoking Plugins

From Code

PHP usage
// Check if plugin is enabled
if (is_plugin_enabled('my-plugin')) {
    $content = invoke_plugin('my-plugin', $content);
}

Available Plugin Functions

PHP functions
// Check if plugin exists in database
is_plugin_exist('plugin-name');

// Check if plugin is enabled
is_plugin_enabled('plugin-name');

// Execute plugin hook
invoke_plugin('plugin-name', $args);

// Set plugin navigation in admin sidebar
set_plugin_navigation('plugin-name');

Common Issues

Issue Solution
Plugin not appearing in listCheck plugin.ini syntax
Plugin loads but no outputEnsure hooks are registered in constructor
Activation failsCheck schema.sql syntax
Hook not firingVerify clip() call exists and plugin is enabled

Best Practices

Follow these guidelines for secure and maintainable plugins:

Security First

Always check defined('SCRIPTLOG') at the top of PHP files to prevent direct access.

Unique Hook Names

Always use unique hook names with your plugin prefix (e.g., clip_myplugin_) to avoid conflicts.

Input Sanitization

Always sanitize user input in your plugin using built-in sanitization functions.

Use Hooks

Avoid modifying core files - use hooks (clip()) instead to extend functionality.

Documentation

Document your plugin's hooks, functions, and configuration options for future reference.

Version Control

Version your plugin using semantic versioning (e.g., 1.0.0) and test on development first.

Example: Complete Social Share Plugin

This example demonstrates a complete plugin implementation with all components.

Directory Structure

TEXT directory structure
admin/plugins/social-share/
├── plugin.ini
├── SocialSharePlugin.php
└── functions.php

plugin.ini

INI plugin.ini
[INFO]
plugin_name = "Social Share"
plugin_description = "Add social sharing buttons to posts"
plugin_level = "administrator"
plugin_version = "1.0.0"
plugin_author = "Developer Name"
plugin_loader = "SocialSharePlugin"
plugin_action = "social-share"

SocialSharePlugin.php

PHP SocialSharePlugin.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");

class SocialSharePlugin
{
    private $pluginDir;
    
    public function __construct()
    {
        $this->pluginDir = dirname(__FILE__);
        $this->registerHooks();
    }
    
    private function registerHooks()
    {
        clip('clip_social_share', null, function($content = '') {
            return $this->addSocialButtons($content);
        });
        
        clip('clip_social_share_admin', null, function() {
            return $this->adminPage();
        });
    }
    
    public function activate()
    {
        return true;
    }
    
    public function deactivate()
    {
        return true;
    }
    
    public function addSocialButtons($content = '')
    {
        $buttons = '<div class="social-share" style="margin-top:20px; padding:15px; border-top:1px solid #eee;">
            <h4>Share this post</h4>
            <a href="#" class="btn btn-primary">Facebook</a>
            <a href="#" class="btn btn-info">Twitter</a>
            <a href="#" class="btn btn-danger">Google+</a>
        </div>';
        
        return $content . $buttons;
    }
    
    public function adminPage()
    {
        return '<div class="box box-primary">
            <div class="box-header"><h3>Social Share Settings</h3></div>
            <div class="box-body">
                <p>Configure your social share buttons here.</p>
            </div>
        </div>';
    }
}

functions.php

PHP functions.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");

function social_share_plugin()
{
    static $instance = null;
    if (null === $instance) {
        $instance = new SocialSharePlugin();
    }
    return $instance;
}

function social_share_buttons($content = '')
{
    return social_share_plugin()->addSocialButtons($content);
}

Testing Your Plugin

Follow this checklist to ensure your plugin works correctly through its entire lifecycle.

Manual Testing Checklist

Install: Upload ZIP file - plugin should appear in list (disabled)
Activate: Click Enable - status changes to "Enabled"
Test functionality: Check frontend output and admin page
Deactivate: Click Disable - status changes to "Disabled"
Reactivate: Click Enable again - should work without errors
Delete: Click Delete - plugin removed from list and directory

Debug Tips

Add debugging to your plugin during development:

PHP debug-example
public function __construct()
{
    $this->pluginDir = dirname(__FILE__);
    
    // Debug: Log plugin initialization
    error_log('MyPlugin: Initialized at ' . date('Y-m-d H:i:s'));
    
    $this->registerHooks();
}
Tip: Check your server's error log for debug messages. Remove or comment out debug code before releasing your plugin.