mageos / module-blog
Blog module for Mage-OS / Magento 2 — posts, categories, tags, authors, SEO, GraphQL.
MageOS Blog
A blog module for Mage-OS and Magento 2. Posts, categories, tags, authors, scheduled publishing, RSS, sitemap, 6 storefront widgets, SEO (meta tags, Open Graph, Twitter Cards, JSON-LD), and a full GraphQL API. Works with Luma and Hyvä themes.
Install
composer require mage-os/module-blog
bin/magento module:enable MageOS_Blog
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:clean
For Hyvä storefronts, a template companion (mage-os/module-blog-hyva) is planned for v1.2; v1.0 renders Luma templates on Hyvä via graceful fallback.
Enable
The module ships disabled. Turn it on under Stores → Configuration → MageOS → Blog → General → Enabled (or set mageos_blog/general/enabled = 1).
Key admin settings
All settings live under Stores → Configuration → Blog → Blog.
| Path | Purpose |
|---|---|
mageos_blog/general/enabled |
Master kill switch. Router and cron both short-circuit when off. |
mageos_blog/post/posts_per_page |
Listing pagination size. |
mageos_blog/post/default_robots |
Fallback meta robots value for posts that don't override it. |
mageos_blog/seo/og_default_type |
Default og:type. Usually article. |
mageos_blog/seo/json_ld_enabled |
Emit Article JSON-LD on post detail pages. |
mageos_blog/seo/twitter_site |
Twitter handle used for twitter:site. |
mageos_blog/sidebar/* |
Per-widget toggles for search, recent posts, category list, tag cloud, archive. |
mageos_blog/sitemap/{post,category,tag}/* |
Per-entity sitemap enable + changefreq + priority. |
mageos_blog/rss/enabled, mageos_blog/rss/limit |
RSS feed at /blog/rss. |
Compatibility
| PHP 8.2 | PHP 8.3 | PHP 8.4 | |
|---|---|---|---|
| Magento 2.4.6 | yes | yes | — |
| Magento 2.4.7 | yes | yes | — |
| Magento 2.4.8 | — | yes | yes |
| Magento 2.4.9 | — | yes | yes |
| Hyvä 1.3+ | yes | yes | yes |
| Luma | yes | yes | yes |
Tested against Magento 2.4.6-p15, 2.4.7-p10, 2.4.8-p5, and 2.4.9, plus the equivalent Mage-OS releases.
Requires magento/module-url-rewrite-graph-ql for the GraphQL URL resolver integration.
Storefront URLs
By default:
- Post:
/blog/{url-key} - Category:
/blog/category/{url-key} - Tag:
/blog/tag/{url-key} - Author:
/blog/author/{slug} - RSS:
/blog/rss - Search:
/blog/search?q=...
URL rewrites are populated on save by the repository plugins in Plugin/Repository/. The router (Controller/Router.php) delegates path parsing to MageOS\Blog\Model\UrlResolver.
GraphQL
Queries: blogPost, blogPosts, blogCategory, blogCategories, blogTag, blogTags, blogAuthor, blogAuthors. Each list query accepts filter, sort, pageSize, currentPage and returns items + page_info + total_count.
Mutations: createBlogPost, updateBlogPost, deleteBlogPost (and the equivalent for category / tag / author). Every mutation requires an admin token and passes through Magento\Framework\AuthorizationInterface against the entity's ACL resource (MageOS_Blog::post, MageOS_Blog::category, MageOS_Blog::tag, MageOS_Blog::author).
urlResolver(url: "/blog/my-post") returns { type: BLOG_POST, id, relative_url }. Supported types: BLOG_POST, BLOG_CATEGORY, BLOG_TAG, BLOG_AUTHOR.
Full schema: etc/schema.graphqls.
Upgrade notes
v1.0.0 is a greenfield rewrite. There is no migration path from any v0.x fork, including the original Magefan-derived codebase that lived under this package name before the rewrite. To move off a v0 install, export content from the old admin, fresh-install v1, and re-import via the admin or GraphQL.
Development
composer install
vendor/bin/phpunit --testsuite unit
vendor/bin/phpstan analyse --memory-limit=1G
vendor/bin/phpcs --standard=phpcs.xml.dist
vendor/bin/php-cs-fixer fix --dry-run --diff --allow-risky=yes
XDEBUG_MODE=coverage vendor/bin/infection --threads=4
Integration tests live under Test/Integration/ and run in CI against a live Magento install via graycoreio/github-actions-magento2.
Contributing
Issues and PRs welcome at https://github.com/mage-os-lab/module-blog. Full workflow, quality bar, and review expectations in CONTRIBUTING.md. Participation governed by the Contributor Covenant 2.1.
Security
Security vulnerabilities: please don't open a public issue. See SECURITY.md for the private advisory flow.
License
OSL-3.0. See LICENSE.
Attribution
Design inspired by Magefan Blog (OSL-3.0). v1 is an independent implementation with no shared code.
Changelog
All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.3.1 (2026-08-28)
Miscellaneous Chores
- actions: Update ci to use the latest versions (28fe7f2)
- ci: Update Check Extension CI to the test versions (04179ec)
1.3.0 (2026-08-25)
Features
- blog: resolve published posts linked to a product (b04aa93)
- config: add related-posts config group, disabled by default (1689f50), closes #20
- viewmodel: add PDP related-posts view model (3396caf), closes #20
Bug Fixes
- blog: filter related posts by published status and store scope (87d8c64)
- cache: declare cache identities on storefront blog blocks (e8b7264), closes #20
- pdp: replace related-posts placeholder with working block (5b823c5), closes #20
- styling: move away from css layout declarations to the luma styles generation (92af6f3)
[Unreleased]
Fixed
- Product pages no longer show a developer placeholder (#20). 1.1.0 injected an unimplemented "Related Posts" block into every product detail page, rendering the text "Related posts for products — scoped ViewModel lands in v1.1." to customers, with no way to switch it off short of a theme layout override. The block is now a working feature and is disabled by default.
- Related posts no longer leak drafts or other stores' content.
Model\RelatedPostsProvider\ManualRelationLoaderselected on the pivot alone, with no status or store-scope filter, so unpublished drafts and posts assigned to a different store view could appear in the related-posts section of any post detail page.AlgorithmicLoaderfiltered status but had the same store-scope gap. Both now apply the predicates the storefront listings already used. - Blog pages are now invalidated in the full-page cache. No block in the module implemented
IdentityInterface, so blog pages carried no blog cache tags and editing a post left them stale. This also meantCron\PublishScheduledPostscould not do the one thing it exists for — re-saving a post to invalidate FPC invalidates nothing when nothing is tagged. Ten storefront blocks and content widgets now declare identities.
Added
- Related posts on product detail pages. Posts linked to a product through the post form's Related Products field are shown in a product information tab, store-scoped and published-only. Controlled by
mageos_blog/related_posts/*—enabled(default0),limit(default 3) andtitle. The section renders nothing at all when the module is off, the feature is off, or a product has no linked posts, so no empty tab appears.
Changed
view/frontend/web/css/blog.csssplit: design tokens, base typography and the post-card component moved toblog-cards.css, so product pages can load the card styles without the blog page layout.blog.cssnow requiresblog-cards.cssto be loaded first;blog_default.xmldoes so.- Removed five retired placeholder strings from
i18n/en_US.csv, including the one shown by the #20 block.
No public Api/ signature changed.
1.2.0 (2026-08-12)
Features
- add url rewrite regenerate command (7a2be85)
Bug Fixes
- Exception on NULL URL Key Value (3db2f76)
- resolve url_key instead of nulling it on save (9850042)
- TypeError on post, category, tag, author save with empty URL key (3db2f76)
- write url rewrites for posts, categories and tags (4e144bd)
1.1.0 - 2026-06-25
Admin usability and storefront-design release. No schema or API breaking changes; safe upgrade from 1.0.0.
Added
- Admin "no raw IDs" pickers. The post form now uses real pickers instead of hand-typed entity IDs: author, category, tag, related-post, and related-product. The category form gains a hierarchical parent-category picker. Backed by new option sources under
Ui/Component/Form/{Authors,Categories,Tags,ParentCategory,RelatedPosts,RelatedProducts}/Options.php. Related-post and related-product assignments now actually persist on save (Model\Post\Link\RelatedPostLinkManager,RelatedProductLinkManager,Model\Post\PostsByAssignmentProvider). - Human-readable admin listings. Linked author-name column on the post grid and a parent-category column on the category grid (
Ui/Component/Listing/Column/{AuthorName,ParentCategoryName}.php). - Storefront redesign. New
view/frontend/web/css/blog.csswith a typography scale, layout primitives, and card components. Sharedpost/card.phtml(replacingpost/item.phtml), hero-on-top post cards with pagination, a redesigned post-detail page (hero, tag chips, related posts), and context headers on category / tag / author / search pages. - API surface. Additional data fields and interface docblocks on
Api\Data\{Author,Category,Post,Tag}Interface(cleans upsetup:di:compile). - Project docs & CI.
CONTRIBUTING.md,CODE_OF_CONDUCT.md,SECURITY.md, GitHub issue/PR templates, and a GitHub Actions CI workflow.
Changed
- Naming. Module name references aligned to the
mage-osnaming convention (PR #12). - Layout. Blog storefront switched to a 1-column layout; Luma sidebar bleed removed. Body / heading / post-content type scale bumped;
.mageos-blog-containerleft-aligned. - Compatibility. Widened the
magento/frameworkconstraint to support Magento 2.4.6-p15.
Fixed
- Double
<h1>on category / tag / author / search pages (removed redundantpage.main.title). - Author avatar upload (dropped an invalid nested
xsi:type="string"param). - Admin form rendering and category / tag / author storefront post listings; flattened per-entity DataProvider row shapes and stringified IDs for ui-select.
setStringify/escaping fixes and numerous PHPCS warnings cleared; integration and unit test suites updated for 2.4.6-p15, 2.4.7-p10, 2.4.8-p5 & 2.4.9 & Equivilent MageOS versions.
Deferred
The following items were listed under "Deferred to v1.1" in the 1.0.0 release but are not included in 1.1.0. They remain planned for a future release:
- Comments (native, moderation, spam, email).
- Content importers from Mageplaza, Magefan, Aheadworks, Mirasvit, WordPress (planned as a separate
mage-os/module-blog-migrationpackage). - OpenSearch /
Magento_Searchindexer + mview +etc/search_request.xml. - PageBuilder content editing.
- Hyvä-native
.phtmlset (detection plugin is in place; the companionmage-os/module-blog-hyvapackage remains pending — see README). - Configurable URL prefix, custom per-page CSS, preview-token model, Commerce-only AdminGWS plugin, MFTF tests, gravatar autofetch, per-post multi-language content variants.
- Infection MSI ≥ 75% / Covered MSI ≥ 80%.
1.0.0 - 2026-04-20
First release of the greenfield rewrite. No migration path from pre-v1 forks.
Added
- Content model. Four entities (
blog_post,blog_category,blog_tag,blog_author) with DB-level foreign keys and cascading deletes. Store-view scoping via pivot tables. Fulltext indexes on post and category content for search. - Admin UI. UI-component grids and forms for posts, categories, tags, authors. Image upload (featured image, OG image, gallery) via a single
MageOS\Blog\ImageUploadvirtualType. Related-posts and related-products pickers on the post form. - Storefront. Custom router with URL-rewrite integration. Post detail, category detail, tag detail, author detail, blog index, search, pagination. Luma template set under
view/frontend/templates/; Hyvä template set ships in the companion packagemageos/module-blog-hyva. - Scheduled publishing.
Cron\PublishScheduledPostsruns every minute, finds posts whosepublish_datejust passed, and re-saves them to invalidate FPC. Gated bymageos_blog/general/enabled. - SEO.
meta title,meta description,meta keywords,meta robots, canonical link, Open Graph, Twitter Cards, andArticleJSON-LD on post detail pages. Configurable defaults undermageos_blog/seo/*. - RSS.
/blog/rssemits RSS 2.0 XML. Limit configurable viamageos_blog/rss/limit. - Sitemap. Three
ItemProviders (post, category, tag) wired intoMagento\Sitemap\Model\ItemProvider\Composite. Per-entity enable / frequency / priority undermageos_blog/sitemap/*. - Search. DB-fulltext-backed search against the
MAGEOS_BLOG_POST_FULLTEXTindex. Controller at/blog/search?q=.... OpenSearch / Magento_Search integration deferred to v1.1. - Widgets. 6 storefront widgets: recent posts, featured post, post list, post link, category link, tag link. Admin chooser blocks for picking a post / category / tag from grids.
- GraphQL. Queries (
blogPost,blogPosts,blogCategory,blogCategories,blogTag,blogTags,blogAuthor,blogAuthors). Mutations (create/update/deletefor all four entities). URL resolver integration:urlResolver(url:"/blog/my-post")returnstype: BLOG_POST. Mutations require admin token plus ACL. - Hyvä support. A
Plugin\Magento\Framework\View\TemplateEngine\Phpplugin injects aHyvaThemeDetectionhelper into every.phtmlscope.Plugin\Magento\Framework\View\Element\TemplateRewriteremapsMageOS_Blog::Xpaths toMageOS_Blog::hyva/Xon Hyvä themes. - i18n. Seed
i18n/en_US.csvwith 236 phrases. - Quality gates. PHPStan level 8, PHPCS (Magento2 ruleset), PHP-CS-Fixer, PHPUnit unit suite (57 tests), Infection mutation testing (baselines: MSI 54%, Covered MSI 70%).
Notes on design
Design inspired by Magefan Blog (OSL-3.0). v1 is an independent implementation with no shared code.
Deferred to v1.1
- Comments (native, moderation, spam, email).
- Content importers from Mageplaza, Magefan, Aheadworks, Mirasvit, WordPress. Will ship as a separate
mageos/module-blog-migrationpackage. - OpenSearch /
Magento_Searchindexer + mview +etc/search_request.xml. - PageBuilder content editing.
- Hyvä-native
.phtmlset (detection plugin is in place; the companion package is empty until v1.1). - Configurable URL prefix, custom per-page CSS, preview-token model, Commerce-only AdminGWS plugin, MFTF tests, gravatar autofetch, per-post multi-language content variants.
- Infection MSI ≥ 75% / Covered MSI ≥ 80%. v1.0 ships at 54% / 70% respectively; raising the floor depends on expanding unit-test coverage into
ViewModel/Post/Detail(JSON-LD shape edge cases) andModel/HyvaThemeDetection(theme-chain walking).
| Version | Stability | QA Status | Compatibility | Released |
|---|---|---|---|---|
| v1.3.1 | stable | Not tested | Not yet tested Details | 2026-08-28 16:52:14 |
| v1.3.0 | stable | Not tested | Not yet tested Details | 2026-08-25 22:38:04 |
| v1.2.0 | stable | Not tested | Not yet tested Details | 2026-08-20 15:12:55 |
| v1.1.0 | stable | Not tested | Not yet tested Details | 2026-06-25 06:52:08 |
| 1.0.0 | stable | Fail | Not compatible Details | 2026-04-20 13:36:00 |
Requires 15
| Package | Constraint |
|---|---|
| php | ^8.2 |
| magento/framework | ^103.0.7 |
| magento/module-backend | * |
| magento/module-catalog | * |
| magento/module-cms | * |
| magento/module-customer | * |
| magento/module-graph-ql | * |
| magento/module-media-storage | * |
| magento/module-search | * |
| magento/module-sitemap | * |
| magento/module-store | * |
| magento/module-ui | * |
| magento/module-url-rewrite | * |
| magento/module-url-rewrite-graph-ql | * |
| magento/module-widget | * |
Requires-dev 6
| Package | Constraint |
|---|---|
| phpunit/phpunit | ^10.5 |
| phpstan/phpstan | ^2.0 |
| bitexpert/phpstan-magento | ^0.42 |
| friendsofphp/php-cs-fixer | ^3.50 |
| magento/magento-coding-standard | ^40 |
| infection/infection | ^0.29 |
Compatibility
Each Magento release line is installed on its supported PHP versions, then the module is built (DI compilation + static-content deploy) and its unit and integration suites are run. The matrix shows the lines and PHP versions the module is confirmed to install and run on. Code-quality results further down (phpstan, phpcs, …) are reported separately and never affect compatibility.
Code Quality
Advisory checks against the module's source. Static analysis runs once across the whole module; PHPStan re-runs per Magento + PHP version because resolvable symbols differ between releases. These NEVER affect the Compatibility badge. A phpcs finding can't make a module incompatible.
Static analysis
Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.
| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 206 | 21 errors, 185 warnings (ruleset: Magento2) — 1 auto-fixable with phpcbf |
| PHPMD | Warning | 145 | 145 rule violations (UnusedFormalParameter:102, MissingImport:16, CyclomaticComplexity:11, NPathComplexity:11, ExcessiveClassComplexity:2) |
| Cpd | Warning | 9 | 9 duplicated chunks spanning 311 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 13 | valid; 13 advisory notes (composer validate --strict) |
Tests
Unit and integration suites, run for each applicable Magento and PHP version. A test failure speaks to the module's behaviour, not its compatibility with a Magento line, so it is reported here separately and never reddens the compatibility matrix.
Unit tests
Security
Security checks run directly against the module: an audit of its declared dependencies for known vulnerabilities (composer audit) and a scan of its source for malware and web-shell signatures. Each runs once. A malware detection fails the version outright.
Turn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.