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.
BlogBundleclassChronicles 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
PendingCommentsWidgetTypeclassThe 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
ImportWordPressCommandclassA 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
CommentCrudControllerclassModerating the comments: what waits, what is online, what Akismet held.
9 methods
PostCrudControllerclassWriting 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
BlogControllerclassThe 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
CommentControllerclassA 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
BlogConfigurationclass1 method
BlogExtensionclass2 methods
PostclassA 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
SitemapListenerclassThe 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
PostRepositoryclass11 methods
BlogPageclassThe 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
BlogScreensclassThe 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
BookPageclassThe visitors' book: the comments left on no post, and whether one may write.
3 methods · 1 property
FeedPageclassThe 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
PostPageclassOne 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
AkismetReporterclassTeaching 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
CommentNotifierclassThe 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
CommentPolicyclassWho 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
JsonLdclassA 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
BlogExtensionclassWhat 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
CleanerclassWhat 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
ClientclassA 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
ContextclassThe 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
ImporterclassA 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
ItemclassA 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
LinkRewriterclassThe 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
ResultclassWhat a target did with an item, and the run's count of each.
2 methods · 3 properties · 4 constants
TargetInterfaceinterfaceWhere 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
PostTargetclassA 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
SkipTargetclassLeft 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(): stringModern layout: the class lives in src/, the bundle root is the package root.
BlogBundle::build()
public function build(ContainerBuilder $container): voidBase\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(): stringPendingCommentsWidgetType::getTemplate()
public function getTemplate(): stringPendingCommentsWidgetType::getTemplateVars()
public function getTemplateVars(MenuItem $widget): arrayBase\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): voidCommentCrudController::getEntityFqcn()
public static function getEntityFqcn(): stringCommentCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringCommentCrudController::configureFilters()
public function configureFilters(Filters $filters): FiltersCommentCrudController::configureFields()
public function configureFields(string $pageName): iterableCommentCrudController::configureActions()
public function configureActions(Actions $actions): ActionsCommentCrudController::approve()
public function approve(string $entityId): ResponseCommentCrudController::spam()
public function spam(string $entityId): ResponseCommentCrudController::trash()
public function trash(string $entityId): ResponsePostCrudController 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(): stringPostCrudController::getPreferredIcon()
public static function getPreferredIcon(): ?stringPostCrudController::configureFilters()
public function configureFilters(Filters $filters): FiltersPostCrudController::configureFields()
public function configureFields(string $pageName): iterablePostCrudController::createEntity()
public function createEntity(string $entityFqcn): objectBase\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): arrayThe 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): ResponseBlogController::month()
public function month(Request $request, int $year, int $month): ResponseBlogController::feed()
public function feed(Request $request): ResponseBlogController::post()
public function post(Request $request, string $slug): ResponseCommentController 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): ResponseThe 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): ResponseCommentController::commentPost()
public function commentPost(Request $request, string $slug): ResponseCommentController::form()
public function form(?Post $post, ?CommentModel $model = null): FormInterfaceThe form a page embeds (the Twig function blog_comment_form(), @Blog/client/_comments.html.twig).
CommentController::isOpen()
public function isOpen(?Post $post): boolCommentController::canReply()
public function canReply(): boolAnswering 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(): TreeBuilderBlogExtension class
class BlogExtension extends AbstractBaseExtension BlogExtension.php:11
BlogExtension::getConfiguration()
public function getConfiguration(array $config, ContainerBuilder $container): BlogConfigurationBlogExtension::load()
public function load(array $configs, ContainerBuilder $container): voidBase\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 = 220Words read in a minute, for the reading time.
Post::__iconizeStatic()
public static function __iconizeStatic(): ?arrayPost::__toLink()
public function __toLink(array $routeParameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): ?stringPost::__construct()
public function __construct(?\Base\Entity\User $owner = null, ?Thread $parent = null, ?string $title = null, ?string $slug = null)Post::__toString()
public function __toString(): stringPost::getCover()
public function getCover(): ?stringPost::getCoverFile()
public function getCoverFile(): ?FilePost::setCover()
public function setCover($cover): selfPost::hasCover()
public function hasCover(): boolWhether a cover is set, without asking the storage where it is.
Post::getCoverUrl()
public function getCoverUrl(): ?stringThe cover's address on the site ("/uploads/…"): the storage's public path without the public directory.
Post::isFeatured()
public function isFeatured(): boolPost::setFeatured()
public function setFeatured(bool $featured): selfPost::isCommentsOpen()
public function isCommentsOpen(): boolPost::setCommentsOpen()
public function setCommentsOpen(bool $open): selfPost::getReadingMinutes()
public function getReadingMinutes(): intMinutes to read it, rounded up, one at least.
Post::publish()
public function publish(bool $published = true, ?\DateTimeInterface $at = null): selfPublish 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): voidBase\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): arrayA 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): intPostRepository::findAllPublished()
public function findAllPublished(): array- Returns
every published post, newest first: for a sitemap
PostRepository::findLatest()
public function findLatest(int $limit = 3): arrayPostRepository::findHeadline()
public function findHeadline(): ?PostThe featured post, or the newest one.
PostRepository::findOnePublished()
public function findOnePublished(string $slug): ?PostPostRepository::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(): arrayThe 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): arrayA 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 $pagepublic int $pagespublic int $foundpublic ?string $nextpublic array $articlespublic ?string $prevpublic ?Card $headlinepublic array $topicspublic ?string $topicpublic ?string $taxonpublic ?string $monthpublic array $monthspublic ?string $indexUrlpublic ?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(): arrayBlogPage::jsonSerialize()
public function jsonSerialize(): arrayBlogScreens 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 = 800const HEADLINE = 1200const 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): BlogPageA 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): BlogPageThe posts of one month.
BlogScreens::post()
public function post(Post $post, ?string $base = null): PostPageA 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(): BookPageThe visitors' book.
BlogScreens::feed()
public function feed(string $host): FeedPageThe 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): CardA post on a list, its cover at $width.
BlogScreens::discussion()
public function discussion(?Post $post, ?iterable $comments = null): DiscussionThe 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(): arrayBookPage::jsonSerialize()
public function jsonSerialize(): arrayFeedPage 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 $titlepublic string $indexUrlpublic string $feedUrlpublic array $items
FeedPage::__construct()
public function __construct(public string $title, public string $indexUrl, public string $feedUrl, public array $items)FeedPage::toArray()
public function toArray(): arrayFeedPage::jsonSerialize()
public function jsonSerialize(): arrayPostPage 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 $articlepublic ?Link $prevpublic ?Link $nextpublic Discussion $discussionpublic ?Link $connexpublic ?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(): arrayPostPage::jsonSerialize()
public function jsonSerialize(): arrayBase\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): boolAkismetReporter::reportHam()
public function reportHam(Comment $comment): boolCommentNotifier 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): voidCommentPolicy 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): boolCommentPolicy::canReply()
public function canReply(bool $signedIn): boolJsonLd 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$urlthe post's absolute address
$basethe 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(): arrayBase\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): stringClient 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$typeposts, 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 $sitepublic readonly bool $updatepublic readonly array $optionsitem 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)$optionsitem slug => what follows the target's name in the map
Context::option()
public function option(Item $item): ?stringContext::warn()
public function warn(string $message): voidImporter 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(): arrayImporter::read()
public function read(string $site): arrayEvery 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$mapan item's slug or path ("about-me/biography"), or "*post" / "*page" => "target" or "target:option"
$folderthe 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 $idpublic readonly string $typepublic readonly string $slugpublic readonly string $titlepublic string $contentpublic readonly ?string $excerptpublic readonly \DateTimeImmutable $datepublic readonly ?\DateTimeImmutable $modifiedpublic readonly string $statuspublic readonly string $linkpublic readonly int $parentpublic readonly array $ancestorsthe parents' slugs, the root first ("about-me" for /about-me/biography)
public readonly array $categoriestheir names
their names
public ?string $coverthe 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 $localepublic readonly int $featuredMediathe 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)$categoriestheir names
$tagstheir names
$ancestorsthe parents' slugs, the root first ("about-me" for /about-me/biography)
$coverthe 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
$featuredMediathe 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(): boolItem::text()
public function text(): stringThe 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 = '/')$mediaan upload's URL on the old site (without its size suffix) => its new URL
$byIda post's or a page's id => its new URL
$byPatha permalink's path ("about-me/biography") => its new URL
LinkRewriter::rewrite()
public function rewrite(string $html): stringLinkRewriter::to()
public function to(string $url): ?stringThe new address of an old one, or null to leave it.
LinkRewriter::mediaKey()
public static function mediaKey(string $url): stringAn 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): voidResult::count()
public function count(string $result, ?string $target = null): intTargetInterface 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(): stringTargetInterface::url()
public function url(Item $item): ?stringIts 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): stringWrite 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(): stringPostTarget::url()
public function url(Item $item): ?stringPostTarget::import()
public function import(Item $item, Context $context): stringSkipTarget 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(): stringSkipTarget::url()
public function url(Item $item): ?stringSkipTarget::import()
public function import(Item $item, Context $context): string