visavi/rotor
Rotor is a modular Laravel admin panel and CMS toolkit from Visavi. It helps you scaffold management interfaces with CRUD resources, authentication, menus, and a responsive UI, so you can manage users, content, and settings without building a backend from scratch.
app/ while each site section (forum, blogs, gallery, and the other modules) ships as an independently activatable module. That means we can adopt a forum without inheriting a gallery, or run two modules and build the third ourselves — a materially cheaper decision than the usual "whole CMS or nothing" trade-off. v14.8.0 strengthens the evidence: Feedback (contact forms) and MailNotify (digest, birthday, unsubscribe) were extracted out of core into opt-in modules, so transactional email and inbound contact handling are now surfaces we choose rather than inherit. A core that is willing to shed scope, not just add it, is a healthier modularity signal than one that only grows.php artisan module:migrate wired into deploy.php after artisan:migrate — adding a surface later is a config change plus a theme pass, not a re-architecture, and it fits a standard deploy pipeline instead of a bespoke one.Registry::homepage(), plus FeedService::getLatest() for "latest from selected sections" on custom templates. We can ship feed-first for a community launch and switch to an editorial front page for a content play — per site, without a fork. The retirement of the homepageView hook in favour of a registered contract also means the composition point is now typed rather than stringly-convention.make:module Name --model=Item --admin --api) scaffolds API surface as a first-class option. v14.8.0 deepens this: GET /api/comments, per-list documented sort values in the spec, stable voting error codes and messages, and module-facing withUserVote() / capped() scopes (CappableTrait) so vote state and pagination behave consistently across surfaces. Guest access to ?user= filters now returns 403 rather than leaking, which is a policy we'd have had to write ourselves.og:image — i.e. the thumbnail people see when a link is shared. Media is capped at 700px/90vh in posts, attachments can be re-inserted into the editor by reference without re-upload, and accepted file types are offered per post type with the form and API sharing one rule set (FileService::rules()), including pre-uploaded files counting toward the limit. That is unusually concrete for a CMS release: it moves share-preview quality and upload validation out of "our problem."/index.php/..., /public/... and trailing slashes on any web server; canonical accounts for page number and ?pid= links are closed in robots.txt. Duplicate-URL cleanup is normally a post-launch remediation project, so shipping it as default behaviour is a direct acquisition benefit.Lang<Language> naming convention, so each new locale is an additive module rather than a fork — still relevant given the documentation and conventions are Russian-first.Registry::textFilter(), the Antimat module) is applied at attribute-read time via casts, with the original preserved in the database. We can tighten or relax moderation retroactively without a data migration — a genuine product lever, not just an implementation detail.PlaceCast cast (whitespace collapsed, first letter capitalized). Free-text geo fields become reportable instead of a cleanup exercise.database/migrations/ (fresh installs) and database/upgrades/ (existing sites, marked as applied on fresh installs) — an explicit forward-upgrade contract. v14.8.0 adds the operational layer that contract needs: a standalone cron.php for hosts whose control panel accepts only a file path (403 on web requests), and admin health warnings for scheduler, queue and mail that can be dismissed but reappear on a new failure. That is what makes a managed-instance or multi-year self-hosted offering defensible.data-ajax contract and a "Bootstrap utilities first" CSS rule, so visual differentiation remains a theme concern. The UX baseline itself improved without a build step: native <dialog> and <details> for modals and disclosures, collapsed comment/dialog composers, back/forward-safe flash messages. Lower JS surface means a cheaper theme and a better accessibility story.App\Classes\* → App\Support\* bridge is documented as deprecated until 15.0; v14.8.0 adds new dated bridges (/mails, the old ?module=Name admin URL) rather than only removals, and drops the abandoned spatie/laravel-ignition. The deprecation runway remains the signal we use to judge whether we can ride upstream or must budget for a fork. The counterweight: modules are version-locked to core (many now require 14.8.0), so upgrades are a coordinated core-plus-modules rollout.Adopt when:
artisan/module-activation/module:migrate workflow. The new cron.php also covers cheap shared hosts where scheduling is path-only.AGENTS.md conventions are unusually complete (module generator, per-module changelog, upgrade migrations, pint/phpstan/test commands, known traps), so a timeboxed spike has a low cost of failure and a fast onboarding path for contract or AI-assisted contributions.Look elsewhere when:
_language_modal/_comment_edit_modal → *_dialog, plus a changed data-dialog attribute), removed a hook, changed a public API query parameter to token-only, and requires post-upgrade manual actions (install MailNotify and Feedback or mail and the contact form stop working; re-save pinned records; re-select the Classic homepage). That is normal for an actively developed CMS, but it is a maintenance budget, not a one-click upgrade.truncate()/DDL breaks RefreshDatabase, partial-column upserts fail under DB_STRICT, empty form fields arrive as null into NOT NULL columns, and file handling must go through FileService or video conversion and attachment ordering silently break. The 14.8.0 fix list (TypeError on empty fields under strict_types, sort-key 500s, error pages before theme is wired) shows the class of bug this density produces. Misapplied, they are subtle and expensive.To executives: Rotor is a modular, mobile-oriented community CMS built on Laravel 13. It hands us forum, blogging, gallery, contact/feedback and email-notification capabilities as installable modules instead of a monolith, with an API path per module for a mobile client, a locale-per-module path for market expansion, and — as of 14.8.0 — a configurable homepage so we can choose a social feed or an editorial front page per site. The commercial case is time-to-market: it compresses months of undifferentiated community-feature build into module adoption plus a theme pass, and the latest release adds the unglamorous parts that decide whether a launch is actually cheap — canonical URLs and redirects for search, correct social-share previews from attachment ordering, per-post-type upload rules validated identically in UI and API, a scheduler entry point for cheap hosting, and deploy-integrated module migrations. Its documented upgrade mechanism protects any deployment we promise to keep alive for years.
The honest risks: it is GPL-3.0, so any plan to redistribute modified code or embed it in a closed-source product needs a legal review first. It is also small — ~58 stars, no visible commercial backing — so we should assume a fork-and-own contingency rather than a vendor relationship, and budget for Laravel 13 / PHP 8.3 / strict-MySQL competence on our side. Finally, upgrades are not free: this release moved mail and feedback into modules (not installing them silently breaks mail and the contact form), renamed theme partials, and version-locks modules to the core. We need a named owner for core-plus-module upgrade cycles, not just a go-live plan.
To engineering: the conventions are explicit and enforced — a generator for new modules, per-module changelogs, a separate upgrade-migration path for existing installs, module:migrate hooked into deploy.php, pint/phpstan/test gates, a declarative ajax contract instead of per-module JS, and HTML sanitization pushed down to model casts. Extension points are becoming contracts rather than hooks: Registry::homepage() replaces the retired homepageView, and withUserVote()/capped() give modules one canonical way to express vote state and pagination in the API. Deprecated APIs carry dated removal at 15.0 and this release adds bridges (/mails, legacy admin module URL) rather than only deleting, so there is a runway. Scope is enforceable at the module boundary, which is exactly what we want for incremental delivery.
The ask: a timeboxed spike — stand the stack up in Docker, activate two modules plus Feedback and MailNotify, ship one API endpoint (comments or a vote-aware list), one homepage-selection change and one theme change, run the test suite, then perform a real core-plus-module upgrade to measure the actual cost. Then make an adopt / fork / build call with real numbers instead of a star count.
How can I help you explore Laravel packages today?