PHPackages                             idoneo/cms-core - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. idoneo/cms-core

ActiveLibrary

idoneo/cms-core
===============

Multi-tenant CMS foundation with Teams support, Livewire components and Filament integration for Laravel.

v2.0.0(3mo ago)051AGPL-3.0-or-laterPHPPHP ^8.2

Since Dec 8Pushed 3mo agoCompare

[ Source](https://github.com/diego-mascarenhas/cms-core)[ Packagist](https://packagist.org/packages/idoneo/cms-core)[ Docs](https://github.com/diego-mascarenhas/cms-core)[ RSS](/packages/idoneo-cms-core/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (16)Versions (38)Used By (0)

CMS-Core
========

[](#cms-core)

[![Latest Version on Packagist](https://camo.githubusercontent.com/89b91a49f85545513b486ef0c6a705cb7ab8fa3f518fdc3611e85a01ca664165/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f69646f6e656f2f636d732d636f72652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/idoneo/cms-core)[![Total Downloads](https://camo.githubusercontent.com/610b9dc95bdc3a78f6f8e04180987cef6a177480f2b2ccd3835ff6f587854d3d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f69646f6e656f2f636d732d636f72652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/idoneo/cms-core)[![License](https://camo.githubusercontent.com/1fa769277cadf5961d18b9a60e38284b0a8f8c0ef0d84ba9f3ec5709cbfc75ca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f69646f6e656f2f636d732d636f72652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/idoneo/cms-core)

Multi-tenant CMS foundation with Teams support, Livewire components and Filament integration for Laravel.

Features
--------

[](#features)

- **BelongsToCurrentTeam** trait for automatic team scoping
- **TeamSwitcher** Livewire component with alphabetical sorting
- **CmsCorePlugin** for Filament panels with user menu
- **Spatie Laravel Tags** integration for tagging and categorization
- Toggle teams feature via environment variable

Installation
------------

[](#installation)

### Create New Project

[](#create-new-project)

To create a new Laravel project with CMS-Core from scratch:

```
composer create-project laravel/laravel my-project
cd my-project
composer require idoneo/cms-core
php artisan cms-core:install --fresh --seed
npm install && npm run build
```

This will:

- Create a new Laravel project
- Install CMS-Core package (with Jetstream, Filament, Livewire, and Spatie packages)
- Configure Jetstream with Livewire + Teams
- Install Filament panel (with Spanish locale)
- Register Jetstream routes automatically (`profile.show`, `teams.show`, `teams.create`, etc.)
- Publish Spatie Permission migrations
- Publish Spatie Tags migrations
- Publish Spatie Media Library migrations
- Publish Teams migrations (included in CMS-Core)
- Publish Post model
- Publish CMS-Core config files
- Configure authentication to use Filament login
- Set application locale to Spanish
- Run all migrations
- Create admin user ( / Simplicity!)
- Install and build npm assets

### Install in Existing Project

[](#install-in-existing-project)

If you already have a Laravel project, install the package:

```
composer require idoneo/cms-core
php artisan cms-core:install --fresh --seed
npm install && npm run build
```

**Note:** The `--fresh` flag will drop all existing tables. If you want to keep your data, use:

```
php artisan cms-core:install --seed
php artisan migrate
```

### Update Existing Installation

[](#update-existing-installation)

To update CMS-Core resources (migrations, config, views, translations) in an existing project:

```
composer update idoneo/cms-core
php artisan cms-core:update
php artisan migrate
npm run build
```

**Options:**

- `--migrations`: Only publish migrations
- `--force`: Force publish even if files exist

### Manual Setup

[](#manual-setup)

If you need more control:

```
# Install dependencies
composer require idoneo/cms-core

# Install Jetstream manually
php artisan jetstream:install livewire --teams

# Publish CMS-Core assets
php artisan vendor:publish --tag="cms-core"

# Run migrations
php artisan migrate

# Build assets
npm install && npm run build
```

Register the plugin in your `AdminPanelProvider`:

```
use Idoneo\CmsCore\Filament\CmsCorePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(CmsCorePlugin::make())
```

Tags System
-----------

[](#tags-system)

This package includes Spatie Laravel Tags for tagging and categorization. See [docs/TAGS.md](docs/TAGS.md) for complete documentation.

Quick example:

```
use Spatie\Tags\HasTags;
use Filament\Forms\Components\SpatieTagsInput;

class Post extends Model
{
    use HasTags;
}

// In Filament Resource
SpatieTagsInput::make('tags')
    ->label('Tags')

// For categorization
SpatieTagsInput::make('categories')
    ->type('categories')
        // ... other configuration
}
```

### Default Credentials

[](#default-credentials)

After running with `--seed` flag:

FieldValueEmail`hola@humano.app`Password`Simplicity!`Configuration
-------------

[](#configuration)

Add to your `.env` file:

```
# Show/hide teams UI features (default: false)
# Note: Users always have a personal team (Jetstream requirement)
# This setting only controls UI visibility (team switcher, create team, team settings)
APP_TEAMS=false
```

When `APP_TEAMS=false`:

- ✅ Users still have a personal team (required by Jetstream)
- ❌ Team switcher is hidden
- ❌ "Create New Team" option is hidden
- ❌ "Team Settings" option is hidden

When `APP_TEAMS=true`:

- ✅ All team features visible
- ✅ Users can create multiple teams
- ✅ Team switcher appears in user menu

### Brand Logos

[](#brand-logos)

CMS-Core automatically configures brand logos for your Filament panel if you place logo files in `public/custom/`:

**Supported files:**

- `logo-light.svg` - Logo for light mode
- `logo-dark.svg` - Logo for dark mode

**Setup:**

1. Create the `public/custom/` directory (if it doesn't exist)
2. Place your logo files: ```
    public/
    └── custom/
        ├── logo-light.svg
        └── logo-dark.svg

    ```
3. The logos will be automatically detected and configured on the next page load

**Note:** Only SVG files are supported. The plugin automatically detects and configures the logos without any additional configuration needed.

Usage
-----

[](#usage)

### BelongsToCurrentTeam Trait

[](#belongstocurrentteam-trait)

Add to models that should be scoped by team:

```
use Idoneo\CmsCore\Traits\BelongsToCurrentTeam;

class Project extends Model
{
    use BelongsToCurrentTeam;
}
```

This automatically:

- Filters queries by `current_team_id`
- Sets `team_id` on create
- Provides `forTeam()` and `withoutTeamScope()` methods

### TeamSwitcher Component

[](#teamswitcher-component)

Include in your Blade views:

```
@if(\Idoneo\CmsCore\CmsCore::teamsEnabled())

@endif
```

### Filament Plugin

[](#filament-plugin)

Register in your Panel Provider:

```
use Idoneo\CmsCore\Filament\CmsCorePlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugins([
            CmsCorePlugin::make(),
        ]);
}
```

### Helper Methods

[](#helper-methods)

```
use Idoneo\CmsCore\CmsCore;

// Check if teams are enabled
CmsCore::teamsEnabled();

// Get configured models
CmsCore::teamModel();
CmsCore::userModel();
```

### Team Roles Configuration

[](#team-roles-configuration)

After installation, configure team roles in `app/Providers/JetstreamServiceProvider.php`:

```
protected function configurePermissions(): void
{
    Jetstream::defaultApiTokenPermissions(['read']);

    Jetstream::role('admin', 'Administrator', [
        'create',
        'read',
        'update',
        'delete',
    ])->description('Administrator users can perform any action.');

    Jetstream::role('member', 'Member', [
        'read',
        'create',
        'update',
    ])->description('Members have standard access to create and manage content.');

    Jetstream::role('viewer', 'Viewer', [
        'read',
    ])->description('Viewers can only read and view content.');
}
```

These are **suggested roles** that work for most use cases. You can customize them:

- Rename roles (e.g., "member" → "employee", "student", "collaborator")
- Add new roles
- Modify permissions per role

### Panel Access Control

[](#panel-access-control)

By default, all authenticated users can access the Filament panel. To restrict access, implement `FilamentUser` in your User model:

```
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;

class User extends Authenticatable implements FilamentUser
{
    public function canAccessPanel(Panel $panel): bool
    {
        // Allow all authenticated users (default behavior)
        return true;

        // Or restrict by email domain
        // return str_ends_with($this->email, '@yourdomain.com');

        // Or use roles with Spatie Permission
        // return $this->hasRole('admin');
    }
}
```

API Documentation
-----------------

[](#api-documentation)

CMS-Core provides a RESTful API for accessing posts with authentication via Sanctum tokens.

### Authentication

[](#authentication)

All API endpoints require authentication using a Bearer token. You can generate a token in two ways:

#### Option 1: Generate Token via Artisan Command (Recommended)

[](#option-1-generate-token-via-artisan-command-recommended)

Generate a token and add it to your `.env` file:

```
# Generate token for admin user (default)
php artisan cms-core:api-token

# Or specify a user email
php artisan cms-core:api-token --email=hola@humano.app --name="API Token"
```

**Output example:**

```
API Token generated successfully!

Token Name: API Token
User: hola@humano.app

Add this to your .env file:
APP_TOKEN=1|abc123def456ghi789jkl012mno345pqr678stu901vwx234yz

⚠️  Save this token now! You won't be able to see it again.

```

**Copy the token and add it to your `.env` file:**

```
APP_TOKEN=1|abc123def456ghi789jkl012mno345pqr678stu901vwx234yz
```

**Then use it in your curl requests:**

```
# Replace YOUR_TOKEN with the token from .env
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

#### Option 2: Generate Token via User Profile

[](#option-2-generate-token-via-user-profile)

Generate a token in your user profile settings at `/user/api-tokens` or via Jetstream's API token management interface.

**Headers:**

```
Authorization: Bearer {your-token}
Content-Type: application/json
Accept: application/json

```

Troubleshooting
---------------

[](#troubleshooting)

### Teams Feature Not Showing

[](#teams-feature-not-showing)

If you have `APP_TEAMS=true` in your `.env` but the teams options don't appear in the user menu, run the diagnostic command:

```
php artisan cms-core:diagnose
```

This will check:

- ✓ Config file exists
- ✓ Teams configuration (ENV, config, CmsCore helper)
- ✓ Jetstream routes registration
- ✓ Current user and team setup

**Common solutions:**

```
# 1. Clear config cache
php artisan config:clear

# 2. Verify .env has APP_TEAMS=true (no spaces)
echo "APP_TEAMS=true" >> .env

# 3. Republish config
php artisan vendor:publish --tag=cms-core-config --force

# 4. Clear cache again
php artisan config:clear

# 5. Restart server (if using php artisan serve)
```

Accept: application/json

```

**Note:** If you set `APP_TOKEN` in your `.env`, you can use that token directly. The system will automatically authenticate the user associated with that token.

#### Quick Start Example

1. **Generate a token:**
```bash
php artisan api:token --email=hola@humano.app

```

2. **Copy the output token to your `.env` file:**

```
APP_TOKEN=1|abc123def456ghi789...
```

3. **Test the API with curl:**

```
# List all published posts
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer 1|abc123def456ghi789..." \
  -H "Accept: application/json"

# Or use the token from .env (if you have it exported)
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer $APP_TOKEN" \
  -H "Accept: application/json"
```

### Endpoints

[](#endpoints)

#### List Posts

[](#list-posts)

Get a paginated list of posts with optional filters.

**GET** `/api/posts`

**Query Parameters:**

- `status` (optional): Filter by status (`draft`, `published`, `archived`). Default: `published`
- `category` (optional): Filter by category name
- `tag` (optional): Filter by tag name
- `search` (optional): Search in title, excerpt, and content
- `per_page` (optional): Number of items per page (1-100, default: 15)
- `page` (optional): Page number (default: 1)

**Example Request:**

```
# Using token from .env (APP_TOKEN)
curl -X GET "http://localhost:8000/api/posts?status=published&category=Tutorials&per_page=10" \
  -H "Authorization: Bearer $(php artisan tinker --execute='echo env(\"APP_TOKEN\");')" \
  -H "Accept: application/json"

# Or using a token directly
curl -X GET "http://localhost:8000/api/posts?status=published&category=Tutorials&per_page=10" \
  -H "Authorization: Bearer 1|your-token-here" \
  -H "Accept: application/json"

# Simple example (replace YOUR_TOKEN with your actual token)
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**Example Response:**

```
{
  "data": [
    {
      "id": 1,
      "title": "My First Post",
      "slug": "my-first-post",
      "excerpt": "This is an excerpt...",
      "content": "Full content here...",
      "status": "published",
      "published_at": "2024-01-15T10:00:00Z",
      "created_at": "2024-01-15T09:00:00Z",
      "updated_at": "2024-01-15T09:30:00Z",
      "author": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com"
      },
      "featured_image": {
        "url": "https://...",
        "thumb": "https://...",
        "web": "https://..."
      },
      "gallery": [
        {
          "url": "https://...",
          "thumb": "https://...",
          "web": "https://..."
        }
      ],
      "categories": [
        {
          "id": 1,
          "name": "Tutorials",
          "slug": "tutorials"
        }
      ],
      "tags": [
        {
          "id": 2,
          "name": "Laravel",
          "slug": "laravel"
        }
      ]
    }
  ],
  "links": {
    "first": "https://...",
    "last": "https://...",
    "prev": null,
    "next": "https://..."
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 5,
    "per_page": 10,
    "to": 10,
    "total": 50
  }
}
```

#### Get Post by Slug

[](#get-post-by-slug)

Get a single post by its slug.

**GET** `/api/posts/{slug}`

**Example Request:**

```
# Using token from .env (APP_TOKEN)
curl -X GET "http://localhost:8000/api/posts/my-first-post" \
  -H "Authorization: Bearer $(php artisan tinker --execute='echo env(\"APP_TOKEN\");')" \
  -H "Accept: application/json"

# Or using a token directly
curl -X GET "http://localhost:8000/api/posts/my-first-post" \
  -H "Authorization: Bearer 1|your-token-here" \
  -H "Accept: application/json"

# Simple example (replace YOUR_TOKEN with your actual token)
curl -X GET "http://localhost:8000/api/posts/my-first-post" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**Example Response:**

```
{
  "data": {
    "id": 1,
    "title": "My First Post",
    "slug": "my-first-post",
    "excerpt": "This is an excerpt...",
    "content": "Full content here...",
    "status": "published",
    "published_at": "2024-01-15T10:00:00Z",
    "created_at": "2024-01-15T09:00:00Z",
    "updated_at": "2024-01-15T09:30:00Z",
    "author": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com"
    },
    "featured_image": {
      "url": "https://...",
      "thumb": "https://...",
      "web": "https://..."
    },
    "gallery": [],
    "categories": [],
    "tags": []
  }
}
```

### Team Scoping

[](#team-scoping)

If teams are enabled (`APP_TEAMS=true`), the API automatically filters posts by the authenticated user's current team. Users can only access posts from their current team.

### Error Responses

[](#error-responses)

**401 Unauthorized:**

```
{
  "message": "Unauthenticated."
}
```

**404 Not Found:**

```
{
  "message": "No query results for model [App\\Models\\Post] {slug}"
}
```

**422 Validation Error:**

```
{
  "message": "The given data was invalid.",
  "errors": {
    "status": ["The selected status is invalid."]
  }
}
```

### Complete cURL Examples

[](#complete-curl-examples)

Here are ready-to-use curl examples (replace `YOUR_TOKEN` with your actual token):

**List all published posts:**

```
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**List posts with filters:**

```
curl -X GET "http://localhost:8000/api/posts?status=published&category=Tutorials&search=laravel&per_page=5" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**Get a specific post by slug:**

```
curl -X GET "http://localhost:8000/api/posts/my-first-post" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**Filter by tag:**

```
curl -X GET "http://localhost:8000/api/posts?tag=laravel" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json"
```

**Pretty print JSON response (using jq):**

```
curl -X GET "http://localhost:8000/api/posts" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Accept: application/json" | jq
```

Requirements
------------

[](#requirements)

- PHP ^8.2
- Laravel ^11.0 | ^12.0
- Livewire ^3.0
- Laravel Jetstream with Teams (recommended)

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

If you discover a security vulnerability, please send an e-mail to Diego Mascarenhas Goytía via . All security vulnerabilities will be promptly addressed.

License
-------

[](#license)

Licensed under the [GNU Affero General Public License v3.0 (AGPL-3.0)](https://www.gnu.org/licenses/agpl-3.0.html).

### Additional Terms

[](#additional-terms)

By deploying this software, you agree to notify the original author at  or by visiting [linkedin.com/in/diego-mascarenhas](https://linkedin.com/in/diego-mascarenhas/). Any modifications or enhancements must be shared with the original author.

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance82

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Recently: every ~15 days

Total

36

Last Release

96d ago

Major Versions

v1.4.4 → v2.0.02026-02-11

### Community

Maintainers

![](https://www.gravatar.com/avatar/fa190334db1e228625e2df973f74328681cde2469306e3f6f7e519375e04a600?d=identicon)[idoneo](/maintainers/idoneo)

---

Top Contributors

[![diego-mascarenhas](https://avatars.githubusercontent.com/u/1038571?v=4)](https://github.com/diego-mascarenhas "diego-mascarenhas (51 commits)")

---

Tags

spatielaravelcmslivewiremulti-tenantfilamentjetstreamTeams

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/idoneo-cms-core/health.svg)

```
[![Health](https://phpackages.com/badges/idoneo-cms-core/health.svg)](https://phpackages.com/packages/idoneo-cms-core)
```

###  Alternatives

[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[spatie/livewire-filepond

Upload files using Filepond in Livewire components

306452.7k3](/packages/spatie-livewire-filepond)[a2insights/filament-saas

Filament Saas for A2Insights

161.1k](/packages/a2insights-filament-saas)[riodwanto/superduper-filament-starter-kit

A comprehensive Laravel Filament 3 💡 starter kit with pre-installed plugins, admin panel, user management, SEO tools, theme customization, and content management for rapid application development

2708.5k](/packages/riodwanto-superduper-filament-starter-kit)[andreia/filament-ui-switcher

Add a modal with options to switch between different UI layouts and styles (colors, fonts, font sizes).

233.8k](/packages/andreia-filament-ui-switcher)[tapp/filament-form-builder

User facing form builder using Filament components

131.2k1](/packages/tapp-filament-form-builder)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
