Installation

Installation Guide

This guide covers installing medialight/textstem-laravel into a new or existing Laravel application.


Requirements

PHP and Laravel

Requirement Version
PHP >= 8.3
Laravel 10, 11, 12, or 13
Livewire ^3.2
Database MySQL 8+ or PostgreSQL 13+ recommended

PHP Extensions

The following PHP extensions must be enabled:

Extension Used for
gd or imagick Image processing via Glide
pdo Database access
fileinfo File type detection on upload
zip Export commands

System Packages

Package Used for
Ghostscript PDF-to-image conversion (gravitymedia/ghostscript)
Node.js + npm (or Bun) Compiling Jetstream frontend assets

Install Ghostscript via your OS package manager:

# Debian / Ubuntu
apt-get install ghostscript

# macOS (Homebrew)
brew install ghostscript

Step 1 -- Add the Repository

The package is hosted on a private GitHub repository. Add it to the repositories array in your host app's composer.json before requiring it:

"repositories": [
    {
        "type": "vcs",
        "url": "git@github.com:MeccaMedialight/textstem-laravel.git"
    }
]

Ensure your machine or CI environment has SSH access to that repository via a deploy key or personal access token.


Step 2 -- Install via Composer

composer require medialight/textstem-laravel

Laravel's package auto-discovery registers all service providers and facade aliases automatically. No manual entry in config/app.php is needed.


Step 3 -- Run the Post-Install Wizard

php artisan textstem:post-install

The wizard prompts for an installation type and then publishes all required files:

Option What it does
Vanilla - no Jetstream Skips Jetstream installation
Jetstream with Livewire Runs jetstream:install livewire
Jetstream with Inertia Runs jetstream:install inertia

After the stack choice is confirmed, the wizard publishes:

  • config/textstemapp.php, config/openai.php, config/chunk-upload.php (tag: textstem-config)
  • Wrangler views to resources/views/wrangler/ (tag: textstem-components)
  • Auth views to resources/views/auth/ (tag: textstem-components)
  • JS/CSS assets to public/vendor/medialight/textstem/ including TinyMCE (tag: public)
  • Jetstream views (tag: jetstream-views)

Build Frontend Assets (Jetstream installs only)

If you chose a Jetstream stack, compile the frontend before continuing:

npm install && npm run build
# or, if you use Bun:
bun install && bun run build

Step 4 -- Configure Sanctum

The API uses Laravel Sanctum for token authentication. Jetstream installs Sanctum automatically. If you chose the Vanilla option, publish the Sanctum config and migrations manually:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

If you need cookie-based (stateful) API authentication, add your app's domain to the stateful array in config/sanctum.php:

'stateful' => [
    'localhost',
    'your-app.test',
    env('APP_URL'),
],

Token-based (Bearer) API requests work without this change.


Step 5 -- Configure Environment

Ensure your .env contains a working database connection before running migrations:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database
DB_USERNAME=your_user
DB_PASSWORD=your_password

Then add the package-specific variables below. All have defaults in config/textstemapp.php; only set what your app needs to change.

Core

# Email address(es) that receive contact form notifications (comma-separated)
CONTACT_NOTIFICATIONS_EMAIL=you@example.com

# Set to true to redirect all outbound mail to CONTACT_NOTIFICATIONS_EMAIL instead of real recipients
EMAIL_TEST_MODE=false

# Items per page in admin list views
PAGINATION_ITEMS_PER_PAGE=10

# Storage disk for uploaded assets (matches a key in config/filesystems.php)
ASSET_DISK=public

Page Cache

WRANGLER_CACHE_ENABLE=false
WRANGLER_CACHE_TTL=3600
# HTTP cache-control header value: public or private
WRANGLER_CACHE_MODE=private
WRANGLER_CACHE_WITHQUERY=false

TinyMCE

# Leave blank to use the locally published TinyMCE build (no account required)
TINYMCE_API_KEY=

OpenAI / AI Features

OPENAI_API_KEY=sk-...
OPENAI_ENABLED=true
OPENAI_USE_QUEUE=true
OPENAI_MODEL=gpt-4o-mini

Google Cloud Vision

Image comparison and analysis features use Google Cloud Vision. Provide a service account credentials file:

GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json

If you do not use image AI features, set textstemapp.images.comparison_driver to fingerprint in config/textstemapp.php to skip Cloud Vision entirely.

Accessibility Analysis

ACCESSIBILITY_ENABLED=true
ACCESSIBILITY_USE_QUEUE=true
ACCESSIBILITY_WCAG_LEVEL=AA         # A, AA, or AAA
ACCESSIBILITY_CHECK_ON_SAVE=false
ACCESSIBILITY_STORE_REPORTS=true
ACCESSIBILITY_REPORT_RETENTION_DAYS=30

SEO Analysis

SEO_ENABLED=true
SEO_USE_QUEUE=true
SEO_USE_AI=true
SEO_CHECK_ON_SAVE=false
SEO_STORE_REPORTS=true
SEO_REPORT_RETENTION_DAYS=30

File Uploads

TEXTSTEM_ALLOWED_EXTENSIONS=jpg,jpeg,png,gif,svg,webp,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,zip,mp3,wav,mp4,mov,avi,wmv
TEXTSTEM_MAX_FILE_SIZE=10240             # kilobytes; default is 10 MB
TEXTSTEM_STORAGE_PATH=public/uploads
TEXTSTEM_UPLOADS_USE_QUEUE=false
TEXTSTEM_UPLOADS_ENABLE_FINGERPRINT=true
TEXTSTEM_UPLOADS_GENERATE_THUMBNAILS=true
TEXTSTEM_UPLOADS_EXTRACT_METADATA=true

Redis (optional)

TEXTSTEM_USE_REDIS=false

Set to true if your environment has Redis available. The package will use it for caching and queues.

Admin UI

DARK_MODE=false

Step 6 -- Run Migrations

Installing into an existing app? If your app already has a users table, the package migration 2014_10_12_000000_create_users_table.php will conflict and fail. Skip it by inserting the migration filename into the migrations table manually, then run php artisan migrate. The textstem:migrations:sync command can do this in bulk -- see the artisan commands reference.

Run all outstanding migrations:

php artisan migrate

This creates the following tables (in addition to Laravel defaults):

Table Description
users Extended user model with roles and Jetstream traits
options Global site options
events Event records
previews Page preview snapshots
categories Shared category model for pages, posts, and assets
wrangler_pages Page-builder pages
wrangler_components Page components
assets Media asset library
posts Blog/news posts
post_collections Post groupings
redirects URL redirect rules
tags / taggables Spatie tag tables
messages In-app messages
roles / permissions Spatie permission tables
personal_access_tokens Sanctum API tokens
prompts AI prompt library
seo_reports Stored SEO analysis results
accessibility_reports Stored accessibility analysis results
orchestration_* AI orchestration state tables

Step 7 -- Set Up Asset Storage

Run Laravel's storage link command so uploaded files in the public disk are accessible via the web:

php artisan storage:link

For S3 storage, configure the s3 disk in config/filesystems.php and set ASSET_DISK=s3 in .env.


Step 8 -- Set Up Queues

AI generation, accessibility and SEO analysis, and asset processing jobs are dispatched to a queue. Configure a real queue driver for production:

QUEUE_CONNECTION=redis     # or database, sqs, etc.

Start a worker:

php artisan queue:work --queue=default --tries=3

For production, manage the worker process with Supervisor or Laravel Horizon.


Step 9 -- Register the Scheduler

The package registers scheduled commands automatically via the service provider. Add the scheduler call to your server crontab so they execute:

* * * * * cd /path-to-your-app && php artisan schedule:run >> /dev/null 2>&1

Commands the package schedules:

Command Schedule Description
textstem:prune-reports Daily at 03:00 Removes old SEO and accessibility reports
textstem:health-check Daily at 01:00 Checks application health
cache:manage clear --warm-up Mondays at 01:30 Clears and re-warms the cache

Step 10 -- Create a Super Admin User

Register a user through your app's registration flow, then create the super-admin role (if it does not already exist) and assign it:

use Medialight\Textstem\Models\User;
use Spatie\Permission\Models\Role;

$role = Role::firstOrCreate(['name' => 'super-admin']);

$user = User::find(1);
$user->assignRole($role);

This can be run in Tinker (php artisan tinker) or placed in a seeder.

Super-admin users bypass all gate checks and have unrestricted access to the admin panel.


Step 11 -- Seed Sample Data (optional)

php artisan textstem:dbseed

Creates sample pages, posts, categories, and assets to give the admin panel something to work with immediately.


Verification

After completing the steps above, confirm the following:

  • public/vendor/medialight/textstem/ exists and contains JS/CSS files
  • resources/views/wrangler/ exists with components/, pages/, posts/, and template-commands/ subdirectories
  • config/textstemapp.php exists in the host app
  • php artisan migrate:status shows all package migrations as Ran
  • The admin panel loads at /textstem without errors
  • /textstem/dashboard is accessible after logging in as the super-admin user

Updating the Package

composer update medialight/textstem-laravel
php artisan migrate
php artisan textstem:refresh-assets

Review config/textstem.php in the package against your published config/textstemapp.php after each update and merge in any new keys.


Notes

User Model

The service provider overrides auth.providers.users.model at runtime to point to Medialight\Textstem\Models\User. This model extends the standard Laravel user and adds Spatie role/permission support and Jetstream traits.

If your app needs a custom user model, extend the package model rather than Illuminate\Foundation\Auth\User:

// app/Models/User.php
class User extends \Medialight\Textstem\Models\User
{
    // your customisations
}

Then re-point the auth config in your AppServiceProvider:

public function boot(): void
{
    config(['auth.providers.users.model' => \App\Models\User::class]);
}

Localization Middleware

The Localization middleware is appended to the web group automatically by the service provider. It reads a locale from the session and calls app()->setLocale(). No manual registration is needed.

Re-publishing Assets

If you update the package or suspect published files are out of date, re-run:

php artisan textstem:refresh-assets

This clears public/vendor/medialight/textstem/, re-publishes the JS/CSS and TinyMCE build, and ensures the required Wrangler view directories exist.

esc