Events
Blog\Service\BlogService triggers three events through its own Laminas event manager, whose identifiers are Blog and Blog\Service\BlogService. Each event carries a post_id parameter and the service as its target.
| Event | When |
|---|---|
blog.post.create |
After a post row and its tags are inserted (admin form and both APIs). |
blog.post.update |
After a post is updated. |
blog.post.delete |
Before the post row is deleted, so the post can still be read. |
Attach listeners from another module's attachListeners() or onBootstrap() through the shared event manager:
$sharedEvents->attach('Blog', 'blog.post.create', function (\Laminas\EventManager\Event $event) {
$postId = (int) $event->getParam('post_id');
$post = $event->getTarget()->getPost($postId); // array row
// index, notify, mirror...
});
src/Listener/PostEventListener.php is an example listener with stubs for a search index and a message queue. It is not attached by default.
Service
Blog\Service\BlogService (registered in the service manager under its class name) holds all database access and is the safest way to read blog data from another module or theme helper. Useful methods: listPosts(array $filters, int $page, int $perPage), countPosts(), getPost(), getPostBySlug(), getPostTags(), getPostSites(), listCategories(), listTags(), getPostComments($postId, 'approved'). Filters accepted by listPosts() are status, exclude_future, site_id, category_id, category_slug, tag_slug, author_id and search.
Other modules can detect the blog with class_exists('Blog\Module').
Database tables
| Table | Purpose |
|---|---|
blog_post |
Posts: title, slug (unique), excerpt, content, status (draft, pending, published), author_id, category_id, featured_image_asset_id, featured_image_url, published_at, created_at, updated_at. |
blog_category |
name, slug (unique), description. |
blog_tag |
name, slug (unique). |
blog_post_tag |
Post to tag links; rows are removed with the post or the tag. |
blog_post_site |
Post to site assignments; rows are removed with the post. A post with no rows is not public anywhere. |
blog_comment |
post_id, author_name, author_email, body, status (pending, approved, spam), posted_at, ip_hash (SHA-256 of the IP). Rows are removed with the post. |
author_id references user.id and category_id references blog_category.id without foreign keys; deleting a category sets category_id to NULL on its posts.
Settings keys
| Key | Scope | Value |
|---|---|---|
blog_posts_per_page |
global | integer |
blog_comments_enabled |
global | boolean |
blog_rss_enabled |
global | boolean |
blog_moderation_user_ids |
global | JSON array of user IDs |
blog_primary_color |
global | hex string, stored but not applied |
blog_bio |
user | HTML |
blog_profile_image_id |
user | asset ID |
Routes
Admin routes are named admin-blog, admin-blog/post, admin-blog/category, admin-blog/comment and admin-blog/settings. Site routes are children of site: site/blog, site/blog/post, site/blog/post/preview, site/blog/author, site/blog/tag, site/blog/category, site/blog/feed-rss, site/blog/feed-atom and site/blog/sitemap. The legacy API route is api-blog with children posts, post and comments.
CKEditor
The module listens to Omeka's o:ckeditor-config event on every admin page and adds the codesnippet plugin and a CodeSnippet toolbar button to each CKEditor instance, not just the blog forms.
Tests
From modules/Blog:
composer install
vendor/bin/phpunit --configuration tests/phpunit.xml
The unit suite needs nothing else. The integration suite runs against a live installation through the legacy API and is skipped unless OMEKA_BASE_URL, OMEKA_KEY_IDENTITY and OMEKA_KEY_CREDENTIAL (and optionally OMEKA_SITE_SLUG, default test-site) are set.