Skip to content

Commit 417d480

Browse files
committed
Sitemap generator
1 parent a1625c6 commit 417d480

8 files changed

+582
-24
lines changed

README.md

Lines changed: 173 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,30 @@
55
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/statikbe/laravel-filament-flexible-content-block-pages/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/statikbe/laravel-filament-flexible-content-block-pages/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
66
[![Total Downloads](https://img.shields.io/packagist/dt/statikbe/laravel-filament-flexible-content-block-pages.svg?style=flat-square)](https://packagist.org/packages/statikbe/laravel-filament-flexible-content-block-pages)
77

8-
A simple content page management system with a flexible content block builder based on the [Filament Flexible Content Blocks](https://github.com/statikbe/laravel-filament-flexible-content-blocks).
9-
10-
This package aims to provide a basic, batteries-included CMS for Filament by providing page creation in Filament and
11-
renders web pages that can be easily extended and styled.
12-
13-
Other features that will be provided:
14-
- Pages with hero, slugs, content blocks, publication options and SEO fields.
15-
- Website: routing, blade views, CSS themes included.
16-
- Menu builder with customisable blade templates
17-
- Extendable settings model and Filament resource to store CMS settings and images.
18-
- Redirect support for when slugs are renamed
19-
- Sitemap generation
20-
- A ready-to-use, extendable Filament panel with all CMS features implemented.
21-
- Extendable models, resources and database tables.
22-
- A simple asset manager (TODO)
23-
- Re-usable content blocks (TODO)
24-
- Contact form (TODO)
25-
26-
This package combines several existing packages and is therefore quite opinionated.
8+
A complete CMS solution for Laravel applications built on [Filament Flexible Content Blocks](https://github.com/statikbe/laravel-filament-flexible-content-blocks). This package extends the flexible content block system into a full page management solution with routing, SEO, menus, and multilingual support.
9+
10+
Designed for developers who need a content management system that integrates seamlessly with existing Laravel applications while providing editors with an intuitive interface for managing pages and content.
11+
12+
## Key Features
13+
14+
- **Flexible page management** - Create pages with hero images, flexible content blocks, SEO fields, and publication controls
15+
- **Hierarchical menu builder** - Configurable depth with drag-and-drop interface for creating navigation menus
16+
- **Multilingual support** - Full localization with automatic route generation for multiple languages
17+
- **SEO tools** - Automatic sitemap generation, meta tag management, and URL redirect handling
18+
- **Ready-to-use admin interface** - Pre-configured Filament panel with all resources and management tools
19+
- **Developer-friendly** - Extendable models, customizable templates, and comprehensive configuration options
20+
- **Content organization** - Tag system, hierarchical page structure, and settings management
21+
22+
## Additional Features
23+
24+
- Website routing with customizable URL patterns
25+
- Blade view components and CSS themes
26+
- Media library integration via Spatie packages
27+
- Automatic 301 redirects when page URLs change
28+
- Multiple sitemap generation methods (manual, crawling, hybrid)
29+
- Configurable content block types and layouts
30+
31+
This package combines several Laravel packages into a cohesive CMS solution, making it opinionated but comprehensive for typical content management needs.
2732

2833
## Installation
2934

@@ -79,7 +84,7 @@ Publish the config and change the tag model to the package model:
7984
]
8085
```
8186

82-
Check [the configuration documentation}(#configuration) for more explanations on how to tweak the package.
87+
Check [the configuration documentation](#configuration) for more explanations on how to tweak the package.
8388

8489
Optionally, you can publish the views using
8590

@@ -202,6 +207,150 @@ The style can also be configured in the database model, then you can skip the `s
202207

203208
See the [menu seeding documentation](documentation/seeders.md) for programmatic menu creation.
204209

210+
## Sitemap Generator
211+
212+
The package includes an automatic sitemap generator that creates XML sitemaps for your website with support for multilingual sites and various content types.
213+
214+
### Features
215+
216+
- **Multiple generation methods** - Manual, crawling, or hybrid approach
217+
- **Multilingual support** - Automatic hreflang tags for alternate language versions
218+
- **Smart priority calculation** - Homepage gets priority 1.0, parent pages 0.8, child pages 0.6
219+
- **Dynamic change frequency** - Based on last modification date (weekly, monthly, yearly)
220+
- **Flexible content inclusion** - Pages, routes, linkable models, and custom URLs
221+
- **URL exclusion patterns** - Skip specific URLs or patterns from the sitemap
222+
- **SEO optimization** - Includes last modification dates and proper XML structure
223+
224+
### Configuration
225+
226+
Enable and configure the sitemap generator in your config file:
227+
228+
```php
229+
// config/filament-flexible-content-block-pages.php
230+
'sitemap' => [
231+
'enabled' => true,
232+
'generator_service' => \Statikbe\FilamentFlexibleContentBlockPages\Services\SitemapGeneratorService::class,
233+
'method' => SitemapGeneratorMethod::MANUAL, // MANUAL, CRAWL, or HYBRID
234+
'include_pages' => true,
235+
'include_link_routes' => true,
236+
'include_linkable_models' => true,
237+
'exclude_patterns' => [
238+
'/admin/*',
239+
'/api/*',
240+
],
241+
'custom_urls' => [
242+
'https://example.com/special-page',
243+
],
244+
],
245+
```
246+
247+
### Generation Methods
248+
249+
**Manual** (`SitemapGeneratorMethod::MANUAL`):
250+
- Generates sitemap based on database content (pages, routes, models)
251+
- Faster and more predictable
252+
- Best for most use cases
253+
254+
**Crawl** (`SitemapGeneratorMethod::CRAWL`):
255+
- Crawls your website to discover URLs
256+
- May find URLs not in your database
257+
- Slower but comprehensive
258+
259+
**Hybrid** (`SitemapGeneratorMethod::HYBRID`):
260+
- Combines both approaches
261+
- Crawls first, then adds manual entries
262+
- Most comprehensive but slowest
263+
264+
### Usage
265+
266+
Generate the sitemap manually:
267+
268+
```bash
269+
php artisan flexible-content-block-pages:generate-sitemap
270+
```
271+
272+
The sitemap will be saved to `public/sitemap.xml` and can be accessed at `https://yoursite.com/sitemap.xml`.
273+
274+
### Automatic Generation
275+
276+
You can schedule automatic sitemap generation in your `routes/console.php`:
277+
278+
```php
279+
$schedule->command('flexible-content-block-pages:generate-sitemap')
280+
->daily()
281+
->at('03:00');
282+
```
283+
284+
### Linkable Models
285+
286+
To include your own models in the sitemap, ensure they implement [the `Linkable` contract and have a `getViewUrl()` method](https://github.com/statikbe/laravel-filament-flexible-content-blocks#linkable).
287+
288+
You will most likely already have added those models to the menu configuration's `linkable_models` array or
289+
[call-to-actions models](https://github.com/statikbe/laravel-filament-flexible-content-blocks#linkable) then they will automatically be included in the sitemap.
290+
291+
If you do not want your model in menus or call-to-actions, you can extend the [SitemapGeneratorService](src/Services/SitemapGeneratorService.php).
292+
293+
### Extending the SitemapGeneratorService
294+
295+
For full customization power, you can create your own sitemap generator service by extending the base class:
296+
297+
```php
298+
<?php
299+
300+
namespace App\Services;
301+
302+
use Statikbe\FilamentFlexibleContentBlockPages\Services\SitemapGeneratorService;
303+
304+
class CustomSitemapGeneratorService extends SitemapGeneratorService
305+
{
306+
protected function addCustomUrls(): void
307+
{
308+
parent::addCustomUrls();
309+
310+
// Add your custom logic
311+
$this->addToSitemap(
312+
url: 'https://example.com/dynamic-page',
313+
lastModifiedAt: now(),
314+
priority: 0.7,
315+
frequency: 'weekly'
316+
);
317+
}
318+
319+
protected function getLinkableModels(): array
320+
{
321+
$models = parent::getLinkableModels();
322+
323+
// Add additional models not in menu/CTA config
324+
$models[] = \App\Models\BlogPost::class;
325+
$models[] = \App\Models\Event::class;
326+
327+
return $models;
328+
}
329+
330+
protected function calculatePriority($page): float
331+
{
332+
// Custom priority logic
333+
if ($page->is_featured) {
334+
return 0.9;
335+
}
336+
337+
return parent::calculatePriority($page);
338+
}
339+
}
340+
```
341+
342+
Then update your configuration to use your custom service:
343+
344+
```php
345+
// config/filament-flexible-content-block-pages.php
346+
'sitemap' => [
347+
'generator_service' => \App\Services\CustomSitemapGeneratorService::class,
348+
// ... other config
349+
],
350+
```
351+
352+
You can override any protected method to customize the sitemap generation behavior, including priority calculation, change frequency, URL filtering, or adding entirely new content types.
353+
205354
## Configuration
206355

207356
TODO
@@ -213,10 +362,12 @@ TODO
213362
- undeletable page toggle only for permission holder
214363
- redirect controller
215364
- tag controller
216-
- sitemap implementation
217-
- asset manager install in panel
218365
- orm listeners for linkable models that are in a menu to avoid accidental deletion.
366+
- page delete modal when page used in menu
219367
- frontend caching for menus
368+
- A simple asset manager
369+
- Re-usable content blocks
370+
- Contact form
220371

221372
## Changelog
222373

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
"artesaos/seotools": "^1.3",
2121
"filament/spatie-laravel-tags-plugin": "^3.2",
2222
"guava/filament-icon-picker": "^2.0",
23-
"illuminate/contracts": "^10.0||^11.0||^12.0",
23+
"illuminate/contracts": "^11.0||^12.0",
2424
"mcamara/laravel-localization": "^2.3",
2525
"solution-forest/filament-tree": "^2.1",
2626
"spatie/laravel-missing-page-redirector": "^2.11",
2727
"spatie/laravel-package-tools": "^1.16",
28+
"spatie/laravel-sitemap": "^7.3",
2829
"statikbe/laravel-filament-flexible-content-blocks": "dev-main"
2930
},
3031
"require-dev": {

config/filament-flexible-content-block-pages.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Illuminate\Session\Middleware\StartSession;
1313
use Illuminate\View\Middleware\ShareErrorsFromSession;
1414
use Statikbe\FilamentFlexibleContentBlockPages\FilamentFlexibleContentBlockPagesConfig;
15+
use Statikbe\FilamentFlexibleContentBlockPages\Services\Enum\SitemapGeneratorMethod;
1516

1617
return [
1718
'models' => [
@@ -125,4 +126,19 @@
125126
// 'breadcrumb',
126127
],
127128
],
129+
130+
'sitemap' => [
131+
'enabled' => true,
132+
'generator_service' => \Statikbe\FilamentFlexibleContentBlockPages\Services\SitemapGeneratorService::class,
133+
'method' => SitemapGeneratorMethod::MANUAL,
134+
'include_pages' => true,
135+
'include_link_routes' => true,
136+
'include_linkable_models' => true,
137+
'exclude_patterns' => [
138+
// URL patterns to exclude from sitemap
139+
],
140+
'custom_urls' => [
141+
// Custom URLs to include in sitemap
142+
],
143+
],
128144
];
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Statikbe\FilamentFlexibleContentBlockPages\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Statikbe\FilamentFlexibleContentBlockPages\Facades\FilamentFlexibleContentBlockPages;
7+
8+
class GenerateSitemapCommand extends Command
9+
{
10+
/**
11+
* The name and signature of the console command.
12+
*
13+
* @var string
14+
*/
15+
protected $signature = 'flexible-content-block-pages:generate-sitemap';
16+
17+
/**
18+
* The console command description.
19+
*
20+
* @var string
21+
*/
22+
protected $description = 'Generate the sitemap.';
23+
24+
/**
25+
* Execute the console command.
26+
*/
27+
public function handle(): int
28+
{
29+
if (! FilamentFlexibleContentBlockPages::config()->isSitemapEnabled()) {
30+
$this->info('Sitemap generation is disabled in configuration.');
31+
32+
return Command::SUCCESS;
33+
}
34+
35+
$this->info('Generating sitemap...');
36+
37+
try {
38+
$generatorServiceClass = FilamentFlexibleContentBlockPages::config()->getSitemapGeneratorService();
39+
$generator = app($generatorServiceClass);
40+
41+
$generator->generate();
42+
43+
$this->info('Sitemap generated successfully at: '.public_path('sitemap.xml'));
44+
} catch (\Exception $e) {
45+
$this->error('Failed to generate sitemap: '.$e->getMessage());
46+
47+
return Command::FAILURE;
48+
}
49+
50+
return Command::SUCCESS;
51+
}
52+
}

src/FilamentFlexibleContentBlockPagesConfig.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Statikbe\FilamentFlexibleContentBlockPages\Models\TagType;
1313
use Statikbe\FilamentFlexibleContentBlockPages\Routes\Contracts\HandlesPageRoutes;
1414
use Statikbe\FilamentFlexibleContentBlockPages\Routes\LocalisedPageRouteHelper;
15+
use Statikbe\FilamentFlexibleContentBlockPages\Services\Enum\SitemapGeneratorMethod;
1516
use Statikbe\FilamentFlexibleContentBlocks\FilamentFlexibleContentBlocksServiceProvider;
1617

1718
class FilamentFlexibleContentBlockPagesConfig
@@ -259,6 +260,46 @@ public function getMorphMap(): array
259260
];
260261
}
261262

263+
public function isSitemapEnabled(): bool
264+
{
265+
return $this->packageConfig('sitemap.enabled', true);
266+
}
267+
268+
public function getSitemapGeneratorService(): string
269+
{
270+
return $this->packageConfig('sitemap.generator_service', \Statikbe\FilamentFlexibleContentBlockPages\Services\SitemapGeneratorService::class);
271+
}
272+
273+
public function getSitemapMethod(): SitemapGeneratorMethod
274+
{
275+
return $this->packageConfig('sitemap.method', SitemapGeneratorMethod::MANUAL);
276+
}
277+
278+
public function shouldIncludePagesInSitemap(): bool
279+
{
280+
return $this->packageConfig('sitemap.include_pages', true);
281+
}
282+
283+
public function shouldIncludeLinkRoutesInSitemap(): bool
284+
{
285+
return $this->packageConfig('sitemap.include_link_routes', true);
286+
}
287+
288+
public function shouldIncludeLinkableModelsInSitemap(): bool
289+
{
290+
return $this->packageConfig('sitemap.include_linkable_models', true);
291+
}
292+
293+
public function getSitemapExcludePatterns(): array
294+
{
295+
return $this->packageConfig('sitemap.exclude_patterns', []);
296+
}
297+
298+
public function getSitemapCustomUrls(): array
299+
{
300+
return $this->packageConfig('sitemap.custom_urls', []);
301+
}
302+
262303
private function packageConfig(string $configKey, $default = null): mixed
263304
{
264305
return config('filament-flexible-content-block-pages.'.$configKey, $default);

0 commit comments

Comments
 (0)