Standardize manifest.xml identity block: ensure <name> contains only
the machine identifier (PascalCase) and <display-name> contains the
human-readable label with Joomla extension type prefix. Remove duplicate
<version> tags where present. Update CONTRIBUTING.md from moko-platform
default.
Authored-by: Moko Consulting
C-1: CSRF nonce on OAuth authorize/callback flow
C-2: POST method enforcement on REST dispatch endpoint
C-5: Service credential fields now saved from form to JSON column
(collect cred_* fields, strip prefix, JSON encode on save;
expand back on load for editing)
H-1: Joomla 5 event ArrayAccess pattern for service plugin collection
(reads from Event indices instead of broken by-reference)
H-4: ServiceTable::check() with alias generation, required validation
H-9: WebhookService credential keys match form XML field names,
Bearer/Basic auth headers implemented correctly
M-4: XSS fix — escape $extraClass in ServiceIconHelper::renderIcon()
M-5: Article history HTML injection via setFieldAttribute() instead
of double-escaped XML description attribute
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Category routing rules — new DB table #__mokojoomcross_category_rules
maps Joomla categories to specific services (whitelist). Integrated
into CrossPostDispatcher before per-article filters.
2. Character counter — live JS counter in template editor shows
remaining chars per platform with color coding (green/yellow/red)
3. Service type icons — ServiceIconHelper maps 34 types to Bootstrap
icons, used in services list, posts list, and dashboard
4. Per-service analytics drill-down — ServiceStats view with stats
cards, daily trend chart, recent posts, top articles. Dashboard
service rows are now clickable links.
5. Article editor cross-post history — read-only panel in the
Cross-Posting fieldset showing last 10 post results with status
badges, service names, and timestamps
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add media capability reporting to MokoJoomCrossServiceInterface.
Each plugin now returns its supported media types:
- image, video, gif, document (per platform capability)
- Empty array for text-only services (Nostr, Ntfy, ConvertKit)
Enables the dispatcher to skip media attachments for text-only
services and choose appropriate media types per platform.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract dispatch logic from monolithic system plugin into a shared
CrossPostDispatcher helper. Each content type now has its own plugin:
- plg_content_mokojoomcross — articles (onContentAfterSave/ChangeState)
- plg_system_mokojoomcross_events — MokoJoomCalendar events
- plg_system_mokojoomcross_gallery — MokoJoomGallery galleries/images
- plg_mokojoomcross_mokojoomcalendar — calendar service enrichment
- plg_mokojoomcross_mokojoomgallery — gallery service enrichment
System plugin stripped to page-load queue processing only.
Also fixes: onContentBeforeDisplay Joomla 5/6 BeforeDisplayEvent
compatibility (was crashing with wrong argument type).
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete the Post CRUD that was previously stub-only:
- PostModel (AdminModel) for loading/saving individual posts
- Post HtmlView with toolbar (apply, save, cancel, dashboard)
- post.xml form with article selector, service selector, message
textarea, status dropdown, and scheduled_at calendar picker
- Post edit template with results sidebar and re-queue button
- Posts list: New button in toolbar, clickable article titles,
scheduled_at display with clock icon
- 20 new language strings for the post edit UI
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Articles can be marked as "evergreen" in the Cross-Posting fieldset,
with a configurable re-share interval (default 30 days). The queue
processor checks for due articles and re-queues them automatically,
bypassing the duplicate guard for articles whose last successful post
exceeds the interval.
- Per-article: evergreen toggle + interval (days) in article editor
- Global config: enable/disable, default interval, max per run
- QueueProcessor::processEvergreen() finds and re-queues due articles
- Task plugin calls processEvergreen() before processQueue()
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Service edit sidebar now shows a contextual "Setup Guide" button when
a service type is selected. Links to the matching KB article on the
live site (e.g., /kb/mokojoomcross/service-twitter-mokojoomcross).
All 34 service types mapped.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All 13 plugins had copy-paste stub code with literal placeholder URLs
(e.g. '{site_url}/api/endpoint') that were never substituted with
actual credential values. Each plugin now has correct:
- URL construction from credentials
- Auth method (Basic Auth for WP, JWT for Ghost, GraphQL for Hashnode)
- API payload format per platform spec
- Credential validation with live API checks
Fixed: ActivityPub, Blogger, Ghost, Google Business, Hashnode, Matrix,
Medium, Nostr (stub), RSS Feed, Threads, Tumblr, WhatsApp, WordPress.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboard link buttons in Logs, Posts, Services, Template, and
Templates views used Toolbar::getInstance() and Route::_ without
importing the classes — causing fatal errors on page load.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix Twitter posting by replacing Bearer token (app-only, read-only)
with OAuth 1.0a HMAC-SHA1 signing using all 4 keys. Add credential
fields for 19 previously missing services and optional fields for
7 existing services. Add Developer Guide wiki page.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced raw JSON credentials textarea with individual form fields
per service type using Joomla showon directives. Each service now
has labeled inputs with help descriptions:
- Telegram: Chat ID + Bot Token (custom mode only)
- Discord/Slack/Teams/Google Chat: Webhook URL
- Facebook: Page ID + Page Access Token
- Twitter: Bearer Token + API Key + API Secret
- LinkedIn: Access Token + Organization ID
- Mastodon: Instance URL + Access Token
- Bluesky: Handle + App Password
- WhatsApp: Access Token + Phone Number ID + Recipient
- Mailchimp/SendGrid: API Key + List ID
- WordPress: Site URL + Username + App Password
- Webhook: URL + HTTP Method
- Matrix: Homeserver + Token + Room ID
- Ntfy: Server + Topic + Token
- Reddit: Client ID + Secret + Username + Subreddit
- Medium/Dev.to/Ghost/Blogger: API keys/tokens
Default/Custom mode selector for services with MokoWaaS bot support.
Authorize button for OAuth services (Facebook, LinkedIn, Twitter,
Threads) — visible after first save.
Dashboard button added to toolbar on ALL views (Services, Posts,
Templates, Logs, Service edit, Template edit).
Help panel sidebar in service edit with setup steps.
90+ new language strings for credential fields and help text.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added View/Service/HtmlView.php and tmpl/service/edit.php for the
service edit form (was causing 404 "View not found" error)
- Added credential hint panel in service edit sidebar
- Added 16 more default templates (telegram, discord, slack, facebook,
linkedin, bluesky, threads, teams, medium, wordpress, webhook,
sendgrid, brevo, ntfy, reddit, pinterest) — total 20 default templates
- Added credential hint language strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes three issues found during dev site testing:
1. All 4 list views (Services, Posts, Logs, Templates) missing
filterForm and activeFilters properties. Joomla searchtools
layout calls getGroup() on null filterForm. Added get('FilterForm')
and get('ActiveFilters') to all list HtmlView classes.
2. Content plugin onContentPrepareForm typed as Form but Joomla 5/6
passes PrepareFormEvent. Now accepts both: extracts Form from
PrepareFormEvent when available, falls back to legacy Form type.
3. WebServices API routes expanded: added templates and logs CRUD
endpoints alongside posts and services.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MySQL strict mode does not allow default values on TEXT/BLOB columns.
Removes DEFAULT '' from error_message in #__mokojoomcross_posts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#19 Per-article cross-posting: Content plugin injects "Cross-Posting"
fieldset into article editor via onContentPrepareForm. Dynamic
checkbox list of all enabled services. Skip toggle. System plugin
reads article attribs for mokojoomcross_services (array of service
IDs) and mokojoomcross_skip (boolean). Unchecked = post to all.
#20 Analytics dashboard: Posts-by-service breakdown table with
success rate column (color-coded). Top cross-posted articles
list. DashboardModel methods: getServiceBreakdown(),
getDailyTrend(), getTopArticles().
#21 OAuth flows: OAuthHelper with authorize URL generation (Facebook,
LinkedIn, Twitter), PKCE for Twitter, code→token exchange, token
storage in service credentials. OauthController with authorize
and callback actions. Reads client ID/secret from plugin params.
#22 Wiki documentation: Services guide (all 9 platforms, default vs
custom mode), REST API reference, Message Templates guide with
examples per platform, Troubleshooting guide.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#12 LinkedIn: plugin config form (OAuth client ID/secret, redirect URI)
#13 Mastodon: plugin config (default instance, visibility, hashtags)
#14 Bluesky: plugin config (default PDS URL, auto link cards)
#15 Mailchimp: plugin config (sender name/email, auto-send toggle)
#17 Template management: full CRUD with TemplatesController,
TemplateController, TemplatesModel, TemplateModel, TemplateTable.
List view with service type badges and body preview. Edit view
with placeholder reference panel showing all 8 placeholders.
Filter form with search, published, service_type filters.
Added Templates submenu item and dashboard quick link.
#18 Logs: added filter form with level and search filters.
#16 WebServices: implementation already in place from scaffold,
routes registered for posts and services CRUD.
Admin component now has 5 submenu items: Dashboard, Post Queue,
Services, Templates, Activity Logs.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#6 PP Pro migration rewritten to read #__autotweet_channels table
directly, mapping channeltype names to MokoJoomCross service types.
Credential extraction per platform (Facebook page tokens, Twitter
OAuth, Telegram bot tokens, Discord/Slack webhooks). Falls back to
component params extraction when channel table doesn't exist.
#7 Facebook plugin: config form with default_page_access_token and
default_page_id. resolveToken() reads from plugin params.
#8 Discord plugin: config form with default_webhook_url and
embed_color. resolveWebhook() reads from plugin params.
#9 Twitter plugin: implementation already complete from scaffold.
#10 Slack plugin: config form with default_webhook_url.
resolveWebhook() reads from plugin params.
All service plugins with universal bot support now store default
credentials in their own plugin params (Extensions → Plugins)
rather than component params. This keeps sensitive tokens scoped
to the plugin that uses them.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three-pronged queue processing:
1. Joomla Scheduled Task (preferred): New plg_task_mokojoomcross plugin
registers "MokoJoomCross - Process Queue" task type. Admin creates
a scheduled task in System → Scheduled Tasks with desired interval.
2. Page-load fallback: System plugin onAfterRender with configurable
throttle interval. Runs on backend, frontend, or both. Small batch
size (5) to avoid slowing page loads.
3. Both can run simultaneously — QueueProcessor uses DB-based lock
to prevent concurrent execution (120s safety timeout).
Shared QueueProcessor helper handles:
- Queued post dispatch to service plugins
- Failed post retry with configurable max retries + delay
- Scheduled post firing (when scheduled_at <= now)
- Log cleanup based on retention period
Dashboard shows warning banner when page-load processing is active,
recommending switch to Joomla Scheduled Tasks for production.
Config options: queue_processing (scheduler/pageload/both),
pageload_client (admin/site/both), pageload_interval (seconds).
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
#1 Core engine: System plugin now dispatches to service plugins via
onMokoJoomCrossGetServices event, executes publish() immediately,
handles success/failure, duplicate guard prevents re-posting, listens
to both onContentAfterSave and onContentChangeState events. Template
rendering now resolves {category}, {author}, {date}, {fulltext}.
#3 Services CRUD: Admin list template with service type icons,
default/custom mode badges, publish toggle. Service edit form template.
#4 Post Queue: Admin list template with status badges (color-coded),
article title, service, message preview, platform post ID, error
messages, retry count, timestamps.
#5 Dashboard: Enhanced with recent activity feed from logs table,
migration controller action for PP Pro import, quick links sidebar.
#2 Telegram: Already implemented in scaffold, provider.php fixed.
Also fixes: All 9 service plugin provider.php files had broken
namespace references from bash heredoc escaping.
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>