avris/esse

The essential CMS

Maintainers

Details

gitlab.com/Avris/Esse

Installs: 288

Dependents: 0

Suggesters: 0

Security: 0

Type:symfony-bundle

v1.0.2 2020-05-10 15:28 UTC

This package is auto-updated.

Last update: 2022-11-10 21:05:23 UTC


README

A filesystem-based, git-oriented CMS focusing only on what is (deemed by me as) "essential".

Installation

composer require avris/esse

Enable the bundle in config/bundles.php:

Avris\Esse\AvrisEsseBundle::class => ['all' => true],

config/routes.yaml:

esse_image:
    path: '/image/{filename}_{size}.{extension}'
    requirements: { filename:'.+' }
    controller: 'Avris\Esse\Controller\EsseController::image'
esse_file:
    path: '/file/{filename}.{extension}'
    requirements: { filename:'.+' }
    controller: 'Avris\Esse\Controller\EsseController::file'

.gitignore:

/public/image

(Optionally) overwrite default config in config/packages/avris_esse:

avris_esse:
    entriesDir: '%kernel.project_dir%/content/entries'
    imagesDir: '%kernel.project_dir%/content/images'
    filesDir: '%kernel.project_dir%/content/files'
    imageSizes:
        big: { maxwidth: 960 }
        small: { maxwidth: 480 }
        micro: { maxwidth: 36, maxheight: 36}

Usage

Basic concepts

Content is basically a set of entries (SUML files). They can be of different types:

  • block - just a generic string or an array
  • image
  • file
  • any custom type you want

They are put in the /content/entries, /content/images and /content/files directories, respectively. For instance, if you put the following content in /content/entries/about/skills.suml:

content:
    en:
        programming: 
            php: 'PHP'
            js: 'JavaScript'
        soft:
            teamwork: 'Teamwork'
    pl:
        soft:
            teamwork: 'Praca zespołowa'

Then you can fetch it like this:

public function foo(Esse $esse)
{
    return $this->render('home/foo.html.twig', [
        'skills' => $esse->get('about/skills'),
    ]);
}

You can also fetch a specific field inside of the content using $esse->getPart('about/skills.soft.teamwork'), or in a template: {{ esse('about/skills.soft.teamwork') }}. It will be automatically translated to the current request locale, using %locale% and then a generic "language" _ as a fallback.

You can optionally add any metadata you want (including type which defaults to block, image or file depending on which directory the file is in, and published which defaults to false):

type: `article`
createdAt: 2020-03-11 12:34:56
content: [] # ...

Those can be accessed with $entry->meta('createdAt').

Images

With an image put in /content/images/album.png the following content of /content/images/album.suml:

type: 'image'  # optional
filename: 'album.png'
alt: 'My photo album'
source: 'https://example.com/album.png'

Esse will give you that image with $esse->getImage('album') and under https://127.0.0.1:8000/image/album_sm.png it will serve the sm version of that image (as defined in config, under imageSizes).

Files

With an file put in /content/files/foo.txt the following content of /content/files/foo.suml:

type: 'file'  # optional
filename: 'foo.txt'
published: true  # optional
title: 'The Foo file'

Esse will give you that file with $esse->getFile('foo') and serve it under https://127.0.0.1:8000/file/file.txt.

Modifiers

You can implement Avris\Esse\Interfaces\EsseModifier to modify any entry before it gets served by Esse, for example:

<?php

namespace App\Article;

use App\Service\ArticleProcessor;
use Avris\Esse\Entity\Entry;
use Avris\Esse\Interfaces\EsseModifier;

final class ArticleModifier implements EsseModifier
{
    private ArticleProcessor $articleProcessor;

    public function __construct(ArticleProcessor $articleProcessor)
    {
        $this->articleProcessor = $articleProcessor;
    }

    public function modifyEntry(Entry $entry): ?Entry
    {
        if ($entry->type()->toString() !== 'article') {
            return $entry;
        }

        if (!$entry->published() || $entry->meta('publishedAt') > new \DateTimeImmutable()) {
            return null;
        }

        $data = $entry->allMeta();

        $data['content'] = [];

        foreach ($entry->versions() as $version) {
            $data['content'][$version] = $this->articleProcessor->process($entry->content($version));
        }

        return $entry->with($data);
    }
}

Indexes

You can implement Avris\Esse\Interfaces\EsseIndex to create a cached index of entries, for example:

<?php
namespace App\Article;

use Avris\Esse\Interfaces\EsseIndex;

final class TagIndex implements EsseIndex
{
    public function id(): string
    {
        return 'tag';
    }

    public function build(iterable $rawFiles): array
    {
        $index = [];

        foreach ($rawFiles as $key => $data) {
            if (($data['type'] ?? null) !== 'article') {
                continue;
            }

            foreach ($data['content'] ?? [] as $version => $content) {
                foreach ($content['tags'] ?? [] as $tag) {
                    $tag = mb_strtolower($tag);
                    if (!isset($index[$tag])) {
                        $index[$tag] = [];
                    }
                    $index[$tag][] = $key;
                }
            }
        }

        return $index;
    }
}

Example usage:

/**
 * @Route("/tag/{tag}")
 */
public function tag(string $tag, Esse $esse)
{
    return $this->renderFormat('tag', [
        'articles' => $esse->fromIndex('tag', mb_strtolower($tag)),
    ]);
}

Copyright