Skip to content

omnibase/blog API reference

Floor
Prepared structure · Omnibase
Version
main
Verified for real
Its README does not say
Licence
MIT
composer require omnibase/blog:dev-main

Read from the package's own PHP files at the commit 3ba70fd60485: its namespaces, classes, interfaces and enums, their public constants, properties and methods with their PHPDoc, and the options its factories take. Nothing of it is run.

Base\Blog

  • BlogBundle class

    Chronicles and comments on top of omnibase's Thread. A Post IS a Thread (JOINED subclass); its comments are glitchr/omnibase's Base\Entity\Thread\Comment, read by Akismet.

    3 methods

Base\Blog\Admin\Widget

  • PendingCommentsWidgetType class

    The dashboard's tile: how many comments wait, the last few of them, each opening the comments screen. `yield MenuItem::block('blog_pending_comments', ...)` in the dashboard's configureWidgetItems() places it.

    4 methods

Base\Blog\Command

  • ImportWordPressCommand class

    A WordPress site moved in through its REST API (wp-json/wp/v2): the posts become blog posts, each page goes where the map sends it - a site's own targets (a biography, a page, the agenda, a service) or nowhere -, the pictures are copied, the links rewritten.

    1 method

Base\Blog\Controller\Admin\Crud

  • CommentCrudController class

    Moderating the comments: what waits, what is online, what Akismet held.

    9 methods

  • PostCrudController class

    Writing a chronicle: a title, a line under it, the preview shown on the list, the text (EditorJS, autosaved), its subjects (tags), its cover.

    5 methods

Base\Blog\Controller\Client

  • BlogController class

    The chronicles: the posts a page at a time, by topic (a tag) or by what the site classifies them with (a taxon: a level, a subject), by month, one post with its comments, and the RSS feed. The first segment is blog.path ("chroniques" by default).

    6 methods

  • CommentController class

    A comment left on a post (/chroniques/{slug}/commenter) or in the visitors' book (/livre-d-or): the guard first (trap, speed, flood), then the form - which asks Akismet through omnibase's FormTypeSpamExtension -, then the administrators told. The visitor lands back on the page, at the comment or at a word saying it waits.

    7 methods

Base\Blog\DependencyInjection

Base\Blog\Entity

  • Post class

    A chronicle: an article, a tutorial, a note from the classroom. An omnibase thread - title, headline, excerpt and text translated, slug, publication state and date, owners (the author), tags (its subjects), taxa (a level, a discipline, when the site has them) - written in the back office (Controller\Admin\Crud\PostCrudController), with a cover, a "featured" flag and the reading time on top.

    15 methods · 1 constant

Base\Blog\EventListener

  • SitemapListener class

    The posts' own pages in /sitemap.xml: omnibase lists the routes it can generate by itself (the blog's first page), and a post is behind a slug it cannot know. Each carries the day it was last changed, so a crawler comes back to the ones that move. blog.sitemap: false leaves them out.

    2 methods

Base\Blog\Repository

Base\Blog\Screen

  • BlogPage class

    The chronicles a page at a time (lesringostars' BlogPage: page, pages, found, next, articles), and what omnibase's blog shows around them: the newer page's address, the featured post on the first page, the topics to filter by (the one asked marked current), the topic or taxon asked (their slugs), the month of a month's page (its first day, ISO), the archive's years and months (newest first, each with its count and address), and the list's and the feed's addresses (indexUrl, feedUrl: the frame owns `links`).

    3 methods · 14 properties

  • BlogScreens class

    The blog's screens (plan "omnibase agnostique", C5): its pages as view models, from its repositories, omnibase's Stories builder, the Urls contract and the request's context. What its controllers hand to their templates; the Twig functions blog_card() and blog_discussion() give a host page the same models from its own entities.

    8 methods · 3 constants

  • BookPage class

    The visitors' book: the comments left on no post, and whether one may write.

    3 methods · 1 property

  • FeedPage class

    The RSS feed: the channel's title (the site's name, else the host it is asked at), the list's and the feed's absolute addresses (indexUrl, feedUrl: the frame owns `links`), the last posts (their absolute addresses).

    3 methods · 4 properties

  • PostPage class

    One chronicle (lesringostars' StoryPage: article, prev, next, connex; its comments in a Discussion): the post, the older one (prev) and the newer (next), no connex, its comments and whether one may write, and schema.org's BlogPosting (null on the page shown again with a form's errors).

    3 methods · 6 properties

Base\Blog\Service

  • AkismetReporter class

    Teaching the classifier: a comment it let through that was spam (submit-spam), one it held that was not (submit-ham). The classifier is the one glitchr/omnibase's SpamChecker asks - Akismet with the site's key (api.spam.akismet, from base.spam.akismet), or base.guard.classifier - told the comment as it was classified: its text, its author, the address and the browser it came from, the site's home page. Without a classifier nothing is sent; a provider's error is logged, not thrown at the moderator.

    3 methods

  • CommentNotifier class

    The administrators told of a new comment: in the bell of omnibase's notification centre, with a link to the comments screen. A comment that waits says so; one that went online says that.

    2 methods

  • CommentPolicy class

    Who may write a comment, and answer one (blog.comments.*): comments at all, then without an account when guest_allowed, and on a post that keeps them open; an answer takes an account unless guest_replies. Framework-free: the caller says whether someone is signed in (CommentController by Symfony's user, the screens by their viewer).

    3 methods

  • JsonLd class

    A post as schema.org reads it: an Article (BlogPosting) with its headline, its dates, its author, its cover and its subjects. Plain arrays: the template encodes them.

    1 method

Base\Blog\Twig

  • BlogExtension class

    What a host's own pages ask the blog: the last posts for a home page, the comments of a post or of the visitors' book, and the comment form to put under them. blog_card() and blog_discussion() give the view models the blog's partials read (plan "omnibase agnostique", C5) from a host page's own entities: `_card.html.twig` and `_meta.html.twig` with a `post`, `_comments.html.twig` with a `post` (null: the visitors' book) and its `comments`.

    2 methods

Base\Blog\WordPress

  • Cleaner class

    What a WordPress page's HTML carries that is not its text: the scripts and styles plugins add (share buttons, forms that need their own JavaScript), the shortcodes left unexpanded, the empty paragraphs, the classes and sizes of the theme.

    1 method

  • Client class

    A WordPress site's REST API (wp-json/wp/v2), read whole: every page of posts, pages, media, categories and tags, a hundred at a time, following X-WP-TotalPages. Nothing is written to the site; no key is needed for what a public site publishes.

    3 methods

  • Context class

    The run as a target sees it: whether an existing record is brought up to date (--update) or left alone, the site read, the options a target was given in the map ("schedule-2016=agenda:2016" gives "2016"), the author of what is created, and where to say what went wrong.

    3 methods · 5 properties

  • Importer class

    A WordPress site moved in: its posts and pages read from its REST API, each sent to a target (a blog post by default, anything a site declares through the map), their HTML cleaned of what plugins add, their pictures copied into the uploads (the featured one given to the target as the item's cover), their links rewritten - to the pictures copied, to the posts and pages imported (by permalink, ?p=, ?page_id=). Run it again with --update: what was imported is found again and brought up to date, never doubled.

    4 methods

  • Item class

    A post or a page of a WordPress site, as its REST API gives it (wp-json/wp/v2/posts, /pages): what an import target needs, the HTML already cleaned and its links already rewritten to where they lead now.

    4 methods · 18 properties · 2 constants

  • LinkRewriter class

    The old site's links, to where their targets are now: a picture of its uploads (wp-content/uploads/..., a thumbnail "-300x225" to its full size) to the copy imported, an imported post or page (by its permalink, by "?p=123" or "?page_id=123") to its new address, the old home page to the new one. A link to a page that was not imported is left to the old site.

    4 methods

  • Result class

    What a target did with an item, and the run's count of each.

    2 methods · 3 properties · 4 constants

  • TargetInterface interface

    Where an imported post or page goes: a blog post (`post`), nowhere (`skip`), or anything a site declares - a biography, a page of its own, an event, a service. A service implementing this is a target, named by getName(); `blog:import-wordpress --map slug=name` sends a page to it.

    3 methods

Base\Blog\WordPress\Target

  • PostTarget class

    A blog post (omnibase/blog's Post) under the same slug: its title, its excerpt, its text (its pictures copied into the site's uploads), its featured picture as its cover, its categories and tags as tags, published at its date (a draft stays a draft). Found again by its slug: left alone, or brought up to date with --update.

    4 methods

  • SkipTarget class

    Left behind: a template page, a form the new site does again, a page with nothing in it.

    3 methods

Base\Blog

BlogBundle class

class BlogBundle extends AbstractBaseBundle BlogBundle.php:13

Chronicles and comments on top of omnibase's Thread. A Post IS a Thread (JOINED subclass); its comments are glitchr/omnibase's Base\Entity\Thread\Comment, read by Akismet.

BlogBundle::__construct()

public function __construct()

BlogBundle::getPath()

public function getPath(): string

Modern layout: the class lives in src/, the bundle root is the package root.

BlogBundle::build()

public function build(ContainerBuilder $container): void

Base\Blog\Admin\Widget

PendingCommentsWidgetType class

final class PendingCommentsWidgetType implements DashboardWidgetTypeInterface PendingCommentsWidgetType.php:14

The dashboard's tile: how many comments wait, the last few of them, each opening the comments screen. yield MenuItem::block('blog_pending_comments', ...) in the dashboard's configureWidgetItems() places it.

PendingCommentsWidgetType::__construct()

public function __construct(readonly CommentRepository $comments)

PendingCommentsWidgetType::getName()

public static function getName(): string

PendingCommentsWidgetType::getTemplate()

public function getTemplate(): string

PendingCommentsWidgetType::getTemplateVars()

public function getTemplateVars(MenuItem $widget): array

Base\Blog\Command

ImportWordPressCommand class

final class ImportWordPressCommand extends Command ImportWordPressCommand.php:29

A WordPress site moved in through its REST API (wp-json/wp/v2): the posts become blog posts, each page goes where the map sends it - a site's own targets (a biography, a page, the agenda, a service) or nowhere -, the pictures are copied, the links rewritten.

bin/console blog:import-wordpress https://example.org bin/console blog:import-wordpress https://example.org --map about-me/biography=bio --map contact=skip --update bin/console blog:import-wordpress https://example.org --map map.json

--update brings what was imported before up to date; without it, what is there is left alone. Either way nothing is doubled.

ImportWordPressCommand::__construct()

public function __construct(readonly Importer $importer, readonly EntityManagerInterface $entityManager)

Base\Blog\Controller\Admin\Crud

CommentCrudController class

class CommentCrudController extends AbstractCrudController CommentCrudController.php:29

Moderating the comments: what waits, what is online, what Akismet held.

Three buttons on each - approve, spam, trash - and two that teach Akismet (a spam it let through, a comment it held wrongly).

CommentCrudController::setBlogServices()

public function setBlogServices(AkismetReporter $akismet): void

CommentCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

CommentCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

CommentCrudController::configureFilters()

public function configureFilters(Filters $filters): Filters

CommentCrudController::configureFields()

public function configureFields(string $pageName): iterable

CommentCrudController::configureActions()

public function configureActions(Actions $actions): Actions

CommentCrudController::approve()

public function approve(string $entityId): Response

CommentCrudController::spam()

public function spam(string $entityId): Response

CommentCrudController::trash()

public function trash(string $entityId): Response

PostCrudController class

class PostCrudController extends AbstractCrudController PostCrudController.php:25

Writing a chronicle: a title, a line under it, the preview shown on the list, the text (EditorJS, autosaved), its subjects (tags), its cover.

Published, it is on /chroniques at its date (a date to come schedules it).

PostCrudController::getEntityFqcn()

public static function getEntityFqcn(): string

PostCrudController::getPreferredIcon()

public static function getPreferredIcon(): ?string

PostCrudController::configureFilters()

public function configureFilters(Filters $filters): Filters

PostCrudController::configureFields()

public function configureFields(string $pageName): iterable

PostCrudController::createEntity()

public function createEntity(string $entityFqcn): object

Base\Blog\Controller\Client

BlogController class

class BlogController extends AbstractController BlogController.php:30

The chronicles: the posts a page at a time, by topic (a tag) or by what the site classifies them with (a taxon: a level, a subject), by month, one post with its comments, and the RSS feed. The first segment is blog.path ("chroniques" by default).

Each action hands a template and its view model (Base\Blog\Screen\*, plan "omnibase agnostique", C5) to omnibase's ScreenResponder: the templates read screen and frame alone, a front end gets the same data in JSON. The variables the pages had before (the entities) are passed as well, for the templates a site overrides (wineseller's post page): deprecated, gone in 4.0.

BlogController::__construct()

public function __construct(readonly PostRepository $posts, readonly CommentRepository $comments, readonly int $perPage = 12, readonly ?JsonLd $jsonLd = null, readonly ?BlogScreens $screens = null, readonly ?ScreenResponder $responder = null, readonly ?CommentController $commenting = null)

BlogController::legacy()

public static function legacy(\Twig\Environment $twig, string $template, \Closure $variables): array

The variables a page had before its view model (the entities), given only to a template the site overrides (templates/bundles/BlogBundle/...): the blog's own read the model alone, and the queries are not made twice.

BlogController::index()

public function index(Request $request): Response

BlogController::month()

public function month(Request $request, int $year, int $month): Response

BlogController::feed()

public function feed(Request $request): Response

BlogController::post()

public function post(Request $request, string $slug): Response

CommentController class

class CommentController extends AbstractController CommentController.php:33

A comment left on a post (/chroniques/{slug}/commenter) or in the visitors' book (/livre-d-or): the guard first (trap, speed, flood), then the form - which asks Akismet through omnibase's FormTypeSpamExtension -, then the administrators told. The visitor lands back on the page, at the comment or at a word saying it waits.

CommentController::__construct()

public function __construct(readonly PostRepository $posts, readonly CommentRepository $comments, readonly EntityManagerInterface $entityManager, readonly CommentGuard $guard, readonly CommentNotifier $notifier, readonly TranslatorInterface $translator, readonly bool $enabled = true, readonly bool $guestAllowed = true, readonly bool $guestReplies = false, readonly bool $autoApprove = true, readonly int $maxLength = 4000, readonly int $minDelay = 4, readonly int $floodInterval = 60, readonly ?\Google\Service\GrService $recaptcha = null, ?CommentPolicy $policy = null, readonly ?BlogScreens $screens = null, readonly ?ScreenResponder $responder = null)

CommentController::book()

public function book(Request $request): Response

The visitors' book: the comments with no post, and the form (a template and its view model, plan "omnibase agnostique", C5).

CommentController::commentBook()

public function commentBook(Request $request): Response

CommentController::commentPost()

public function commentPost(Request $request, string $slug): Response

CommentController::form()

public function form(?Post $post, ?CommentModel $model = null): FormInterface

The form a page embeds (the Twig function blog_comment_form(), @Blog/client/_comments.html.twig).

CommentController::isOpen()

public function isOpen(?Post $post): bool

CommentController::canReply()

public function canReply(): bool

Answering a comment: an account, unless blog.comments.guest_replies opens it to everyone.

Base\Blog\DependencyInjection

BlogConfiguration class

class BlogConfiguration extends AbstractBaseConfiguration BlogConfiguration.php:8

BlogConfiguration::getConfigTreeBuilder()

public function getConfigTreeBuilder(): TreeBuilder

BlogExtension class

class BlogExtension extends AbstractBaseExtension BlogExtension.php:11

BlogExtension::getConfiguration()

public function getConfiguration(array $config, ContainerBuilder $container): BlogConfiguration

BlogExtension::load()

public function load(array $configs, ContainerBuilder $container): void

Base\Blog\Entity

Post class

class Post extends Thread implements LinkableInterface Post.php:23

A chronicle: an article, a tutorial, a note from the classroom. An omnibase thread - title, headline, excerpt and text translated, slug, publication state and date, owners (the author), tags (its subjects), taxa (a level, a discipline, when the site has them) - written in the back office (Controller\Admin\Crud\PostCrudController), with a cover, a "featured" flag and the reading time on top.

const WORDS_PER_MINUTE = 220

Words read in a minute, for the reading time.

Post::__iconizeStatic()

public static function __iconizeStatic(): ?array

Post::__toLink()

public function __toLink(array $routeParameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): ?string

Post::__construct()

public function __construct(?\Base\Entity\User $owner = null, ?Thread $parent = null, ?string $title = null, ?string $slug = null)

Post::__toString()

public function __toString(): string

Post::getCover()

public function getCover(): ?string

Post::getCoverFile()

public function getCoverFile(): ?File

Post::setCover()

public function setCover($cover): self

Post::hasCover()

public function hasCover(): bool

Whether a cover is set, without asking the storage where it is.

Post::getCoverUrl()

public function getCoverUrl(): ?string

The cover's address on the site ("/uploads/…"): the storage's public path without the public directory.

Post::isFeatured()

public function isFeatured(): bool

Post::setFeatured()

public function setFeatured(bool $featured): self

Post::isCommentsOpen()

public function isCommentsOpen(): bool

Post::setCommentsOpen()

public function setCommentsOpen(bool $open): self

Post::getReadingMinutes()

public function getReadingMinutes(): int

Minutes to read it, rounded up, one at least.

Post::publish()

public function publish(bool $published = true, ?\DateTimeInterface $at = null): self

Publish it now (or at $at), or take it back to draft.

Base\Blog\EventListener

SitemapListener class

final class SitemapListener SitemapListener.php:16

The posts' own pages in /sitemap.xml: omnibase lists the routes it can generate by itself (the blog's first page), and a post is behind a slug it cannot know. Each carries the day it was last changed, so a crawler comes back to the ones that move. blog.sitemap: false leaves them out.

SitemapListener::__construct()

public function __construct(readonly PostRepository $posts, readonly bool $enabled = true)

SitemapListener::__invoke()

public function __invoke(SitemapEvent $event): void

Base\Blog\Repository

PostRepository class

class PostRepository extends ServiceEntityRepository PostRepository.php:13

PostRepository::__construct()

public function __construct(ManagerRegistry $registry)

PostRepository::findPublishedPage()

public function findPublishedPage(int $page = 1, int $perPage = 10, ?string $tag = null, ?string $taxon = null): array

A page of the published posts, newest first - of one topic when a tag slug is given, of one taxon (a level, a subject) when a taxon slug is.

Paged by hand: Doctrine's Paginator walks the SQL with an output walker omnibase's own SqlWalker is incompatible with.

PostRepository::countPublished()

public function countPublished(?string $tag = null, ?string $taxon = null): int

PostRepository::findAllPublished()

public function findAllPublished(): array
Returns

every published post, newest first: for a sitemap

PostRepository::findLatest()

public function findLatest(int $limit = 3): array

PostRepository::findHeadline()

public function findHeadline(): ?Post

The featured post, or the newest one.

PostRepository::findOnePublished()

public function findOnePublished(string $slug): ?Post

PostRepository::findNeighbours()

public function findNeighbours(Post $post): array
Returns

the older one, the newer one

PostRepository::findTopics()

public function findTopics(): array
Returns

the topics of the posts on the site: published, and their publication date come

PostRepository::countByMonth()

public function countByMonth(): array

The published posts, by year then month, for the chronicle's archive.

@return array<int, array<int, int>> year => month => count

PostRepository::findByMonth()

public function findByMonth(int $year, int $month): array

A page of the posts of one month.

@return list<Post>

Base\Blog\Screen

BlogPage class

final readonly class BlogPage implements Screen BlogPage.php:19

The chronicles a page at a time (lesringostars' BlogPage: page, pages, found, next, articles), and what omnibase's blog shows around them: the newer page's address, the featured post on the first page, the topics to filter by (the one asked marked current), the topic or taxon asked (their slugs), the month of a month's page (its first day, ISO), the archive's years and months (newest first, each with its count and address), and the list's and the feed's addresses (indexUrl, feedUrl: the frame owns links).

public int $page
public int $pages
public int $found
public ?string $next
public array $articles
public ?string $prev
public ?Card $headline
public array $topics
public ?string $topic
public ?string $taxon
public ?string $month
public array $months
public ?string $indexUrl
public ?string $feedUrl

BlogPage::__construct()

public function __construct(public int $page, public int $pages, public int $found, public ?string $next, public array $articles, public ?string $prev = null, public ?Card $headline = null, public array $topics = [], public ?string $topic = null, public ?string $taxon = null, public ?string $month = null, public array $months = [], public ?string $indexUrl = null, public ?string $feedUrl = null)

BlogPage::toArray()

public function toArray(): array

BlogPage::jsonSerialize()

public function jsonSerialize(): array

BlogScreens class

class BlogScreens BlogScreens.php:28

The blog's screens (plan "omnibase agnostique", C5): its pages as view models, from its repositories, omnibase's Stories builder, the Urls contract and the request's context. What its controllers hand to their templates; the Twig functions blog_card() and blog_discussion() give a host page the same models from its own entities.

Covers at the width each place shows them: a card 800, the headline 1200, a post 1600.

const CARD = 800
const HEADLINE = 1200
const POST = 1600

BlogScreens::__construct()

public function __construct(readonly PostRepository $posts, readonly CommentRepository $comments, readonly Stories $stories, readonly Urls $urls, readonly ContextProvider $context, readonly CommentPolicy $policy, readonly int $perPage = 12, readonly ?JsonLd $jsonLd = null)

BlogScreens::index()

public function index(int $page, ?string $topic = null, ?string $taxon = null): BlogPage

A page of the list, of a topic (a tag's slug) or a taxon's when one is asked.

BlogScreens::month()

public function month(int $year, int $month): BlogPage

The posts of one month.

BlogScreens::post()

public function post(Post $post, ?string $base = null): PostPage

A post, its neighbours and its comments; $base the site's scheme and host for schema.org's description, which only the page asked whole has (null: none, as on the page shown again with a form's errors).

BlogScreens::book()

public function book(): BookPage

The visitors' book.

BlogScreens::feed()

public function feed(string $host): FeedPage

The feed: the last twenty posts, by their absolute addresses; $host the host it is asked at, the channel's title for a site without a name.

BlogScreens::card()

public function card(Post $post, int $width = self::CARD): Card

A post on a list, its cover at $width.

BlogScreens::discussion()

public function discussion(?Post $post, ?iterable $comments = null): Discussion

The comments of a post, or of the visitors' book (null), and whether one may write; the comments given (a host's own list), else those online.

The messages left for the page are read (and so taken) here.

BookPage class

final readonly class BookPage implements Screen BookPage.php:9

The visitors' book: the comments left on no post, and whether one may write.

public Discussion $discussion

BookPage::__construct()

public function __construct(public Discussion $discussion)

BookPage::toArray()

public function toArray(): array

BookPage::jsonSerialize()

public function jsonSerialize(): array

FeedPage class

final readonly class FeedPage implements Screen FeedPage.php:14

The RSS feed: the channel's title (the site's name, else the host it is asked at), the list's and the feed's absolute addresses (indexUrl, feedUrl: the frame owns links), the last posts (their absolute addresses).

public string $title
public string $indexUrl
public string $feedUrl
public array $items

FeedPage::__construct()

public function __construct(public string $title, public string $indexUrl, public string $feedUrl, public array $items)

FeedPage::toArray()

public function toArray(): array

FeedPage::jsonSerialize()

public function jsonSerialize(): array

PostPage class

final readonly class PostPage implements Screen PostPage.php:16

One chronicle (lesringostars' StoryPage: article, prev, next, connex; its comments in a Discussion): the post, the older one (prev) and the newer (next), no connex, its comments and whether one may write, and schema.org's BlogPosting (null on the page shown again with a form's errors).

public Story $article
public ?Link $prev
public ?Link $next
public Discussion $discussion
public ?Link $connex
public ?array $jsonLd

PostPage::__construct()

public function __construct(public Story $article, public ?Link $prev, public ?Link $next, public Discussion $discussion, public ?Link $connex = null, public ?array $jsonLd = null)

PostPage::toArray()

public function toArray(): array

PostPage::jsonSerialize()

public function jsonSerialize(): array

Base\Blog\Service

AkismetReporter class

final class AkismetReporter AkismetReporter.php:21

Teaching the classifier: a comment it let through that was spam (submit-spam), one it held that was not (submit-ham). The classifier is the one glitchr/omnibase's SpamChecker asks - Akismet with the site's key (api.spam.akismet, from base.spam.akismet), or base.guard.classifier - told the comment as it was classified: its text, its author, the address and the browser it came from, the site's home page. Without a classifier nothing is sent; a provider's error is logged, not thrown at the moderator.

AkismetReporter::__construct()

public function __construct(readonly SpamChecker $spam, readonly ?LoggerInterface $logger = null)

AkismetReporter::reportSpam()

public function reportSpam(Comment $comment): bool

AkismetReporter::reportHam()

public function reportHam(Comment $comment): bool

CommentNotifier class

final class CommentNotifier CommentNotifier.php:18

The administrators told of a new comment: in the bell of omnibase's notification centre, with a link to the comments screen. A comment that waits says so; one that went online says that.

CommentNotifier::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly RoleHierarchyInterface $roles, readonly UrlGeneratorInterface $urls, readonly TranslatorInterface $translator)

CommentNotifier::commentLeft()

public function commentLeft(Comment $comment): void

CommentPolicy class

final class CommentPolicy CommentPolicy.php:15

Who may write a comment, and answer one (blog.comments.*): comments at all, then without an account when guest_allowed, and on a post that keeps them open; an answer takes an account unless guest_replies. Framework-free: the caller says whether someone is signed in (CommentController by Symfony's user, the screens by their viewer).

CommentPolicy::__construct()

public function __construct(readonly bool $enabled = true, readonly bool $guestAllowed = true, readonly bool $guestReplies = false)

CommentPolicy::isOpen()

public function isOpen(?Post $post, bool $signedIn): bool

CommentPolicy::canReply()

public function canReply(bool $signedIn): bool

JsonLd class

final class JsonLd JsonLd.php:12

A post as schema.org reads it: an Article (BlogPosting) with its headline, its dates, its author, its cover and its subjects. Plain arrays: the template encodes them.

JsonLd::article()

public function article(Post $post, string $url, string $base = ''): array
$url

the post's absolute address

$base

the site's scheme and host ("https://example.org"), put before the cover's path

Base\Blog\Twig

BlogExtension class

final class BlogExtension extends AbstractExtension BlogExtension.php:25

What a host's own pages ask the blog: the last posts for a home page, the comments of a post or of the visitors' book, and the comment form to put under them. blog_card() and blog_discussion() give the view models the blog's partials read (plan "omnibase agnostique", C5) from a host page's own entities: _card.html.twig and _meta.html.twig with a post, _comments.html.twig with a post (null: the visitors' book) and its comments.

BlogExtension::__construct()

public function __construct(readonly PostRepository $posts, readonly CommentRepository $comments, readonly CommentController $commentController, readonly ?BlogScreens $screens = null)

BlogExtension::getFunctions()

public function getFunctions(): array

Base\Blog\WordPress

Cleaner class

final class Cleaner Cleaner.php:11

What a WordPress page's HTML carries that is not its text: the scripts and styles plugins add (share buttons, forms that need their own JavaScript), the shortcodes left unexpanded, the empty paragraphs, the classes and sizes of the theme.

Cleaner::clean()

public function clean(string $html): string

Client class

final class Client Client.php:13

A WordPress site's REST API (wp-json/wp/v2), read whole: every page of posts, pages, media, categories and tags, a hundred at a time, following X-WP-TotalPages. Nothing is written to the site; no key is needed for what a public site publishes.

Client::__construct()

public function __construct(readonly HttpClientInterface $http)

Client::all()

public function all(string $site, string $type, array $query = []): array
$type

posts, pages, media, categories, tags

Client::api()

public static function api(string $site): string

"https://example.org" or "https://example.org/wp-json/wp/v2/" → the API's root, with its slash.

Context class

final class Context Context.php:11

The run as a target sees it: whether an existing record is brought up to date (--update) or left alone, the site read, the options a target was given in the map ("schedule-2016=agenda:2016" gives "2016"), the author of what is created, and where to say what went wrong.

public readonly string $site
public readonly bool $update
public readonly ?object $author
public readonly array $options

item slug => what follows the target's name in the map

public readonly ?Result $result

Context::__construct()

public function __construct(public readonly string $site, public readonly bool $update = false, public readonly ?object $author = null, public readonly array $options = [], public readonly ?Result $result = null)
$options

item slug => what follows the target's name in the map

Context::option()

public function option(Item $item): ?string

Context::warn()

public function warn(string $message): void

Importer class

class Importer Importer.php:21

A WordPress site moved in: its posts and pages read from its REST API, each sent to a target (a blog post by default, anything a site declares through the map), their HTML cleaned of what plugins add, their pictures copied into the uploads (the featured one given to the target as the item's cover), their links rewritten - to the pictures copied, to the posts and pages imported (by permalink, ?p=, ?page_id=). Run it again with --update: what was imported is found again and brought up to date, never doubled.

Importer::__construct()

public function __construct(readonly Client $client, readonly Cleaner $cleaner, readonly EntityManagerInterface $entityManager, readonly HttpClientInterface $http, iterable $targets = [], readonly ?FilesystemOperator $uploads = null)

Importer::getTargets()

public function getTargets(): array

Importer::read()

public function read(string $site): array

Every post and page of the site, cleaned (links not rewritten yet).

Importer::import()

public function import(string $site, array $map = [], bool $update = false, ?object $author = null, string $folder = 'wordpress', bool $media = true): Result
$map

an item's slug or path ("about-me/biography"), or "*post" / "*page" => "target" or "target:option"

$folder

the pictures' folder in the uploads

Item class

final class Item Item.php:10

A post or a page of a WordPress site, as its REST API gives it (wp-json/wp/v2/posts, /pages): what an import target needs, the HTML already cleaned and its links already rewritten to where they lead now.

const POST = 'post'
const PAGE = 'page'
public readonly int $id
public readonly string $type
public readonly string $slug
public readonly string $title
public string $content
public readonly ?string $excerpt
public readonly \DateTimeImmutable $date
public readonly ?\DateTimeImmutable $modified
public readonly string $status
public readonly int $parent
public readonly array $ancestors

the parents' slugs, the root first ("about-me" for /about-me/biography)

public readonly int $menuOrder
public readonly array $categories

their names

public readonly array $tags

their names

public ?string $cover

the featured picture's address once copied into the uploads ("/uploads/wordpress/2014/05/a.jpg"), set by the importer; null when the item has none or the media were not copied

public readonly ?string $locale
public readonly int $featuredMedia

the featured picture's id on the old site (featured_media), 0 for none

Item::__construct()

public function __construct(public readonly int $id, public readonly string $type, public readonly string $slug, public readonly string $title, public string $content, public readonly ?string $excerpt, public readonly \DateTimeImmutable $date, public readonly ?\DateTimeImmutable $modified, public readonly string $status, public readonly string $link, public readonly int $parent = 0, public readonly array $ancestors = [], public readonly int $menuOrder = 0, public readonly array $categories = [], public readonly array $tags = [], public ?string $cover = null, public readonly ?string $locale = null, public readonly int $featuredMedia = 0)
$categories

their names

$tags

their names

$ancestors

the parents' slugs, the root first ("about-me" for /about-me/biography)

$cover

the featured picture's address once copied into the uploads ("/uploads/wordpress/2014/05/a.jpg"), set by the importer; null when the item has none or the media were not copied

$featuredMedia

the featured picture's id on the old site (featured_media), 0 for none

Item::path()

public function path(): string

"about-me/biography": a page's path on the old site (its permalink's, else its parents'); a post's slug.

Item::isPublished()

public function isPublished(): bool

Item::text()

public function text(): string

The text without its markup, on one line.

LinkRewriter class

final class LinkRewriter LinkRewriter.php:12

The old site's links, to where their targets are now: a picture of its uploads (wp-content/uploads/..., a thumbnail "-300x225" to its full size) to the copy imported, an imported post or page (by its permalink, by "?p=123" or "?page_id=123") to its new address, the old home page to the new one. A link to a page that was not imported is left to the old site.

LinkRewriter::__construct()

public function __construct(readonly string $site, readonly array $media = [], readonly array $byId = [], readonly array $byPath = [], readonly string $home = '/')
$media

an upload's URL on the old site (without its size suffix) => its new URL

$byId

a post's or a page's id => its new URL

$byPath

a permalink's path ("about-me/biography") => its new URL

LinkRewriter::rewrite()

public function rewrite(string $html): string

LinkRewriter::to()

public function to(string $url): ?string

The new address of an old one, or null to leave it.

LinkRewriter::mediaKey()

public static function mediaKey(string $url): string

An upload's address without its host nor its size suffix: "2014/05/book-a.jpg".

Result class

final class Result Result.php:6

What a target did with an item, and the run's count of each.

const CREATED = 'created'
const UPDATED = 'updated'
const KEPT = 'kept'
const SKIPPED = 'skipped'
public array $counts = []
public array $warnings = []
public int $media = 0

Result::add()

public function add(string $target, string $result): void

Result::count()

public function count(string $result, ?string $target = null): int

TargetInterface interface

interface TargetInterface TargetInterface.php:13

Where an imported post or page goes: a blog post (post), nowhere (skip), or anything a site declares - a biography, a page of its own, an event, a service. A service implementing this is a target, named by getName(); blog:import-wordpress --map slug=name sends a page to it.

TargetInterface::getName()

public function getName(): string

TargetInterface::url()

public function url(Item $item): ?string

Its new address, before anything is written - the links of every other page are rewritten to it. Null: no page of its own.

TargetInterface::import()

public function import(Item $item, Context $context): string

Write it (persist, do not flush). Its content's links are rewritten already, its pictures copied.

Returns

one of Result::*

Base\Blog\WordPress\Target

PostTarget class

final class PostTarget implements TargetInterface PostTarget.php:26

A blog post (omnibase/blog's Post) under the same slug: its title, its excerpt, its text (its pictures copied into the site's uploads), its featured picture as its cover, its categories and tags as tags, published at its date (a draft stays a draft). Found again by its slug: left alone, or brought up to date with --update.

PostTarget::__construct()

public function __construct(readonly EntityManagerInterface $entityManager, readonly UrlGeneratorInterface $urls, readonly ?FilesystemOperator $uploads = null)

PostTarget::getName()

public function getName(): string

PostTarget::url()

public function url(Item $item): ?string

PostTarget::import()

public function import(Item $item, Context $context): string

SkipTarget class

final class SkipTarget implements TargetInterface SkipTarget.php:11

Left behind: a template page, a form the new site does again, a page with nothing in it.

SkipTarget::getName()

public function getName(): string

SkipTarget::url()

public function url(Item $item): ?string

SkipTarget::import()

public function import(Item $item, Context $context): string