escalated-dev/escalated-laravel
A full-featured, embeddable support ticket system for Laravel with SLA tracking, escalation rules, agent workflows, and a customer portal. Self-host or sync to cloud with one config change.
laravel/framework 11.x security advisories that Composer 2.9+ now excludes during dependency resolution (all released 11.x are affected with no advisory-clean version yet), which was breaking the Laravel 11 compatibility test matrix. Added to this package's root config.audit.ignore only — it does not propagate to host applications, which govern their own audit policy. No runtime/code changes.escalated:newsletters:dispatch scheduled command, with per-minute rate limiting, retry backoff, and auto-pause on high bounce rates. Disabled by default behind escalated.enable_newsletters. The created_by/sent_by/added_by columns are stored UUID-safe so integer/UUID/string-keyed host users all work. (#103, #128)add_follower workflow action — auto-subscribe a host user as a ticket follower from a workflow rule. (#127)Escalated\Laravel\Contracts\TicketSubject contract (or use the PresentsAsTicketSubject trait) to expose a title/subtitle/url/color/icon for the ticket UI. A ticket can reference several subjects via attachSubject()/detachSubject()/syncSubjects(); they're serialized on TicketResource as a subjects[] array. Agent attach/detach endpoints resolve types strictly against the new escalated.ticket_subjects.types allowlist. subject_id is stored as a string so integer/UUID/string-keyed host models all work. (#89)escalated.permissions (alongside is_admin/features), so the UI can gate navigation per-permission. (#129)newsletters.manage, and send-class actions (saving/updating with a scheduled/sending status, and test-send) additionally require newsletters.send. Previously the permissions were seeded but never checked, so any admin-role user could perform every newsletter action. (#129)add_routing_columns_to_escalated_skills_table) that backfills the routing_tag_ids/routing_department_ids columns. v1.4.0 added them by editing the original create_escalated_skills_table migration, which never re-runs on an existing install — so apps upgraded from an earlier version were missing the columns and every skill create/edit failed (Skill::saving() always writes them). The new migration is guarded by Schema::hasColumn, so it is a no-op on fresh installs.AssignTicketRequest (Admin/Agent assign endpoints) again validates that agent_id exists (against the host user key), so an unknown/garbage id returns a clean 422 instead of a 500. The integer rule remains dropped so UUID/string keys are accepted (matching Api\TicketController).AssignmentService::__construct() $skillRoutingService is now optional (resolved from the container when omitted), restoring the v1.3.0 new AssignmentService($manager) single-argument signature for direct instantiation / subclasses.int → int|string widening (UUID/string user-key support). The TicketDriver contract's assignTicket() and several public Ticket methods (assign, follow, unfollow, isFollowedBy, scopeAssignedTo) now accept int|string. If your app implements TicketDriver or subclasses Ticket and type-hinted these parameters as int, widen them to int|string to avoid a PHP "must be compatible" fatal. Apps that only call these methods are unaffected.TicketAssigned event's $agentId is now int|string; for UUID/string-keyed apps the broadcast agent_id is a string. Integer-keyed apps are unchanged.TicketCustomActionTriggered event (with an audit internal note) when clicked, exposed to the agent UI as customActions and to the API as custom_actions. (#107, #108)Escalated::userForeignColumn()/userMorphs() now type user_id/assigned_to/requester/author/causer/pivot columns to match the configured user_model — unsignedBigInteger for integer keys, string-compatible columns for HasUuids/HasUlids/string keys — so UUID/string-keyed apps migrate cleanly with no manual edits. Override via the new escalated.user_key_type config (auto by default). (#112)escalated-dev/locale Composer package so wording stays consistent across every Escalated host plugin. The EscalatedServiceProvider now stitches three layers under the escalated namespace: the central package (canonical), lang/vendor/escalated/ in this repository (Laravel-specific overrides), and the host app's lang/vendor/escalated/ (consumer overrides via php artisan vendor:publish --tag=escalated-lang). The package's own resources/lang/ is retained as a fallback for environments where the central package has not yet been composer-installed.SavedView::scopeForUser() (and every other user-id parameter) now accepts int|string instead of hard-typing int, fixing a TypeError 500 (Argument #2 ($userId) must be of type int, string given) that hit apps with non-integer user keys when opening /support/admin/tickets. Incoming user ids are no longer cast to int anywhere (which corrupted UUIDs). (#110)qs dependency in the demo host-app from 6.15.1 to 6.15.2 to remediate CVE-2026-8723 (NULL pointer dereference). (#113)ReportingService and ReportController::avgFirstResponseHours. Previous TIMESTAMPDIFF(HOUR, ...) and DATE_FORMAT(...) calls (MySQL-only) caused /support/admin/reports to 500 on Postgres deployments with column "hour" does not exist. Date/time and date-format helpers now branch on driver across sqlite | pgsql | mysql. (#60, fixes #59)docker/ (excluded from the Composer dist via archive.exclude). docker compose up --build boots a Postgres-backed Laravel host with the package installed and a click-to-login picker. (#58)Escalated\Laravel\Database\Factories\ so Model::factory() resolves at runtime in real installs. Same class of bug as #55. (#58)WorkflowEngine::actionSendWebhook() by validating URL scheme and rejecting URLs that resolve to private/reserved IPs (#49)compareValues() matches operator via safeRegexMatch() with pattern validation and a PCRE backtrack limit (#49)in: validation for actions.*.type in WorkflowController store/update to prevent arbitrary action type injection (#49)subject, description, ticket_type, channel) in resolveFieldValue() default case instead of open $ticket->{$field} (#49)5/min, chat start 5/min, chat message 30/min (#49)AuditLog entries for workflow create/update/delete and report exports (#49)Escalated\Laravel\Database\Seeders namespace in production autoload so php artisan escalated:install can run the permission seeder when the package is installed as a dependency (#56)url in attachment serialization (#50)$appends to detail-only serialization to keep list endpoints fast (#53)minimum-stability to stable and add audit.ignore for two phpunit advisories that were blocking the resolver from selecting any compatible phpunit version (#57)How can I help you explore Laravel packages today?