Reference

Developer Reference

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.

Log in for Support