docs(wiki): full rebuild — fix slugs/nav, refresh all pages for v01.05.x, add Admin Guide / JSON-LD Schemas / Troubleshooting / sidebar

2026-06-29 11:17:35 -05:00
parent ebf127f25f
commit 267cc00d2d
10 changed files with 446 additions and 212 deletions
+67
@@ -0,0 +1,67 @@
# Admin Guide
Open **Components → MokoSuiteOpenGraph** in the Joomla administrator.
## Dashboard (default view)
The dashboard is the landing screen. It shows:
- **Coverage donut** — percentage of published articles that have an OG tag (green ≥ 80%, amber ≥ 50%, red below)
- **Field coverage gaps** — how many tags are missing a custom title, description, or image
- **Coverage by content type** — a per-`content_type` breakdown (com_content, menu, category, com_mokoshop)
- **Articles missing OG tags** — the most recent published articles with no tag, each linking to the article editor, plus a **Batch Generate** shortcut
Switch between the dashboard and the tag list using the component submenu (**Dashboard** / **Tags**).
## Tag Manager (Tags)
Lists every OG tag record. From here you can:
- **Search and filter** by OG title and content type
- **Open the editor** by clicking a tag's title
- **New** — create a tag manually (you choose the content type, content ID, and language)
- **Edit** — select a row and edit it
- **Publish / Unpublish / Delete** — standard toolbar actions
- **SEO health badges** — flags for missing description, overly long title, or noindex
- **Debug links** — quick links to the Facebook Debugger, LinkedIn Inspector, and Google Rich Results test
### Editing a tag
The edit screen has two tabs:
- **Details** — content type, content ID, language, OG title/description/image/type, og:video, published state
- **SEO Meta Tags** — SEO title, meta description, robots, canonical URL
`maxlength` hints match the database limits (SEO title 70, meta description 200). Required fields are validated on save.
## Batch Generation
Click **Batch Generate** (toolbar or dashboard). The component:
1. Counts published articles that have no OG tag
2. Processes them in chunks, generating OG title/description/image from the article content
3. Shows a live progress bar
Requires the `mokoog.batch` permission (or core `create`).
## CSV Import / Export
- **Export CSV** — downloads all OG tags (including `og_video`, `event_data`, `recipe_data`, `custom_schema`)
- **Import CSV** — click **Import**, choose a `.csv` file, and upload. New columns are appended, so older exports still import. JSON columns are validated (objects/arrays only) and `og_video` is restricted to http/https.
The CSV header row defines the columns; `content_type`, `content_id`, and `language` form the unique key used to update-or-insert each row.
Requires the `mokoog.import` permission (or core `create` + `edit`).
## Permissions
Open **Options → Permissions** to grant per-group access to the custom actions:
- `mokoog.batch` — batch generation
- `mokoog.import` — CSV import/export
These complement the standard core actions (`core.create`, `core.edit`, `core.delete`, `core.manage`).
## Plugin Settings
Site-wide behaviour (defaults, JSON-LD, sitemap, AI, LocalBusiness) is configured in the **System - MokoSuiteOpenGraph** plugin — see [Configuration](Configuration).
+40 -29
@@ -4,48 +4,57 @@
```
pkg_mokoog
com_mokoog -- Admin component (dashboard, CRUD, batch, CSV)
plg_system_mokoog -- System plugin (frontend meta tag injection)
plg_content_mokoog -- Content plugin (editor form fields, live preview)
plg_webservices_mokoog -- WebServices plugin (REST API routes)
com_mokoog -- Admin component (dashboard, tag CRUD, batch, CSV, ACL)
plg_system_mokoog -- System plugin (frontend meta tag injection, sitemap, AI)
plg_content_mokoog -- Content plugin (editor form fields, live preview)
plg_webservices_mokoog -- WebServices plugin (REST API routes)
```
Targets **Joomla 6+** and **PHP 8.2+**. All extensions use the modern namespaced DI architecture: `services/provider.php` providers, `SubscriberInterface` event subscribers, and namespaced MVC. No deprecated `Factory::getDbo/getUser/getSession/getLanguage`, `Joomla\CMS\Filesystem`, or `jexit()` calls remain (forward-compatible with Joomla 7).
## Data Flow
### Frontend (onBeforeCompileHead)
1. System plugin detects current page (`option`, `view`, `id`)
### Frontend (`onBeforeCompileHead`)
1. System plugin detects the current page (`option`, `view`, `id`)
2. Loads custom OG data from `#__mokoog_tags` (language-aware)
3. Falls back to category/menu OG data if available
4. Auto-generates missing fields from article/product content
5. Resolves image URL, auto-resizes if enabled
6. Emits OG, Twitter, LinkedIn, Discord, Telegram meta tags
7. Builds JSON-LD schema (Article, Product, or WebPage)
5. Resolves the image URL, auto-resizes if enabled
6. Emits OG, Twitter, LinkedIn, Discord, Telegram, Fediverse, Pinterest meta tags
7. Builds JSON-LD schema(s) (Article/Product/WebPage + FAQ/HowTo/Event/Recipe/LocalBusiness/Video/custom)
8. Fires `onMokoOGAfterRender` for third-party extensions
### Admin (content plugin)
1. Content plugin adds OG fields tab to article/menu/category editor forms
2. On save, stores custom OG data to `#__mokoog_tags` with language
3. Live preview updates Facebook/Twitter card preview in real-time
### Editor (content plugin)
1. Content plugin adds the OG fields tab to article/menu/category editor forms
2. On save, stores custom OG data to `#__mokoog_tags` keyed by language; JSON fields are validated as objects/arrays
3. Live preview updates the Facebook/Twitter/LinkedIn/Discord/Mastodon/Slack cards in real time
### Admin component
1. **Dashboard** (default view) — `DashboardModel` computes coverage stats; `View/Dashboard` renders a donut + breakdown + missing-articles list
2. **Tags** — list view with publish/unpublish/delete (`TagsController`) and a single-record create/edit screen (`TagController``View/Tag``tmpl/tag/edit.php`)
3. **Batch / Import-Export**`BatchController`, `ImportExportController` (CSRF + ACL gated)
### API
1. WebServices plugin registers routes on `onBeforeApiRoute`
2. JSON:API controller provides full CRUD + content lookup endpoint
3. Field whitelist prevents information leakage
2. JSON:API controller provides full CRUD plus a content-lookup endpoint
3. A field whitelist (`JsonapiView`) prevents information leakage
## Key Classes
| Class | Location | Purpose |
|-------|----------|---------|
| MokoOG | plg_system_mokoog | Main system plugin, meta tag injection |
|---|---|---|
| MokoOG | plg_system_mokoog | Meta tag injection, sitemap trigger, AI AJAX endpoint |
| MokoOGContent | plg_content_mokoog | Editor form injection, OG data save/load |
| MokoOGWebServices | plg_webservices_mokoog | API route registration |
| TagsController | com_mokoog (admin) | Admin list publish/delete operations |
| BatchController | com_mokoog | Batch OG generation for existing articles |
| ImportExportController | com_mokoog | CSV import/export with language support |
| DisplayController | com_mokoog | Default view dispatch (→ dashboard) |
| DashboardModel | com_mokoog | Coverage metrics queries |
| TagController / TagsController | com_mokoog | Single-record edit / list operations |
| BatchController | com_mokoog | Batch OG generation |
| ImportExportController | com_mokoog | CSV import/export (JSON/URL validated) |
| TagTable | com_mokoog | DB table with field validation |
| ImageHelper | plg_system_mokoog | Image resize, center-crop, validation |
| ImageGenerator | plg_system_mokoog | Text overlay image generation (GD) |
| JsonLdBuilder | plg_system_mokoog | Article, Product, WebPage, Breadcrumb schemas |
| ImageHelper | plg_system_mokoog | Image resize, center-crop, per-platform crops, prune |
| JsonLdBuilder | plg_system_mokoog | All JSON-LD schema builders + `toScriptTag()` |
| SitemapBuilder | plg_system_mokoog | Access-filtered XML sitemap, atomic write |
## Database
@@ -53,11 +62,13 @@ Single table: `#__mokoog_tags`
- Unique key: `(content_type, content_id, language)`
- Supports: `com_content`, `com_content.category`, `menu`, `com_mokoshop`
- Language-aware queries prefer specific language over `*` wildcard
- Columns include `og_*`, `seo_title`, `meta_description`, `robots`, `canonical_url`, `og_video`, `event_data`, `recipe_data`, `custom_schema`, `language`, `published`
- Language-aware queries prefer a specific language over the `*` wildcard
## Performance
## Performance & Safety
- `loadArticle()` and `loadShopProduct()` use static per-request caching
- Article pages: 1 DB query instead of 5 (consolidated in v1.0)
- Batch processing capped at 200 per request
- Generated images cached by content hash in `images/mokoog/generated/`
- `loadArticle()` / `loadShopProduct()` use static per-request caching (including negative lookups) — one query instead of several when the image finder, date extractor, and JSON-LD builder all need the same record
- Batch processing is capped per request
- Generated images are cached under `images/mokoog/generated/` and pruned after 30 days
- The sitemap excludes non-public content and writes via a temp file + atomic rename
- Custom JSON-LD is validated as an object/array on save **and** guarded on render, so malformed data cannot crash the public page
+83 -34
@@ -1,52 +1,101 @@
# Configuration
Navigate to **Extensions > Plugins > System - MokoSuiteOpenGraph** to configure the plugin.
Settings live in two places:
## Basic Settings
- **System plugin** — site-wide defaults and behaviour. Navigate to **Extensions → Plugins → System - MokoSuiteOpenGraph**.
- **Component Options** — permissions only. Open **Components → MokoSuiteOpenGraph → Options** (the gear icon).
---
## System Plugin
### Basic
| Parameter | Description |
|-----------|-------------|
| **Site Name** | The `og:site_name` value. Leave blank to use the Joomla site name. |
| **Default OG Title** | Site-wide fallback title for social sharing. |
| **Default OG Description** | Site-wide fallback description. |
| **Default Image** | Fallback image. Recommended: 1200x630px. |
| **Twitter Card Type** | Summary or Summary with Large Image. |
| **Twitter @username** | Your site Twitter handle (e.g. @mokoconsulting). |
| **Facebook App ID** | Your Facebook App ID for `fb:app_id` meta tag. |
| **Telegram Channel** | Your Telegram channel handle for link previews. |
| **Discord Embed Color** | The color of the embed sidebar when shared on Discord. |
| **Fediverse Creator** | Your Fediverse/Mastodon handle (e.g. `@user@mastodon.social`). Outputs `fediverse:creator` meta tag. |
|---|---|
| **Site Name** (`og_site_name`) | The `og:site_name` value. Leave blank to use the Joomla site name. |
| **Default OG Title** (`default_og_title`) | Site-wide fallback title for social sharing. |
| **Default OG Description** (`default_og_description`) | Site-wide fallback description. |
| **Default Image** (`default_image`) | Fallback image. Recommended: 1200×630px. |
| **Twitter Card Type** (`twitter_card_type`) | `summary` or `summary_large_image`. |
| **Twitter @username** (`twitter_site`) | Site Twitter/X handle (e.g. `@mokoconsulting`). |
| **Facebook App ID** (`fb_app_id`) | Outputs the `fb:app_id` meta tag. |
| **Telegram Channel** (`telegram_channel`) | Channel handle for link previews. |
| **Discord Embed Color** (`discord_color`) | Sidebar color of the Discord embed (`theme-color`). |
| **Fediverse Creator** (`fediverse_creator`) | Mastodon/Fediverse handle (e.g. `@user@mastodon.social`) `fediverse:creator`. |
## Advanced Settings
### Advanced
| Parameter | Description |
|-----------|-------------|
| **Auto-generate Tags** | Automatically generate OG tags from article content. |
| **Strip HTML** | Remove HTML tags from auto-generated descriptions. |
| **Description Length** | Maximum character length for `og:description` (50-300, default 160). |
| **Auto-resize Images** | Resize images to 1200x630px using center crop. |
| **Enable JSON-LD** | Output structured data for Google rich results. |
| **JSON-LD Breadcrumbs** | Output BreadcrumbList schema from Joomla pathway. |
|---|---|
| **Auto-generate Tags** (`auto_generate`) | Generate OG tags from article content when no custom value is set. |
| **Strip HTML** (`strip_html`) | Remove HTML from auto-generated descriptions. |
| **Description Length** (`desc_length`) | Max characters for auto `og:description` (50300, default 160). |
| **Auto-resize Images** (`auto_resize`) | Resize images to 1200×630 using center crop. |
| **Per-platform Resize** (`platform_resize`) | Additionally produce platform-specific crops (Twitter 1200×600, Pinterest 1000×1500, WhatsApp 400×400). |
| **Enable JSON-LD** (`jsonld_enabled`) | Output structured data for Google rich results. |
| **FAQ JSON-LD** (`jsonld_faq`) | Auto-detect FAQ pairs from article headings → FAQPage schema. |
| **HowTo JSON-LD** (`jsonld_howto`) | Auto-detect steps from ordered lists → HowTo schema. |
| **JSON-LD Breadcrumbs** (`jsonld_breadcrumbs`) | Output BreadcrumbList from the Joomla pathway. |
### LocalBusiness (`localbusiness`)
Enable (`lb_enabled`) to output a site-wide LocalBusiness schema. Fields: `lb_name`, `lb_type` (LocalBusiness/Restaurant/Store/…), `lb_street`, `lb_city`, `lb_region`, `lb_postal`, `lb_country`, `lb_phone`, `lb_email`, `lb_url`, `lb_opening_hours`, `lb_latitude`, `lb_longitude`, `lb_price_range`. Address, geo, and contact blocks are only emitted when their fields are filled.
### Sitemap (`sitemap`)
| Parameter | Description |
|---|---|
| **Enable Sitemap** (`sitemap_enabled`) | Regenerate `sitemap.xml` on article save. |
| **Change Frequency** (`sitemap_changefreq`) | `daily`, `weekly`, or `monthly`. |
The sitemap includes only published articles at **public** view levels (registered/special-access content is excluded), skips `noindex` items, and is written atomically.
### AI Meta Generation (`ai`)
| Parameter | Description |
|---|---|
| **Enable AI** (`ai_enabled`) | Show the "Generate with AI" button in the editor. |
| **Provider** (`ai_provider`) | `claude` or `openai`. |
| **API Key** (`ai_api_key`) | Provider API key. |
| **Model** (`ai_model`) | e.g. `claude-haiku-4-5-20251001`. |
> AI generation requires **article-edit** permission and uses an outbound HTTP call (20s timeout). Keep your API key private — anyone who can edit articles can spend credits.
---
## Component Options (Permissions)
The component ships an `access.xml` defining standard core actions plus two custom actions:
- **`mokoog.batch`** — run batch OG tag generation
- **`mokoog.import`** — import/export OG tags via CSV
Configure them per user group under **Components → MokoSuiteOpenGraph → Options → Permissions**. Controllers fall back to the matching core action (`core.create` / `core.create`+`core.edit`) so existing setups keep working until you tighten the new actions.
---
## Fallback Chain
OG tag values are resolved in this order:
1. **Custom OG data** -- per-article/menu/category overrides set in the editor
2. **Page metadata** -- Joomla page title, meta description, article images
3. **Site-wide defaults** -- Default OG Title, Default OG Description, Default Image
1. **Custom OG data** per-article/menu/category overrides set in the editor
2. **Page metadata** Joomla page title, meta description, article images
3. **Site-wide defaults** Default OG Title, Default OG Description, Default Image
## Per-Content OG Fields
When editing an article, menu item, or category, you will see a **MokoSuiteOpenGraph** tab with:
When editing an article, menu item, or category, the **MokoSuiteOpenGraph** tab provides:
- **OG Title** -- Custom title (60 optimal / 90 max chars)
- **OG Description** -- Custom description (155 optimal / 200 max chars)
- **OG Image** -- Custom image (Joomla media picker)
- **OG Type** -- article, website, product, profile, book, music, video
- **SEO Title** -- Custom `<title>` (60 optimal / 70 max chars)
- **Meta Description** -- Custom meta description (155 optimal / 160 max chars)
- **Robots** -- noindex, nofollow, noarchive, nosnippet, noimageindex
- **Canonical URL** -- Custom canonical URL override
- **OG Title** — custom title (60 optimal / 90 max chars)
- **OG Description** — custom description (155 optimal / 200 max chars)
- **OG Image** — custom image (Joomla media picker)
- **OG Type** article, website, product, profile, book, music, video
- **og:video** — per-article video URL (YouTube/Vimeo/direct)
- **SEO Title** — custom `<title>` (60 optimal / 70 max chars)
- **Meta Description** — custom meta description (155 optimal / 200 max chars)
- **Robots** — noindex, nofollow, noarchive, nosnippet, noimageindex
- **Canonical URL** — custom canonical (http/https only)
- **Custom JSON-LD / Event / Recipe** — per-article structured data (see [JSON-LD Schemas](JSON-LD-Schemas))
All text fields include live character count indicators with green/yellow/red color coding.
All text fields include live character-count indicators with green/yellow/red color coding.
-120
@@ -1,120 +0,0 @@
# Developer Guide
## REST API
The Web Services plugin exposes OG tags via Joomla JSON:API.
### Endpoints
| Method | URL | Description |
|--------|-----|-------------|
| GET | /api/v1/mokoog/tags | List all OG tags |
| GET | /api/v1/mokoog/tags/:id | Get single OG tag |
| POST | /api/v1/mokoog/tags | Create OG tag |
| PATCH | /api/v1/mokoog/tags/:id | Update OG tag |
| DELETE | /api/v1/mokoog/tags/:id | Delete OG tag |
| GET | /api/v1/mokoog/lookup/:content_type/:content_id | Lookup by content |
### Authentication
All endpoints require Joomla API authentication (token or session).
### Fields
| Field | Type | Description |
|-------|------|-------------|
| id | int | Primary key |
| content_type | string | e.g. `com_content`, `menu`, `com_mokoshop` |
| content_id | int | Content item ID |
| og_title | string | OG title (max 255) |
| og_description | string | OG description |
| og_image | string | Image path |
| og_type | string | article, website, product, profile, book |
| seo_title | string | Page title override (max 70) |
| meta_description | string | Meta description (max 200) |
| robots | string | Robots directives |
| canonical_url | string | Canonical URL |
| language | string | Language tag (e.g. en-GB) or * |
| published | int | 0 or 1 |
## Extending with Custom Plugins
### onMokoOGAfterRender Event
Third-party plugins can subscribe to the `onMokoOGAfterRender` event to add custom social meta tags for any platform.
```php
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;
final class MyCustomSocialPlugin extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return ['onMokoOGAfterRender' => 'onMokoOGAfterRender'];
}
public function onMokoOGAfterRender(Event $event): void
{
$doc = $event->getArgument('subject');
$title = $event->getArgument('title');
$image = $event->getArgument('image');
// Add Pinterest meta tag
$doc->setMetaData('pinterest:media', $image);
}
}
```
### Event Arguments
| Key | Type | Description |
|-----|------|-------------|
| subject | HtmlDocument | The Joomla document object |
| title | string | Resolved OG title |
| description | string | Resolved OG description |
| image | string | Resolved image URL (absolute) |
| url | string | Current page URL |
| type | string | OG type (article, website, product, etc.) |
| option | string | Component option (e.g. com_content, com_mokoshop) |
| view | string | View name (e.g. article, product) |
| id | int | Content item ID |
## MokoSuiteShop Integration
Product pages (`com_mokoshop`, view `product`) automatically receive:
- `og:type` set to `product`
- `product:price:amount` and `product:price:currency` meta tags
- JSON-LD `Product` schema with `offers`, `sku`, and `aggregateRating`
- Image auto-extracted from the linked `#__content` article
Product data is loaded from `#__mokosuite_crm_products` joined to `#__content` via `article_id`.
## Database Schema
Table: `#__mokoog_tags`
| Column | Type | Description |
|--------|------|-------------|
| id | INT UNSIGNED | Primary key |
| content_type | VARCHAR(100) | e.g. com_content, menu, com_mokoshop |
| content_id | INT UNSIGNED | ID of the content item |
| og_title | VARCHAR(255) | Custom OG title |
| og_description | TEXT | Custom OG description |
| og_image | VARCHAR(512) | Image path relative to JPATH_ROOT |
| og_type | VARCHAR(50) | article, website, product, etc. |
| seo_title | VARCHAR(70) | Custom page title override |
| meta_description | VARCHAR(200) | Custom meta description |
| robots | VARCHAR(100) | Robots directive |
| canonical_url | VARCHAR(512) | Canonical URL override |
| language | CHAR(7) | Language tag (e.g. en-GB) or * |
| published | TINYINT(1) | 0 or 1 |
| created | DATETIME | Record creation time |
| modified | DATETIME | Last modification time |
**Unique key:** `(content_type, content_id, language)`
## Performance
Article page OG generation uses a single cached DB query via `loadArticle()` with static per-request caching. Product data is similarly cached via `loadShopProduct()`. This avoids duplicate queries when multiple consumers (image finder, date extractor, JSON-LD builder) need the same article data.
+109
@@ -0,0 +1,109 @@
# Developer Guide
## REST API
The Web Services plugin exposes OG tags via Joomla JSON:API. Full machine-readable spec: [`openapi.yaml`](openapi.yaml).
### Endpoints
| Method | URL | Description |
|---|---|---|
| GET | `/api/index.php/v1/mokoog/tags` | List all OG tags |
| GET | `/api/index.php/v1/mokoog/tags/:id` | Get a single OG tag |
| POST | `/api/index.php/v1/mokoog/tags` | Create an OG tag |
| PATCH | `/api/index.php/v1/mokoog/tags/:id` | Update an OG tag |
| DELETE | `/api/index.php/v1/mokoog/tags/:id` | Delete an OG tag |
| GET | `/api/index.php/v1/mokoog/lookup/:content_type/:content_id` | Look up a tag by content |
### Authentication
All endpoints require Joomla API authentication (token or session) — the routes are registered as non-public.
### Fields
| Field | Type | Description |
|---|---|---|
| id | int | Primary key |
| content_type | string | e.g. `com_content`, `menu`, `com_mokoshop` |
| content_id | int | Content item ID |
| og_title | string | OG title |
| og_description | string | OG description |
| og_image | string | Image path |
| og_type | string | article, website, product, profile, book… |
| og_video | string | Video URL (YouTube/Vimeo/direct) |
| seo_title | string | Page `<title>` override (max 70) |
| meta_description | string | Meta description (max 200) |
| robots | string | Robots directives |
| canonical_url | string | Canonical URL (http/https) |
| event_data | json | Event schema fields (object/array) |
| recipe_data | json | Recipe schema fields (object/array) |
| custom_schema | json | Arbitrary schema.org JSON-LD (object/array) |
| language | string | Language tag (e.g. `en-GB`) or `*` |
| published | int | 0 or 1 |
> JSON fields (`event_data`, `recipe_data`, `custom_schema`) must be JSON **objects/arrays** — scalars are rejected on save and via CSV import, and guarded on render.
## Extending with Custom Plugins
### `onMokoOGAfterRender` Event
Third-party plugins can subscribe to add custom social meta tags for any platform.
```php
use Joomla\Event\Event;
use Joomla\Event\SubscriberInterface;
final class MyCustomSocialPlugin extends CMSPlugin implements SubscriberInterface
{
public static function getSubscribedEvents(): array
{
return ['onMokoOGAfterRender' => 'onMokoOGAfterRender'];
}
public function onMokoOGAfterRender(Event $event): void
{
$doc = $event->getArgument('subject');
$image = $event->getArgument('image');
// Add a Pinterest meta tag
$doc->setMetaData('pinterest:media', $image);
}
}
```
### Event Arguments
| Key | Type | Description |
|---|---|---|
| subject | HtmlDocument | The Joomla document object |
| title | string | Resolved OG title |
| description | string | Resolved OG description |
| image | string | Resolved image URL (absolute) |
| url | string | Current page URL |
| type | string | OG type (article, website, product…) |
| option | string | Component option (e.g. com_content, com_mokoshop) |
| view | string | View name (e.g. article, product) |
| id | int | Content item ID |
## MokoSuiteShop Integration
Product pages (`com_mokoshop`, view `product`) automatically receive:
- `og:type` set to `product`
- `product:price:amount` and `product:price:currency` meta tags
- JSON-LD `Product` schema with `offers`, `sku`, and `aggregateRating`
- Image auto-extracted from the linked `#__content` article
Product data is loaded from `#__mokosuite_crm_products` joined to `#__content` via `article_id`, with per-request caching.
## Database Schema
Table: `#__mokoog_tags` — unique key `(content_type, content_id, language)`. See [Architecture](Architecture) for the full column list. Fresh installs build the complete schema from `sql/install.mysql.sql`; upgrades apply incremental `sql/updates/mysql/*.sql` scripts.
## Forward Compatibility
The codebase is clean for Joomla 7: database access goes through `Factory::getContainer()->get(DatabaseInterface::class)`, language/app via `getApplication()`, filesystem via `Joomla\Filesystem\*`, and controllers throw exceptions instead of `jexit()`. The CI "deprecated Joomla API" check passes with zero findings.
## Testing
PHPUnit unit tests live under `tests/Unit/` (pure helpers — `JsonLdBuilder` schema builders and `toScriptTag()` escaping). Run via `vendor/bin/phpunit`. CI runs the suite on every PR.
+15 -8
@@ -26,7 +26,7 @@ Open Graph, Twitter Card, and social sharing meta tag management for Joomla 6 an
- **og:video** — YouTube, Vimeo, direct files with auto MIME detection
### JSON-LD Structured Data (11+ Types)
Article, Product, WebPage, BreadcrumbList, Organization, VideoObject, FAQPage, HowTo, Event, Recipe, LocalBusiness + custom schema builder for any schema.org type
Article, Product, WebPage, BreadcrumbList, Organization, VideoObject, FAQPage, HowTo, Event, Recipe, LocalBusiness + custom schema builder for any schema.org type. See [JSON-LD Schemas](JSON-LD-Schemas).
### Editor UX
- **6 preview cards** — Facebook, Twitter/X, LinkedIn, Discord, Mastodon, Slack
@@ -42,6 +42,8 @@ Article, Product, WebPage, BreadcrumbList, Organization, VideoObject, FAQPage, H
- **Component permissions** — ACL actions (`mokoog.batch`, `mokoog.import`) in the component Options → Permissions
- **XML sitemap** generation — respects noindex and public access levels
See the [Admin Guide](Admin-Guide).
### Developer Features
- REST API — full CRUD via Joomla Web Services ([OpenAPI spec](openapi.yaml)); includes `og_video`, `event_data`, `recipe_data`, `custom_schema`
- Per-platform image resizing (Twitter, Pinterest, WhatsApp); generated images auto-pruned after 30 days
@@ -50,18 +52,23 @@ Article, Product, WebPage, BreadcrumbList, Organization, VideoObject, FAQPage, H
- PHPUnit unit tests for JsonLdBuilder schema outputs and script-tag escaping
- MokoSuiteShop integration with product JSON-LD
See the [Developer Guide](Developer-Guide).
---
## Wiki Structure
## Documentation
### Guides
- [Installation](guides/Installation)
- [Configuration](guides/Configuration)
- [Installation](Installation)
- [Configuration](Configuration)
- [Admin Guide](Admin-Guide)
### Reference
- [Developer Guide](reference/Developer-Guide)
- [Platform Support](reference/Platform-Support)
- [Architecture](reference/Architecture)
- [Developer Guide](Developer-Guide) — REST API, events, schema, MokoSuiteShop
- [JSON-LD Schemas](JSON-LD-Schemas)
- [Platform Support](Platform-Support)
- [Architecture](Architecture)
- [Troubleshooting](Troubleshooting)
---
@@ -71,7 +78,7 @@ Article, Product, WebPage, BreadcrumbList, Organization, VideoObject, FAQPage, H
| Revision | Date | Author | Description |
|---|---|---|---|
| 6.0 | 2026-06-29 | Moko Consulting | Joomla 6+/PHP 8.2+, rename to MokoSuiteOpenGraph, dashboard view, manual editor, ACL, security & forward-compat fixes |
| 6.0 | 2026-06-29 | Moko Consulting | Joomla 6+/PHP 8.2+, rename to MokoSuiteOpenGraph, dashboard view, manual editor, ACL, security & forward-compat fixes; full wiki rebuild |
| 5.0 | 2026-06-23 | Moko Consulting | v2.0: 11+ JSON-LD types, 6 preview cards, AI, sitemap, tests |
| 4.0 | 2026-06-23 | Moko Consulting | v1.3: Fediverse, LinkedIn preview, character counters |
| 3.0 | 2026-06-21 | Moko Consulting | Full assessment: MokoSuiteShop, JSON-LD Product, multilingual |
+56
@@ -0,0 +1,56 @@
# JSON-LD Schemas
MokoSuiteOpenGraph emits [schema.org](https://schema.org) structured data as JSON-LD `<script type="application/ld+json">` blocks for Google rich results and other consumers. Enable it with **Enable JSON-LD** in the plugin's Advanced settings.
All output is encoded with `json_encode` and `</` is escaped to `<\/` to prevent `</script>` breakout (XSS-safe). Values pulled from content are never concatenated as raw strings.
## Automatic schemas
| Schema | When it's emitted | Source |
|---|---|---|
| **Article** | `com_content` article pages | Title, intro/full text, image, author, publish/modified dates |
| **Product** | `com_mokoshop` product pages | Name, description, image, SKU, offers (price/currency/availability), aggregateRating |
| **WebPage** | All pages | Page name, description, URL |
| **BreadcrumbList** | All pages (optional) | Joomla pathway — toggle with **JSON-LD Breadcrumbs** |
| **Organization** | Site-wide | Site name and URL |
| **LocalBusiness** | Site-wide (optional) | The LocalBusiness fields in plugin config |
## Auto-detected schemas
| Schema | Trigger | Setting |
|---|---|---|
| **FAQPage** | Question/answer pairs detected from article `h3`/`h4` headings | **FAQ JSON-LD** |
| **HowTo** | Steps detected from ordered (`<ol>`) lists | **HowTo JSON-LD** |
## Per-article schemas
Set in the article's **MokoSuiteOpenGraph** tab and stored in `#__mokoog_tags`:
| Schema | Field | Notes |
|---|---|---|
| **Event** | `event_data` | Dates, venue, ticket/offer info |
| **Recipe** | `recipe_data` | Prep/cook times, ingredients, nutrition |
| **VideoObject** | `og_video` | Built from the per-article video URL |
| **Custom** | `custom_schema` | Any schema.org type — paste a JSON object |
> The Event/Recipe/Custom fields must contain a JSON **object** (e.g. `{ "startDate": "…" }`). Scalars (`42`, `"x"`, `true`) are rejected on save, on CSV import, and ignored on render — they previously caused a fatal page error and are now guarded.
## Custom schema example
Paste into the **Custom JSON-LD** field on an article:
```json
{
"@type": "SoftwareApplication",
"name": "MokoSuiteOpenGraph",
"applicationCategory": "Joomla Extension",
"operatingSystem": "Joomla 6+",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" }
}
```
`@context` is added automatically if you omit it. The result is emitted as a standalone JSON-LD block on that page.
## Validating output
Use Google's [Rich Results Test](https://search.google.com/test/rich-results) or the Schema Markup Validator. The tag manager's per-row **G** debug link opens the Rich Results test pre-filled for that content item.
+15 -21
@@ -1,12 +1,12 @@
# Platform Support
MokoSuiteOpenGraph outputs platform-specific meta tags optimized for each social network.
MokoSuiteOpenGraph outputs platform-specific meta tags optimized for each social network. See [JSON-LD Schemas](JSON-LD-Schemas) for structured-data details.
## Meta Tags by Platform
### Facebook / Meta
- `og:title`, `og:description`, `og:image`, `og:url`, `og:type`, `og:site_name`, `og:locale`
- `og:image:width`, `og:image:height` (dynamically detected from actual image)
- `og:image:width`, `og:image:height` (dynamically detected from the actual image)
- `og:video`, `og:video:secure_url`, `og:video:type`, `og:video:width`, `og:video:height` (per-article video URL)
- `fb:app_id` (optional)
@@ -21,7 +21,7 @@ MokoSuiteOpenGraph outputs platform-specific meta tags optimized for each social
### Pinterest
- `article:tag` for article rich pins (auto-extracted from Joomla content tags)
- `product:price:amount`, `product:price:currency`, `product:availability` for product rich(pins (MokoSuiteShop)
- `product:price:amount`, `product:price:currency`, `product:availability` for product rich pins (MokoSuiteShop)
### Discord
- Reads standard `og:*` tags
@@ -38,7 +38,7 @@ MokoSuiteOpenGraph outputs platform-specific meta tags optimized for each social
### WhatsApp
- Reads standard `og:*` tags
- Images auto-resized to meet minimum 300x200px requirement
- Images auto-resized to meet the minimum 300×200px requirement
### Slack / Reddit
- Read standard `og:*` tags natively — no special tags needed
@@ -46,24 +46,9 @@ MokoSuiteOpenGraph outputs platform-specific meta tags optimized for each social
### E-commerce / Product Pages
- `og:type` set to `product` on MokoSuiteShop product pages
- `product:price:amount` and `product:price:currency` for rich product previews
- ~product:availability` derived from stock quantity (instock/outofstock)
- `product:availability` derived from stock quantity (instock/outofstock)
- Automatic on any `com_mokoshop` product view
## JSON-LD Structured Data
### Article pages (`com_content`)
- `Article` schema with headline, description, image, author, datePublished, dateModified
### Product pages (`com_mokoshop`)
- `Product` schema with name, description, image, sku
- `Offer` with price, priceCurrency, availability (InStock/OutOfStock)
- `AggregateRating` from approved reviews (if any)
### All pages
- `WebPage` schema with name, description, URL
- `BreadcrumbList` schema from Joomla pathway (optional)
- `Organization` schema from site configuration
## Editor Preview Cards
When editing articles or menu items, live preview cards show how your content will appear on:
@@ -71,5 +56,14 @@ When editing articles or menu items, live preview cards show how your content wi
- **Facebook** — standard link preview card with image, title, description
- **Twitter / X** — large image card with rounded corners
- **LinkedIn** — professional card with image and title
- **Discord** — embed with `theme-color` accent
- **Mastodon** — Fediverse-style card
- **Slack** — unfurl-style preview
All previews update in real-time as you type. Character count indicators show green/yellow/red warnings when text exceeds platform limits.
All previews update in real time as you type. Character-count indicators show green/yellow/red warnings when text exceeds platform limits.
## Image Sizing
- Default OG image: 1200×630 (center crop)
- Per-platform crops (when "Per-platform Resize" is enabled): Twitter 1200×600, Pinterest 1000×1500, WhatsApp 400×400
- Generated images are cached and pruned after 30 days
+47
@@ -0,0 +1,47 @@
# Troubleshooting
## Meta tags don't appear on the frontend
- Confirm the **System - MokoSuiteOpenGraph** plugin is **enabled** (Extensions → Plugins).
- Meta tags are only injected on the **site** (frontend), on **HTML** documents — not in the admin or on JSON/feed views.
- View the page source and search for `og:title`. If your template or another SEO plugin also sets OG tags, the last one to run wins — disable the duplicate.
- Tags are emitted on `onBeforeCompileHead`; a template that builds its own `<head>` without calling the document head may bypass them.
## A page shows a 500 error after editing structured data
Older builds could crash if a **scalar** (e.g. `42`) was saved into the Custom JSON-LD / Event / Recipe field. This is fixed: such values are now rejected on save and ignored on render. If you hit it on an older version, edit the record (or re-import a corrected CSV) so the JSON field contains an **object** `{ … }` or is empty.
## OG image is missing or 404s
- Set a **Default Image** (1200×630) in the plugin config as a fallback.
- Ensure the GD PHP extension is installed if you use auto-resize.
- Auto-resized files are written to `images/mokoog/generated/` — that directory must be writable. Failed writes are logged (Joomla logs, category `mokoog`).
## Sitemap is empty or missing entries
- Enable **Sitemap** in the plugin config; `sitemap.xml` is regenerated when an article is saved.
- Only **published** articles at **public** view levels are included — registered/special-access and `noindex` items are intentionally excluded.
- `JPATH_ROOT/sitemap.xml` must be writable; failures are logged.
## AI generation returns nothing / an error
- AI must be enabled and an **API key** and **model** configured for the chosen provider.
- The user must have **article-edit** permission — otherwise the request is rejected.
- Non-200 responses from the provider now surface an error (HTTP status) instead of a blank result; check the message and your key/quota.
- A hung provider times out after 20 seconds.
## "Forbidden" when running Batch or Import
Grant the relevant action under **Options → Permissions**: `mokoog.batch` for batch generation, `mokoog.import` for CSV import/export. Users with core `create` (batch) or core `create`+`edit` (import) also pass.
## The Options screen is empty
You're on an older build without `config.xml`. Update to the current version — the component now ships a `config.xml` with a Permissions tab. OG/SEO settings themselves live in the **System** plugin, not the component Options.
## Multilingual: wrong language tags
OG data is stored per language with the unique key `(content_type, content_id, language)`. Language-specific rows take priority over the `*` (All) wildcard. If an article's language is changed after saving, re-open and re-save its OG tab so the row is keyed to the new language.
## Reporting issues
Open an issue at the [repository](https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteOpenGraph/issues) with your Joomla and PHP versions and steps to reproduce.
+14
@@ -0,0 +1,14 @@
### MokoSuiteOpenGraph
**Guides**
- [Home](Home)
- [Installation](Installation)
- [Configuration](Configuration)
- [Admin Guide](Admin-Guide)
**Reference**
- [Developer Guide](Developer-Guide)
- [JSON-LD Schemas](JSON-LD-Schemas)
- [Platform Support](Platform-Support)
- [Architecture](Architecture)
- [Troubleshooting](Troubleshooting)