# Extend Your Taskify
Superpowers with Plugins

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

Read Documentation View Plugin Examples

### 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