TP
Taskify Plugin Dev Kit
Build extendable modules

Extend Your Taskify
Superpowers with Plugins

A complete guide to building, registering, packaging, and distributing extensions for our ecosystem.

Plugin Architecture

Each plugin contains its own routes, views, migrations, permissions, settings, menu placements, and custom logic. Easily extend your SaaS.

TimeTracker/
├── Controllers/
│   ├── TimeTrackerController.php
│   ├── DashboardController.php
│   └── ScreenShotController.php
├── Models/
│   ├── TimeTrack.php
│   ├── Screenshot.php
│   └── TimeTrackerConfig.php
├── Database/
│   └── Migrations/
├── Providers/
│   └── TimeTrackerServiceProvider.php
├── routes/
│   ├── web.php
│   └── api.php
├── Resources/
│   ├── views/
│   └── lang/
│       └── plugin_labels.php
├── public/
│   ├── js/
│   └── css/
├── menus.php
└── plugin.json

Plugin Structure Overview

plugin.json

Defines metadata like name, version, author, and permissions. Required fields: name, slug, description, version, enabled, provider.

{
  "name": "Taskify - Time Tracker",
  "slug": "time-tracker",
  "description": "Tracks user time and screenshots for productivity analysis.",
  "version": "1.0.0",
  "enabled": true,
  "provider": "Plugins\\TimeTracker\\Providers\\TimeTrackerServiceProvider",
  "publish_tag": "timetracker-assets"
}

Folder Structure

Organized directory structure with Controllers, Models, Views, Routes, Migrations, and Assets.

TimeTracker/
├── Controllers/
├── Models/
├── Database/
│   └── Migrations/
├── Providers/
│   └── TimeTrackerServiceProvider.php
├── routes/
│   ├── web.php
│   └── api.php
├── Resources/
│   ├── views/
│   └── lang/
├── public/
│   ├── js/
│   └── css/
├── menus.php
└── plugin.json

Hooks & Events

Plugins register menu items, routes, and system hooks through the Service Provider's boot() method.

$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker');
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker');
$this->publishes([...], ['timetracker-assets', 'public']);

Installation Process

1

Create New Plugin Folder

Create a new folder in the plugins/ directory with your plugin name in PascalCase (e.g., MyPlugin).

2

Add plugin.json

Create a plugin.json file with required metadata: name, slug, description, version, enabled, and provider class.

3

Register Routes & Menus

Create your Service Provider and register routes, views, migrations, and menus in the boot() method.

4

Activate Plugin in Dashboard

Set "enabled": true in your plugin.json file. The system will automatically discover and load your plugin.

5

Test Using Sandbox Mode

Run php artisan migrate to apply database migrations, then test your plugin functionality.

Code Examples

PluginServiceProvider.php


namespace Plugins\TimeTracker\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;

class TimeTrackerServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
        $this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
        $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker');
        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
        $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker');

        $this->publishes([
            __DIR__ . '/../public/js' => public_path('assets/js/timetracker-plugin'),
        ], ['timetracker-assets', 'public']);

        // Log plugin version on load
        if (file_exists(__DIR__ . '/../plugin.json')) {
            $pluginJson = json_decode(file_get_contents(__DIR__ . '/../plugin.json'), true);
            Log::info('✅ TimeTracker Plugin Loaded - Version: ' . ($pluginJson['version'] ?? 'unknown'));
        }
    }
}
menus.php


return [
    [
        'id' => 'team_monitoring_and_productivity_tracker',
        'label' => get_label('team_insights', 'Team Insights'),
        'url' => route('timetracker.index'),
        'icon' => 'bx bx-alarm',
        'class' => 'menu-item' . (request()->is('timetracker*') ? ' active open' : ''),
        'category' => 'team_monitoring_and_productivity_tracker',
        'show' => 1,
        'submenus' => [
            [
                'id' => 'productivity_dashboard',
                'label' => get_label('productivity_dashboard', 'Productivity Dashboard'),
                'url' => route('timetracker.index'),
                'show' => isAdminOrHasAllDataAccess() ? 1 : 0,
            ],
        ],
    ],
];
routes/web.php


use Illuminate\Support\Facades\Route;
use Plugins\TimeTracker\Controllers\TimeTrackerController;

Route::middleware(['web', 'auth'])->prefix('timetracker')->group(function () {
    Route::get('/', [TimeTrackerController::class, 'index'])->name('timetracker.index');
    Route::post('/track', [TimeTrackerController::class, 'storeTime']);
    Route::get('/configuration', [TimeTrackerController::class, 'configuration'])
        ->name('timetracker.configuration');
});

Complete Development Guide

A comprehensive guide covering everything you need to build, test, and deploy Taskify plugins

Quick Start Guide

1. Create Plugin Directory

Create a new folder in the plugins/ directory. Use PascalCase for the folder name (e.g., MyPlugin).

Terminal
mkdir -p plugins/MyPlugin

2. Create plugin.json

Create a plugin.json file in your plugin root with the required metadata.

plugin.json
{
  "name": "Taskify - My Plugin",
  "slug": "my-plugin",
  "description": "Description of your plugin",
  "version": "1.0.0",
  "enabled": true,
  "provider": "Plugins\\MyPlugin\\Providers\\MyPluginServiceProvider"
}

3. Create Service Provider

Create your Service Provider in Providers/MyPluginServiceProvider.php.

Providers/MyPluginServiceProvider.php


namespace Plugins\MyPlugin\Providers;

use Illuminate\Support\ServiceProvider;

class MyPluginServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
        $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'my-plugin');
        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
    }
}

4. Enable and Test

Set "enabled": true in plugin.json, then run migrations and test your plugin.

Terminal
php artisan migrate
php artisan cache:clear

Plugin Directory Structure

Follow this standard directory structure for maintainable plugins. All plugins should be placed in the plugins/ directory.

Directory Structure
MyPlugin/
├── Controllers/              # Plugin controllers
│   └── MyPluginController.php
├── Models/                   # Eloquent models
│   └── MyModel.php
├── Database/
│   └── Migrations/          # Database migrations
│       └── YYYY_MM_DD_HHMMSS_create_table.php
├── Providers/
│   └── MyPluginServiceProvider.php
├── routes/
│   ├── web.php             # Web routes
│   └── api.php             # API routes (optional)
├── Resources/
│   ├── views/              # Blade views
│   │   └── my-plugin/
│   │       └── index.blade.php
│   └── lang/
│       └── plugin_labels.php
├── public/
│   ├── js/                 # JavaScript files
│   ├── css/                # CSS files
│   └── img/                # Images
├── Services/               # Business logic (optional)
├── Commands/               # Artisan commands (optional)
├── Middleware/             # Custom middleware (optional)
├── menus.php               # Menu configuration
├── plugin.json             # Plugin manifest (REQUIRED)
└── README.md               # Plugin documentation

Naming Conventions

  • Plugin Folder: PascalCase (e.g., SocialMediaManagement)
  • Controllers: PascalCase + Controller suffix
  • Models: PascalCase (singular)
  • Routes: kebab-case with plugin prefix
  • Views: kebab-case folder names

Service Provider

The Service Provider is the heart of your plugin. It registers routes, views, migrations, translations, assets, commands, and services.

Complete Service Provider Example

TimeTrackerServiceProvider.php


namespace Plugins\TimeTracker\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;

class TimeTrackerServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Load routes
        $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
        $this->loadRoutesFrom(__DIR__ . '/../routes/api.php');

        // Load views
        $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker');

        // Load migrations
        $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');

        // Load translations
        $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker');

        // Publish assets
        $this->publishes([
            __DIR__ . '/../public/js' => public_path('assets/js/timetracker-plugin'),
        ], ['timetracker-assets', 'public']);
    }

    public function register(): void
    {
        // Register commands, services, etc.
    }
}

Key Methods

  • loadRoutesFrom() - Register routes
  • loadViewsFrom() - Register views
  • loadMigrationsFrom() - Register migrations
  • loadTranslationsFrom() - Register translations
  • publishes() - Publish assets

Best Practices

  • Always use __DIR__ for paths
  • Log plugin version on load
  • Auto-publish assets if needed
  • Register commands in register()
  • Use namespaces for views

Routes & Controllers

Route Definition

Define routes in routes/web.php with proper middleware and prefixes.

routes/web.php


use Illuminate\Support\Facades\Route;
use Plugins\MyPlugin\Controllers\MyPluginController;

Route::middleware(['web', 'auth'])->prefix('my-plugin')->group(function () {
    Route::get('/', [MyPluginController::class, 'index'])->name('my-plugin.index');
    Route::post('/store', [MyPluginController::class, 'store'])->name('my-plugin.store');
});

Controller Example

Controllers/MyPluginController.php


namespace Plugins\MyPlugin\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class MyPluginController extends Controller
{
    public function index()
    {
        return view('my-plugin::index');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string',
        ]);

        // Your logic here

        return redirect()->route('my-plugin.index');
    }
}

Database & Migrations

Creating Migrations

Create migrations in Database/Migrations/ following Laravel's naming convention.

Database/Migrations/2025_01_15_120000_create_items_table.php


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

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('items', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('name');
            $table->timestamps();
        });

        // Create permissions
        DB::table('permissions')->insert([
            ['name' => 'manage_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()],
        ]);
    }

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

Views & Templates

Using Views in Controllers

Render views using the namespace defined in your Service Provider.

Controller
return view('my-plugin::index', ['data' => $data]);

Blade Template Example

Resources/views/my-plugin/index.blade.php
@extends('layouts.app')

@section('content')

{{ get_label('my_plugin', 'My Plugin') }}

{{ get_label('create', 'Create') }}
@endsection

Important Guidelines

  • Always use get_label() for all text - never hardcode
  • Use @extends('layouts.app') to inherit Taskify's layout
  • No inline CSS/JS - use existing classes or custom.css
  • Prefix views with your plugin namespace: my-plugin::

Menu Integration

Creating menus.php

Create a menus.php file in your plugin root to register menu items.

menus.php


return [
    [
        'id' => 'my_plugin_menu',
        'label' => get_label('my_plugin', 'My Plugin'),
        'url' => route('my-plugin.index'),
        'icon' => 'bx bx-icon',
        'class' => 'menu-item' . (request()->is('my-plugin*') ? ' active' : ''),
        'category' => 'utilities',
        'show' => isAdminOrHasAllDataAccess() || auth()->user()->can('manage_items') ? 1 : 0,
        'submenus' => [
            [
                'id' => 'plugin_items',
                'label' => get_label('items', 'Items'),
                'url' => route('my-plugin.index'),
                'show' => 1,
            ],
        ],
    ],
];

Menu Properties

  • id - Unique identifier
  • label - Menu text (use get_label())
  • url - Route URL
  • icon - Boxicons class
  • category - Menu category
  • show - Visibility (1 or 0)

Best Practices

  • Always use get_label() for labels
  • Check permissions in show property
  • Use Boxicons for icons
  • Set active state with route checking
  • Support submenus for complex navigation

Permissions & Access Control

Creating Permissions

Create permissions in your migration's up() method.

Migration
DB::table('permissions')->insert([
    ['name' => 'manage_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()],
    ['name' => 'create_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()],
]);

Using Permissions

In Routes
Route::middleware(['web', 'auth', 'customcan:manage_items'])->group(function () {
    // Routes here
});
In Controllers
if (!isAdminOrHasAllDataAccess() && !auth()->user()->can('manage_items')) {
    abort(403);
}

Why Plugins?

Dynamic Routes

Register web and API routes that integrate seamlessly with the core system.

Menu Registration

Add menu items to the main navigation with custom icons and permissions.

Custom Pages

Create custom Blade views with full access to Taskify's layout system.

API Access

Build RESTful APIs with Laravel's routing and authentication system.

Scheduled Commands

Register Artisan commands and schedule automated tasks with Laravel's scheduler.

Asset Publishing

Automatically publish JavaScript, CSS, and image assets to the public directory.

Start Building Your First Plugin Today

Join the ecosystem and extend Taskify with powerful, modular plugins. Get started in minutes with our comprehensive guide.

Open Plugin Guide