Plugin Development

# Overview

SITS CMS provides a flexible plugin system that allows developers to extend core functionality. This guide explains how to create and integrate custom plugins.

# Plugin Directory Structure

Create your plugin with the following structure in the plugins/ directory:


    your-plugin-name/
    ├── database/
    │   └── migrations
    |       └── create_your_plugin_name_table.php
    ├── helpers/
    │   └── helpers.php
    ├── resources/
    │   └── views/
    |       ├── details.blade.php
    |       ├── index.blade.php
    |       ├── single.blade.php
    |       └── table-details.blade.php
    ├── routes/
    │   └── web.php
    ├── src/
    │   ├── Http/
    |   |   └── Controllers/
    |   |       └── YourPluginController.php
    │   ├── Models/
    |   |   └── YourPluginModel.php
    │   └── Providers/
    |       └── YourPluginServiceProvider.php
    └── plugin.json
    

Key components of a plugin include:

  • database/ - Database migrations
  • resources/ - Views
  • routes/ - Route definitions
  • src/ - Core plugin code including controllers, models, and service providers
  • plugin.json - Plugin metadata and dependencies

Each plugin must have a plugin.json file that provides essential information for auto-loading and plugin management. This file includes details like the plugin name, description, author, and version.

Plugins are registered to the system's autoloader automatically when installed. The service provider in each plugin bootstraps the plugin's components and registers its services with the application container.

# Step-by-Step Plugin Creation

  1. Create plugin.json
    
        {
            "id": "YourVendor/your plugin name",
            "name": "Your Plugin Name",
            "namespace": "YourVendor\\YourPlugin",
            "provider": "YourVendor\\YourPlugin\\Providers\\YourPluginServiceProvider",
            "author": "Your Name",
            "url": "https://yourdomain.com",
            "version": "1.0",
            "description": "Description of your plugin",
            "require":{
                "plugins" :[
                    {
                        "id":"YourVendor/plugin name",
                        "name" : "Plugin Name",
                        "version":"1.0"
                    }
                ]
            }
        }
        

    The key fields in plugin.json are:

    • id - Unique identifier for the plugin
    • name - Display name of the plugin
    • namespace - PHP namespace used by the plugin
    • provider - Service provider class that bootstraps the plugin
    • author - Plugin author/organization name
    • version - Plugin version number
    • description - Brief description of plugin functionality
    • requireThis is optional. Use it only if your plugin depends on another plugin or package that needs to be activated or installed. Otherwise, you can leave it out.

    Example: blog/plugin.json

    Image
  2. Create Service Provider

    Create YourPluginServiceProvider.php in the src directory:

    The service provider is the main bootstrapping class for your plugin. It handles tasks like:

    • Loading routes, views, migrations and other resources
    • Publishing assets and configuration files
    • Registering services with the application container
    • Setting up event listeners and hooks

    Example: blog/src/Providers/BlogServiceProvider.php

    
        namespace Sits\Blog\Providers;
    
        use Illuminate\Support\ServiceProvider;
        use Illuminate\Routing\Events\RouteMatched;
        use Illuminate\Support\Facades\Event;
        use Sits\Base\Traits\LoadAndPublish;
    
        class BlogServiceProvider extends ServiceProvider
        {
            use LoadAndPublish;
    
            public function boot()
            {
                $this->loadRoutes()
                    ->loadViews('sits/blog')
                    ->loadMigrations()
                    ->publishMigrations('sits-blog-migrations');
    
                # Register Dashboard Menu 
                Event::listen(RouteMatched::class, function () {
                    dashboard_menu()->registerMenu([
                        'id'    => 'blogs',
                        'title' => "Blogs",
                        "icon"  => "fa fa-blog",
                        "type"  => "plugin",
                        "url"  => '#',
                        "route_name" => "admin.blogs",
                        "sub_menus" => [
                            (object) [
                                'id'    => 'blog-categories',
                                'title' => "Category",
                                "icon"  => "fa fa-blog",
                                "route_name"  => "admin.blogs.categories",
                                "url"   => url('admin/blogs/categories')
                            ],
                            (object) [
                                'id'    => 'blogs',
                                'title' => "Posts",
                                "icon"  => "fa fa-hammer",
                                "route_name"  => "admin.blogs.posts",
                                "url"   => url('admin/blogs/posts')
                            ],
                            (object) [
                                'id'    => 'blog-tags',
                                'title' => "Tags",
                                "icon"  => "fa fa-tags",
                                "route_name"  => "admin.blogs.tags",
                                "url"   => url('admin/blogs/tags'),
                            ],
                        ],
                    ]);
                });
            }
        }
        
  3. Add Routes

    Define your plugin routes in routes/web.php:

    Example: blog/routes/web.php

    
        Route::middleware(['web', 'auth:admin'])->prefix('admin/blogs/posts')->name('admin.blogs.posts.')->group(function () {
            Route::resource('/', BlogController::class)->parameters(['' => 'blog']);
            Route::post('bulk_delete', [BlogController::class, 'bulk_delete'])->name('bulk_delete');
            Route::post('bulk_restore', [BlogController::class, 'bulk_restore'])->name('bulk_restore');
            Route::post('bulk_permanent_delete', [BlogController::class, 'bulk_permanent_delete'])->name('bulk_permanent_delete');
            Route::post('restore/{id}', [BlogController::class, 'restore'])->name('restore');
            Route::delete('permanent-delete/{id}', [BlogController::class, 'permanent_delete'])->name('permanent_delete');
        });
        
  4. Required Resource Files

    The resources directory of the plugin must contain the following Blade view templates: details.blade.php and table-details.blade.php. These templates are utilized for rendering the detailed view.

# Plugin Activation

Follow the steps below to activate a plugin and make it available in the system:

  1. Open the Admin Panel and navigate to Plugins.
  2. Search for the plugin by its name using the search field.
  3. Click the Activate button next to the plugin.

Once the plugin is successfully activated, its status will change to Activated, as shown in the example below.

Example: blog plugin activated

Image

# About Folders and Files

# database/

The database folder contains the migrations for the plugin.


    sits/plugins/example-plugin/database/migrations/
        └── 2023_01_01_000000_create_example_plugin_table.php
    

To create a new migration file for your plugin table, run the following Artisan command:


    php artisan make:migration create_example_plugin_table --path=sits/plugins/example-plugin/database/migrations
    

After creating the migration file inside your plugin's database/migrations directory, define your table structure as shown below.

Here's an example of a typical migration file that creates a example-plugin table with various columns:


    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;

    return new class extends Migration
    {
        public function up()
        {
            Schema::create('example_plugin', function (Blueprint $table) {
                $table->id();                                    // Auto-incrementing ID
                $table->string('title');                         // Example plugin title
                $table->string('slug')->unique();                // URL-friendly slug
                $table->text('description')->nullable();         // Main content
                $table->string('short_description', 500)->nullable(); // Brief excerpt
                $table->string('featured_image')->nullable();    // Featured image path
                $table->string('banner_image')->nullable();      // Banner image path
                $table->date('post_date')->nullable();          // Publication date
                $table->string('author')->nullable();           // Author name
                $table->boolean('status')->default(1);          // Publication status
                $table->integer('created_by')->nullable();      // User who created
                $table->integer('updated_by')->nullable();      // User who updated
                $table->softDeletes();                         // Soft delete support
                $table->timestamps();                          // Created/Updated timestamps
            });
        }

        public function down()
        {
            Schema::dropIfExists('example_plugin');
        }
    };
    

Once your migration is ready, run the following command to apply (migrate) it to the database:


    php artisan migrate --path=sits/plugins/example-plugin/database/migrations
    

This migration demonstrates common column types and modifiers used in Laravel migrations:

  • id() - Creates an auto-incrementing primary key
  • string() - Creates VARCHAR columns with optional length
  • text() - Creates TEXT columns for longer content
  • boolean() - Creates BOOLEAN columns with defaults
  • integer() - Creates INT columns for numeric values
  • timestamps() - Adds created_at and updated_at columns
  • softDeletes() - Adds deleted_at column for soft deletes

# Helpers/

The helpers folder contains helper functions and utility methods used across the plugin.

Here's an example of a typical helper file:


    sits/plugins/example-plugin/helpers/example-plugin.php
    
sits/plugins/example-plugin/helpers/example-plugin.php

    if (!function_exists('example_plugin_setting')) {
        /**
        * Get example plugin setting value
        * @param string $key
        * @param mixed $default
        * @return mixed
        */
        function example_plugin_setting($key, $default = null) {
            return setting('example_plugin_' . $key, $default);
        }
    }

    if (!function_exists('get_example_plugins')) {
        /**
        * Get list of example plugins
        * @param array $params
        * @return Collection
        */
        function get_example_plugins($params = []) {
            return app(ExamplepluginInterface::class)->getAll($params);
        }
    }

    if (!function_exists('get_example_plugin_by_id')) {
        /**
        * Get example plugin by ID
        * @param int $id
        * @return Exampleplugin|null
        */
        function get_example_plugin_by_id($id) {
            return app(ExamplepluginInterface::class)->findById($id);
        }
    }

    if (!function_exists('format_example_date')) {
        /**
        * Format date for example plugin
        * @param string $date
        * @param string $format
        * @return string
        */
        function format_example_date($date, $format = 'Y-m-d') {
            return date($format, strtotime($date));
        }
    }
    
  • The helper functions are automatically loaded and available as soon as you activate the plugin - no extra configuration needed!

# Resources/

The resources folder contains the views, assets and other frontend resources for the plugin.

Here's an example of a typical resource file:


    sits/plugins/example-plugin/resources/views/example-plugin/index.blade.php
    

Here's an example of a typical index.blade.php view file:


    @extends('sits/base::master')

    @section('table-filter-view')
        <div class="grid grid-cols-2 md:grid-cols-5 lg:grid-cols-5 gap-4">
            {{-- Status Filter --}}
            <div class="form-group">
                <label for="status" class="form-label">Status</label>
                <select name="status" id="status" class="form-control filterTable">
                    <option value="all" selected>All</option>
                    <option value="publish">{{ getStatus(1) }}</option>
                    <option value="draft">{{ getStatus(0) }}</option>
                </select>
            </div>

            {{-- Date Range Filter --}}
            <div class="form-group">
                <div>
                    <label for="created_at_date_range" class="form-label">Date Range</label>
                    <input class="form-control py-2 flatpickr filterTable" 
                        name="created_at_date_range"
                        id="created_at_date_range" 
                        data-mode="range" 
                        type="text" 
                        placeholder="Select date range">
                </div>
            </div>

            {{-- Apply Button --}}
            <div class="form-group content-end w-6/12">
                <div class="datatable-filter-wrapper">
                    <button type="button" class="form-control bg-blue-600 text-white filter-apply-btn">
                        Apply
                    </button>
                </div>
            </div>
        </div>
    @endsection

    @section('content')
        <div class="content-wrapper transition-all duration-150" id="content_wrapper">
            <div class="container-fluid" id="page_layout">
                <div id="content_layout">
                    <div class="space-y-5">
                        @include('sits/base::components.list-section', [
                            'tableId' => 'example-table',
                            'tableHeaders' => ['#', 'Title', 'Status', 'Created At', 'Action'],
                            'isExport' => true,
                            'isFilter' => true,
                            'isRecordTypeFilter' => true,
                            'isColumnVisibility' => true,
                        ])
                    </div>
                </div>
            </div>
        </div>
    @endsection

    @push('scripts')
        <script>
            let columns = [
                {
                    data: 'DT_RowIndex',
                    name: 'DT_RowIndex',
                    orderable: false,
                    searchable: false
                },
                {
                    data: 'title',
                    name: 'title',
                    searchable: true,
                    orderable: true
                },
                {
                    data: 'status_label', 
                    name: 'status',
                    orderable: false,
                    searchable: false,
                    statusType: 'post'
                },
                {
                    data: 'created_at',
                    name: 'created_at',
                    orderable: true,
                    searchable: false
                },
                {
                    data: 'action',
                    name: 'action',
                    orderable: false,
                    searchable: false
                }
            ];

            SITS_TABLE({
                id: '#example-table',
                columns: columns
            });
        </script>
    @endpush
    

This example shows a typical index view that:

  • Extends the base master layout
  • Includes filter sections for status and date ranges
  • Uses the SITS table component for displaying data
  • Configures DataTables columns and settings

# Controllers

Controllers define the logic for handling requests and responses for your plugin. They are located in the src/Http/Controllers directory.

Example:


    sits/plugins/example-plugin/src/Http/Controllers/ExamplepluginController.php
    

Here's an example of a typical controller for a example_plugin plugin:


    namespace Sits\Exampleplugin\Http\Controllers;

    use Sits\Exampleplugin\Models\Exampleplugin;
    use Illuminate\Routing\Controller;
    use Sits\ACL\Models\Administrator;
    use Sits\Base\Traits\HasSitsDeleteAndRestore;
    use Sits\Exampleplugin\Models\Exampleplugin;
    use Yajra\DataTables\DataTables;

    class ExamplepluginController extends Controller
    {
        use HasSitsDeleteAndRestore;

        protected $page_title = "Exampleplugin";
        protected $route_name = "admin.example_plugins.";
        protected $view = "sits/example_plugin::example_plugins";
        protected $table_name = "example_plugins";

        public function __construct()
        {
            $shared_data = (object) [
                'page_title' => $this->page_title,
                'route_name' => $this->route_name,
                'view' => $this->view,
                'table_name' => $this->table_name,
                'file_paths' => $this->eloquentModel()->file_paths,
            ];

            view()->share('shared_data', $shared_data);
        }

        public function eloquentModel()
        {
            return new Modelname();
        }

        public function index()
        {
            if (request()->ajax()) {
                $data = $this->eloquentModel()->with('category:id,name,deleted_at')
                    ->select('id', 'title', 'slug', 'category_id', 'status', 'created_at')
                    ->filterBy($value);
                
                return DataTables::of($data)
                    ->addIndexColumn()
                    ->addColumn('action', fn($row) => getActionButtons($this->route_name, $row))
                    ->make(true);
            }

            $example_plugin = Exampleplugin::select('id', 'name')->withCount('example_plugins')->get();
            return view("{$this->view}.index", compact('example_plugin'));
        }

        public function validation($id = 0)
        {
            request()->validate([
                'title' => "required|min:3|max:255|unique:{$this->table_name},title",
                'description' => "required",
                'status' => "required|in:0,1",
            ]);
        }

        public function store()
        {
            $this->validation();

            $data = request()->only($this->eloquentModel()->fillable);

            $result = $this->eloquentModel()->create($data);

            return redirect()->route("{$this->route_name}index")
                ->with('success', 'Record created successfully');
        }
    }
    

This controller demonstrates common patterns used in SITS CMS plugins:

  • Uses traits for common functionality like soft deletes
  • Shares common data with all views via constructor
  • Implements DataTables for AJAX-powered listings
  • Includes standard CRUD operations with validation
  • Follows RESTful controller conventions

# Models

Models define the data structure and business logic for your plugin. They are located in the src/Models directory.

Example:


    sits/plugins/example-plugin/src/Models/Exampleplugin.php
    

Here's an example of a typical model class for a example_plugin plugin:


    namespace Sits\Exampleplugin\Models;

    use Illuminate\Database\Eloquent\Model;
    use Illuminate\Database\Eloquent\SoftDeletes;

    class Exampleplugin extends Model
    {
        use SoftDeletes, CreatedByUpdatedBy;

        protected $table = "example_plugins";

        protected $file_paths = [
            'profile_path' => 'example_plugins/example'
        ];

        protected $fillable = [
            'title',
            'slug', 
            'description',
            'featured_image',
            'status',
            'category_id'
        ];
    }
    

Key aspects of plugin models include:

  • Use of traits for common functionality like soft deletes
  • Clear definition of fillable fields
  • Relationship methods to other models
  • Helper methods for accessing and formatting data
  • Custom accessors and mutators as needed
Note: Models should follow Laravel conventions and use traits/interfaces provided by the SITS CMS core when appropriate.

# Development Guidelines

  • Use proper namespacing to avoid conflicts with other plugins
  • Follow Laravel's coding standards and conventions
  • Implement proper validation and error handling
  • Create comprehensive documentation for your plugin
  • Test your plugin thoroughly before deployment
  • Keep plugin dependencies minimal and documented

On this Page

On this Page