From 4810371bc06624b4f10cf7b88bb16ea5f71ffbe2 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 28 Jun 2026 19:57:53 +0000 Subject: [PATCH 01/10] chore(version): pre-release bump to 01.12.01-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.12.01.sql | 1 + source/packages/plg_content_mokosuitecross/mokosuitecross.xml | 2 +- source/packages/plg_mokosuitecross_activitypub/activitypub.xml | 2 +- source/packages/plg_mokosuitecross_blogger/blogger.xml | 2 +- source/packages/plg_mokosuitecross_bluesky/bluesky.xml | 2 +- source/packages/plg_mokosuitecross_brevo/brevo.xml | 2 +- .../plg_mokosuitecross_constantcontact/constantcontact.xml | 2 +- source/packages/plg_mokosuitecross_convertkit/convertkit.xml | 2 +- source/packages/plg_mokosuitecross_devto/devto.xml | 2 +- source/packages/plg_mokosuitecross_discord/discord.xml | 2 +- source/packages/plg_mokosuitecross_facebook/facebook.xml | 2 +- source/packages/plg_mokosuitecross_ghost/ghost.xml | 2 +- .../plg_mokosuitecross_googlebusiness/googlebusiness.xml | 2 +- source/packages/plg_mokosuitecross_googlechat/googlechat.xml | 2 +- source/packages/plg_mokosuitecross_hashnode/hashnode.xml | 2 +- source/packages/plg_mokosuitecross_instagram/instagram.xml | 2 +- source/packages/plg_mokosuitecross_linkedin/linkedin.xml | 2 +- source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml | 2 +- source/packages/plg_mokosuitecross_mastodon/mastodon.xml | 2 +- source/packages/plg_mokosuitecross_matrix/matrix.xml | 2 +- source/packages/plg_mokosuitecross_medium/medium.xml | 2 +- .../plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml | 2 +- .../plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml | 2 +- source/packages/plg_mokosuitecross_nostr/nostr.xml | 2 +- source/packages/plg_mokosuitecross_ntfy/ntfy.xml | 2 +- source/packages/plg_mokosuitecross_pinterest/pinterest.xml | 2 +- source/packages/plg_mokosuitecross_reddit/reddit.xml | 2 +- source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml | 2 +- source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml | 2 +- source/packages/plg_mokosuitecross_slack/slack.xml | 2 +- source/packages/plg_mokosuitecross_teams/teams.xml | 2 +- source/packages/plg_mokosuitecross_telegram/telegram.xml | 2 +- source/packages/plg_mokosuitecross_threads/threads.xml | 2 +- source/packages/plg_mokosuitecross_tiktok/tiktok.xml | 2 +- source/packages/plg_mokosuitecross_tumblr/tumblr.xml | 2 +- source/packages/plg_mokosuitecross_twitter/twitter.xml | 2 +- source/packages/plg_mokosuitecross_webhook/webhook.xml | 2 +- source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml | 2 +- source/packages/plg_mokosuitecross_wordpress/wordpress.xml | 2 +- source/packages/plg_mokosuitecross_youtube/youtube.xml | 2 +- source/packages/plg_system_mokosuitecross/mokosuitecross.xml | 2 +- .../plg_system_mokosuitecross_events/mokosuitecross_events.xml | 2 +- .../mokosuitecross_gallery.xml | 2 +- source/packages/plg_task_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/plg_webservices_mokosuitecross/mokosuitecross.xml | 2 +- source/pkg_mokosuitecross.xml | 2 +- 52 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.12.01.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 11958bdc..4da990cc 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.00.00 +# VERSION: 01.12.01 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 193d7ad5..8d215df6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.12.00 + VERSION: 01.12.01 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index eb4252c6..d31cc6c1 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.12.00 + VERSION: 01.12.01 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index 64995ac0..b8dac7e1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteCross - + Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 6. diff --git a/SECURITY.md b/SECURITY.md index bb261b18..86fb034e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.12.00 +VERSION: 01.12.01 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index 2e69901a..6f39b1cc 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.01.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.01.sql new file mode 100644 index 00000000..3ceaf65e --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.01.sql @@ -0,0 +1 @@ +/* 01.12.01 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index a797d4ba..63484ab1 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml index e7c45c5e..cea204cb 100644 --- a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml +++ b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ActivityPub (Fediverse) - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_blogger/blogger.xml b/source/packages/plg_mokosuitecross_blogger/blogger.xml index 6c7cac51..71215f67 100644 --- a/source/packages/plg_mokosuitecross_blogger/blogger.xml +++ b/source/packages/plg_mokosuitecross_blogger/blogger.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Blogger - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml index e48c48ab..b4aa9a3a 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_brevo/brevo.xml b/source/packages/plg_mokosuitecross_brevo/brevo.xml index 7cb3cfe1..4e867426 100644 --- a/source/packages/plg_mokosuitecross_brevo/brevo.xml +++ b/source/packages/plg_mokosuitecross_brevo/brevo.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Brevo (Sendinblue) - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml index 12e964a8..3c9a78fd 100644 --- a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml +++ b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Constant Contact - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml index c0a9ff6a..28fc8a03 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_devto/devto.xml b/source/packages/plg_mokosuitecross_devto/devto.xml index 3ab19bcc..3e159a71 100644 --- a/source/packages/plg_mokosuitecross_devto/devto.xml +++ b/source/packages/plg_mokosuitecross_devto/devto.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Dev.to - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_discord/discord.xml b/source/packages/plg_mokosuitecross_discord/discord.xml index a7c9862a..c3b046e3 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_facebook/facebook.xml b/source/packages/plg_mokosuitecross_facebook/facebook.xml index 2a249503..3e2c17ee 100644 --- a/source/packages/plg_mokosuitecross_facebook/facebook.xml +++ b/source/packages/plg_mokosuitecross_facebook/facebook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Facebook / Meta - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ghost/ghost.xml b/source/packages/plg_mokosuitecross_ghost/ghost.xml index 3dfaebca..88244cfe 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml index 0d7e9ecb..4cc3c281 100644 --- a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml +++ b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Business Profile - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml index 7aa1bae9..ab3b8545 100644 --- a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml +++ b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Chat - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml index d474b7ee..417cd7ca 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_instagram/instagram.xml b/source/packages/plg_mokosuitecross_instagram/instagram.xml index 42e505b0..904bea03 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.12.00 + 01.12.01 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml index d8f66e2a..e7c65487 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml index bb728cc6..285e97f5 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml index 3affc8a9..6c01e6bb 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_matrix/matrix.xml b/source/packages/plg_mokosuitecross_matrix/matrix.xml index e4f8cefb..df0605b7 100644 --- a/source/packages/plg_mokosuitecross_matrix/matrix.xml +++ b/source/packages/plg_mokosuitecross_matrix/matrix.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Matrix / Element - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_medium/medium.xml b/source/packages/plg_mokosuitecross_medium/medium.xml index 5ee00266..ee756c90 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml index 8e57fc9a..f367ff4a 100644 --- a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml +++ b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteCalendar Events - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml index ae9c5d2d..3218a8c1 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_nostr/nostr.xml b/source/packages/plg_mokosuitecross_nostr/nostr.xml index d2d1e0a6..c3c7de40 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml index ba15ecaf..58ab4eb3 100644 --- a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml +++ b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ntfy Push Notifications - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml index d103a6ae..29be32db 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_reddit/reddit.xml b/source/packages/plg_mokosuitecross_reddit/reddit.xml index 42d35f37..e17e17c2 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml index a0d8c930..3e12bc90 100644 --- a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml +++ b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml @@ -1,7 +1,7 @@ MokoSuiteCross - RSS Feed - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml index 2e1c408c..dfe652ee 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_slack/slack.xml b/source/packages/plg_mokosuitecross_slack/slack.xml index df5b9c14..94bd0c8a 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_teams/teams.xml b/source/packages/plg_mokosuitecross_teams/teams.xml index a3ba3569..2601ee8c 100644 --- a/source/packages/plg_mokosuitecross_teams/teams.xml +++ b/source/packages/plg_mokosuitecross_teams/teams.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Microsoft Teams - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_telegram/telegram.xml b/source/packages/plg_mokosuitecross_telegram/telegram.xml index 58414c7f..573b958a 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_threads/threads.xml b/source/packages/plg_mokosuitecross_threads/threads.xml index e5d0e7f3..c42a035f 100644 --- a/source/packages/plg_mokosuitecross_threads/threads.xml +++ b/source/packages/plg_mokosuitecross_threads/threads.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Threads (Meta) - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml index 9dd7a5f1..2efa6728 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml index b7d14cea..b6a7b73a 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_twitter/twitter.xml b/source/packages/plg_mokosuitecross_twitter/twitter.xml index 8c87b722..ee4fca3b 100644 --- a/source/packages/plg_mokosuitecross_twitter/twitter.xml +++ b/source/packages/plg_mokosuitecross_twitter/twitter.xml @@ -1,7 +1,7 @@ MokoSuiteCross - X / Twitter - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_webhook/webhook.xml b/source/packages/plg_mokosuitecross_webhook/webhook.xml index fdf655d1..f0bbae02 100644 --- a/source/packages/plg_mokosuitecross_webhook/webhook.xml +++ b/source/packages/plg_mokosuitecross_webhook/webhook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Generic Webhook - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml index d260ebcc..40b2cc02 100644 --- a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml +++ b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WhatsApp Business - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml index 8184507d..682671e7 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_youtube/youtube.xml b/source/packages/plg_mokosuitecross_youtube/youtube.xml index 10537c39..c131195b 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.12.00 + 01.12.01 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml index 7401b98c..04f79e7a 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml index 11767a92..fa9c1393 100644 --- a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml +++ b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Events - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml index a017db59..402175ef 100644 --- a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml +++ b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Gallery - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml index 96473d2c..0dd1302e 100644 --- a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Task - MokoSuiteCross Queue Processor - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml index ae69ef00..11601451 100644 --- a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Web Services - MokoSuiteCross - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index 172bf5ef..da1e0d08 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.12.00 + 01.12.01 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 1aa58e1d8dcc46c7bed50e73b52daabc2436dc33 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 28 Jun 2026 12:06:02 -0500 Subject: [PATCH 02/10] feat: add social image generator with GD-based OG image compositing (#157) Replace basic single-size OG image generation with full-featured multi-platform social image compositing: - Platform-specific canvas sizes: Facebook 1200x630, Twitter 1200x675, Instagram 1080x1080, Stories 1080x1920 - Vertical linear gradient fallback when no source image available - Semi-transparent overlay with configurable color and opacity (0-100%) - Logo placement in top-right corner, auto-scaled to 15% of canvas width - TTF text rendering with word wrap and text shadow for readability - GD bitmap font fallback when no TTF fonts are available - Configurable text position: top, center, or bottom - Output to images/mokosuitecross/{articleId}_{platform}.jpg - Cache clearing per article via clearCache() method - ImageController AJAX endpoint with platform parameter validation - Full config fieldset: enabled toggle, overlay color/opacity, text color/position, gradient start/end, logo upload Authored-by: Moko Consulting --- CHANGELOG.md | 5 +- source/packages/com_mokosuitecross/config.xml | 84 ++- .../language/en-GB/com_mokosuitecross.ini | 22 + .../src/Controller/ImageController.php | 148 ++++++ .../src/Controller/SocialImageController.php | 98 ---- .../src/Helper/SocialImageHelper.php | 482 ++++++++++++++---- 6 files changed, 601 insertions(+), 238 deletions(-) create mode 100644 source/packages/com_mokosuitecross/src/Controller/ImageController.php delete mode 100644 source/packages/com_mokosuitecross/src/Controller/SocialImageController.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ed2a44..3dc6d605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ - **Analytics service filter**: Filter heatmap and stats by service type with configurable date range - **Analytics service breakdown**: Per-service success rate, failure count, and average posts per day - **Analytics AJAX endpoint**: JSON heatmap data for dynamic filtering without page reload -- **Social image generator**: Generate Open Graph images with article title overlay using PHP GD library (#157) -- **Social image config**: Background color, text color, overlay style, and site name override in component options (#157) +- **Social image generator**: Auto-generate OG/share images with text overlay, logo, and gradient fallback (#157) +- **Social image sizes**: Platform-specific dimensions for Facebook (1200x630), Twitter (1200x675), Instagram (1080x1080), and Stories (1080x1920) +- **Social image config**: Overlay color/opacity, text color/position, gradient colors, logo upload in component options (#157) - **AI caption generation**: Generate platform-optimized cross-post captions from article content using Claude or OpenAI (#161) - **AI provider config**: New "AI Caption Generation" fieldset in component options with provider, API key, model, and tone settings - **AI Generate button**: One-click AI generation button in the Share Content panel that fills all caption fields diff --git a/source/packages/com_mokosuitecross/config.xml b/source/packages/com_mokosuitecross/config.xml index c93837fd..29f0fa70 100644 --- a/source/packages/com_mokosuitecross/config.xml +++ b/source/packages/com_mokosuitecross/config.xml @@ -277,43 +277,73 @@ + + + + + + + + + + + + + + + - - - -
diff --git a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini index e0f2b18c..f2531646 100644 --- a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini +++ b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini @@ -570,6 +570,28 @@ COM_MOKOSUITECROSS_AI_GENERATE_DESC="Generate platform-optimized captions from t COM_MOKOSUITECROSS_AI_GENERATING="Generating captions..." COM_MOKOSUITECROSS_AI_GENERATED="AI captions generated successfully." COM_MOKOSUITECROSS_AI_ERROR="AI generation failed: %s" + +; Social Image Generator +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE="Social Image Generator" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED="Enable Social Image Generator" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED_DESC="Automatically generate OG/share images with text overlay for cross-posted articles. Requires the PHP GD extension." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_COLOR="Overlay Color" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_COLOR_DESC="Color of the semi-transparent overlay drawn on top of the background image or gradient." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_OPACITY="Overlay Opacity" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_OPACITY_DESC="Opacity of the overlay from 0 (fully transparent) to 100 (fully opaque). Default is 60." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR="Text Color" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR_DESC="Color of the article title text rendered on the social image." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_POSITION="Text Position" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_POSITION_DESC="Vertical position of the title text on the generated image." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_BOTTOM="Bottom" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_CENTER="Center" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_TOP="Top" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_START="Gradient Start Color" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_START_DESC="Starting color (top) for the gradient background used when no article image is available." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_END="Gradient End Color" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_END_DESC="Ending color (bottom) for the gradient background used when no article image is available." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_LOGO="Logo" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_LOGO_DESC="Logo image placed in the top-right corner of generated social images. Scaled to 15%% of canvas width." COM_MOKOSUITECROSS_AI_NOT_CONFIGURED="AI is not configured. Go to Options to set up a provider and API key." ; Analytics diff --git a/source/packages/com_mokosuitecross/src/Controller/ImageController.php b/source/packages/com_mokosuitecross/src/Controller/ImageController.php new file mode 100644 index 00000000..557242d2 --- /dev/null +++ b/source/packages/com_mokosuitecross/src/Controller/ImageController.php @@ -0,0 +1,148 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; + +defined('_JEXEC') or die; + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Session\Session; +use Joomla\CMS\Uri\Uri; +use Joomla\Component\MokoSuiteCross\Administrator\Helper\SocialImageHelper; + +class ImageController extends BaseController +{ + /** + * AJAX endpoint to generate a social image for an article and platform. + * + * Expected GET parameters: + * - article_id (int) The Joomla article ID. + * - platform (string) Platform key (facebook, twitter, instagram, stories). + * + * @return void + */ + public function generate(): void + { + if (!Session::checkToken('get')) { + echo json_encode(['success' => false, 'error' => 'Invalid token']); + $this->app->close(); + + return; + } + + $user = $this->app->getIdentity(); + + if (!$user->authorise('core.edit', 'com_mokosuitecross')) { + echo json_encode(['success' => false, 'error' => 'Permission denied']); + $this->app->close(); + + return; + } + + $articleId = $this->input->getInt('article_id', 0); + $platform = $this->input->getCmd('platform', 'facebook'); + + if ($articleId < 1) { + echo json_encode(['success' => false, 'error' => 'Missing article ID']); + $this->app->close(); + + return; + } + + if (!in_array($platform, SocialImageHelper::getSupportedPlatforms(), true)) { + echo json_encode(['success' => false, 'error' => 'Unsupported platform: ' . $platform]); + $this->app->close(); + + return; + } + + // Load article + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName(['id', 'title', 'images'])) + ->from($db->quoteName('#__content')) + ->where($db->quoteName('id') . ' = ' . $articleId); + $db->setQuery($query); + $article = $db->loadObject(); + + if (!$article) { + echo json_encode(['success' => false, 'error' => 'Article not found']); + $this->app->close(); + + return; + } + + // Extract intro image path from the article images JSON field + $imagePath = ''; + $images = json_decode($article->images ?? '{}', true); + + if (!empty($images['image_intro'])) { + $candidate = JPATH_ROOT . '/' . ltrim($images['image_intro'], '/'); + + if (is_file($candidate)) { + $imagePath = $candidate; + } + } + + if ($imagePath === '' && !empty($images['image_fulltext'])) { + $candidate = JPATH_ROOT . '/' . ltrim($images['image_fulltext'], '/'); + + if (is_file($candidate)) { + $imagePath = $candidate; + } + } + + // Build config from component params + $params = ComponentHelper::getParams('com_mokosuitecross'); + + $logoRelative = $params->get('social_image_logo', ''); + $logoPath = ''; + + if ($logoRelative !== '') { + $candidate = JPATH_ROOT . '/' . ltrim($logoRelative, '/'); + + if (is_file($candidate)) { + $logoPath = $candidate; + } + } + + $config = [ + 'article_id' => $articleId, + 'overlay_color' => $params->get('social_image_overlay_color', '#000000'), + 'overlay_opacity' => (int) $params->get('social_image_overlay_opacity', 60), + 'text_color' => $params->get('social_image_text_color', '#FFFFFF'), + 'text_position' => $params->get('social_image_text_position', 'bottom'), + 'gradient_start' => $params->get('social_image_gradient_start', '#1a1a2e'), + 'gradient_end' => $params->get('social_image_gradient_end', '#16213e'), + 'logo_path' => $logoPath, + 'font_path' => '', + ]; + + try { + $relativePath = SocialImageHelper::generate($article->title, $imagePath, $platform, $config); + $url = Uri::root() . $relativePath; + + $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); + echo json_encode([ + 'success' => true, + 'path' => $relativePath, + 'url' => $url, + ]); + } catch (\RuntimeException $e) { + $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); + } + + $this->app->close(); + } +} \ No newline at end of file diff --git a/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php b/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php deleted file mode 100644 index 36462869..00000000 --- a/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php +++ /dev/null @@ -1,98 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; - -defined('_JEXEC') or die; - -use Joomla\CMS\Component\ComponentHelper; -use Joomla\CMS\Factory; -use Joomla\CMS\MVC\Controller\BaseController; -use Joomla\CMS\Session\Session; -use Joomla\CMS\Uri\Uri; -use Joomla\Component\MokoSuiteCross\Administrator\Helper\SocialImageHelper; - -class SocialImageController extends BaseController -{ - public function generate(): void - { - if (!Session::checkToken('get')) { - echo json_encode(['success' => false, 'error' => 'Invalid token']); - $this->app->close(); - - return; - } - - $user = $this->app->getIdentity(); - - if (!$user->authorise('core.manage', 'com_mokosuitecross')) { - echo json_encode(['success' => false, 'error' => 'Permission denied']); - $this->app->close(); - - return; - } - - $articleId = $this->input->getInt('article_id', 0); - - if ($articleId < 1) { - echo json_encode(['success' => false, 'error' => 'Missing article ID']); - $this->app->close(); - - return; - } - - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName(['id', 'title', 'images'])) - ->from($db->quoteName('#__content')) - ->where($db->quoteName('id') . ' = ' . $articleId); - $db->setQuery($query); - $article = $db->loadObject(); - - if (!$article) { - echo json_encode(['success' => false, 'error' => 'Article not found']); - $this->app->close(); - - return; - } - - $params = ComponentHelper::getParams('com_mokosuitecross'); - $siteName = $params->get('social_image_site_name', '') ?: Factory::getApplication()->get('sitename', ''); - - $options = [ - 'bg_color' => $params->get('social_image_bg_color', '#1a1a2e'), - 'text_color' => $params->get('social_image_text_color', '#ffffff'), - 'overlay' => $params->get('social_image_overlay', 'dark'), - ]; - - $backgroundPath = null; - $images = json_decode($article->images ?? '{}', true); - - if (!empty($images['image_intro'])) { - $backgroundPath = JPATH_ROOT . '/' . ltrim($images['image_intro'], '/'); - } elseif (!empty($images['image_fulltext'])) { - $backgroundPath = JPATH_ROOT . '/' . ltrim($images['image_fulltext'], '/'); - } - - try { - $imagePath = SocialImageHelper::generate($article->title, $siteName, $backgroundPath, $options); - $imageUrl = str_replace(JPATH_ROOT, Uri::root(true), str_replace('\\', '/', $imagePath)); - - $result = ['success' => true, 'image_url' => $imageUrl, 'image_path' => $imagePath]; - } catch (\Throwable $e) { - $result = ['success' => false, 'error' => $e->getMessage()]; - } - - $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); - echo json_encode($result); - $this->app->close(); - } -} diff --git a/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php b/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php index 701f0395..5d06d6d3 100644 --- a/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php +++ b/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php @@ -13,142 +13,396 @@ namespace Joomla\Component\MokoSuiteCross\Administrator\Helper; defined('_JEXEC') or die; +/** + * Social image generator using PHP GD. + * + * Produces platform-sized OG/share images with text overlay, logo, and gradient fallback. + */ class SocialImageHelper { - private const WIDTH = 1200; - private const HEIGHT = 630; + /** + * Platform canvas dimensions (width x height). + */ + private const PLATFORMS = [ + 'facebook' => [1200, 630], + 'twitter' => [1200, 675], + 'instagram' => [1080, 1080], + 'stories' => [1080, 1920], + ]; /** - * Generate a branded social/OG image with text overlay. - * - * @param string $title Article title to render on the image - * @param string $siteName Site name for branding watermark - * @param array $config Rendering config: bg_color, text_color, font_size, show_site_name - * - * @return array ['success' => bool, 'image_url' => string, 'error' => string] + * Maximum logo width as a fraction of canvas width. */ - public static function generate(string $title, string $siteName, array $config): array + private const LOGO_MAX_WIDTH_RATIO = 0.15; + + /** + * Logo inset from the top-right corner in pixels. + */ + private const LOGO_INSET = 20; + + /** + * Generate a social image for the given platform. + * + * @param string $title Article title for text overlay. + * @param string $imagePath Absolute path to the source image (may be empty). + * @param string $platform Platform key (facebook, twitter, instagram, stories). + * @param array $config Configuration array: + * - article_id (int) Required for output filename. + * - overlay_color (string) Hex colour, default #000000. + * - overlay_opacity (int) 0-100, default 60. + * - text_color (string) Hex colour, default #FFFFFF. + * - text_position (string) top|center|bottom, default bottom. + * - gradient_start (string) Hex colour, default #1a1a2e. + * - gradient_end (string) Hex colour, default #16213e. + * - logo_path (string) Absolute path to logo image. + * - font_path (string) Absolute path to TTF font. + * + * @return string Relative path to the generated image (from site root). + * + * @throws \RuntimeException If GD is unavailable or generation fails. + */ + public static function generate(string $title, string $imagePath, string $platform, array $config): string { - if (!\function_exists('imagecreatetruecolor')) { - return ['success' => false, 'error' => 'PHP GD extension is not available']; + if (!extension_loaded('gd')) { + throw new \RuntimeException('PHP GD extension is required for social image generation.'); } - $bgColor = $config['bg_color'] ?? '#1a1a2e'; - $textColor = $config['text_color'] ?? '#ffffff'; - $fontSize = (int) ($config['font_size'] ?? 48); - $showSiteName = (bool) ($config['show_site_name'] ?? true); - - $fontSize = max(24, min(96, $fontSize)); - - $image = imagecreatetruecolor(self::WIDTH, self::HEIGHT); - - if ($image === false) { - return ['success' => false, 'error' => 'Failed to create image canvas']; + if (!isset(self::PLATFORMS[$platform])) { + throw new \RuntimeException('Unsupported platform: ' . $platform); } - $bgRgb = self::hexToRgb($bgColor); - $textRgb = self::hexToRgb($textColor); + [$canvasW, $canvasH] = self::PLATFORMS[$platform]; - $bg = imagecolorallocate($image, $bgRgb[0], $bgRgb[1], $bgRgb[2]); - $text = imagecolorallocate($image, $textRgb[0], $textRgb[1], $textRgb[2]); + $canvas = imagecreatetruecolor($canvasW, $canvasH); - imagefilledrectangle($image, 0, 0, self::WIDTH - 1, self::HEIGHT - 1, $bg); + if ($canvas === false) { + throw new \RuntimeException('Failed to create image canvas.'); + } - $fontFile = self::findFont(); - - if ($fontFile !== null) { - self::renderTtfText($image, $title, $text, $fontSize, $fontFile); - - if ($showSiteName && $siteName !== '') { - $siteSize = (int) round($fontSize * 0.45); - $siteBox = imagettfbbox($siteSize, 0, $fontFile, $siteName); - $siteX = self::WIDTH - ($siteBox[2] - $siteBox[0]) - 40; - $siteY = self::HEIGHT - 30; - imagettftext($image, $siteSize, 0, $siteX, $siteY, $text, $fontFile, $siteName); - } + // --- Background: source image or gradient fallback --- + if ($imagePath !== '' && is_file($imagePath)) { + self::drawSourceImage($canvas, $imagePath, $canvasW, $canvasH); } else { - self::renderFallbackText($image, $title, $text); - - if ($showSiteName && $siteName !== '') { - $siteX = self::WIDTH - (\strlen($siteName) * imagefontwidth(3)) - 40; - $siteY = self::HEIGHT - 30; - imagestring($image, 3, $siteX, $siteY, $siteName, $text); - } + self::drawGradient( + $canvas, + $canvasW, + $canvasH, + $config['gradient_start'] ?? '#1a1a2e', + $config['gradient_end'] ?? '#16213e' + ); } - $outputDir = JPATH_ROOT . '/media/com_mokosuitecross/social'; + // --- Semi-transparent overlay --- + self::drawOverlay( + $canvas, + $canvasW, + $canvasH, + $config['overlay_color'] ?? '#000000', + (int) ($config['overlay_opacity'] ?? 60) + ); + + // --- Logo (top-right) --- + if (!empty($config['logo_path']) && is_file($config['logo_path'])) { + self::drawLogo($canvas, $config['logo_path'], $canvasW); + } + + // --- Title text --- + self::drawText( + $canvas, + $title, + $canvasW, + $canvasH, + $config['text_color'] ?? '#FFFFFF', + $config['text_position'] ?? 'bottom', + $config['font_path'] ?? '' + ); + + // --- Write output --- + $articleId = (int) ($config['article_id'] ?? 0); + $outputDir = JPATH_ROOT . '/images/mokosuitecross'; if (!is_dir($outputDir)) { mkdir($outputDir, 0755, true); } - $hash = hash('sha256', $title . $bgColor . $textColor . $fontSize); - $filename = $hash . '.png'; - $filePath = $outputDir . '/' . $filename; + $filename = $articleId . '_' . $platform . '.jpg'; + $outputPath = $outputDir . '/' . $filename; - if (!imagepng($image, $filePath, 6)) { - imagedestroy($image); + if (!imagejpeg($canvas, $outputPath, 90)) { + imagedestroy($canvas); - return ['success' => false, 'error' => 'Failed to save image file']; + throw new \RuntimeException('Failed to write image: ' . $outputPath); } - imagedestroy($image); + imagedestroy($canvas); - $imageUrl = 'media/com_mokosuitecross/social/' . $filename; - - return ['success' => true, 'image_url' => $imageUrl]; + return 'images/mokosuitecross/' . $filename; } - private static function renderTtfText(\GdImage $image, string $title, int $color, int $fontSize, string $fontFile): void + /** + * Remove all cached social images for an article. + * + * @param int $articleId The article ID. + * + * @return void + */ + public static function clearCache(int $articleId): void { - $maxWidth = self::WIDTH - 120; - $lines = self::wordWrapTtf($title, $fontFile, $fontSize, $maxWidth); - $lineHeight = (int) round($fontSize * 1.4); - $totalHeight = \count($lines) * $lineHeight; + $dir = JPATH_ROOT . '/images/mokosuitecross'; - $startY = (int) round((self::HEIGHT - $totalHeight) / 2) + $fontSize; - - foreach ($lines as $i => $line) { - $y = $startY + ($i * $lineHeight); - imagettftext($image, $fontSize, 0, 60, $y, $color, $fontFile, $line); + if (!is_dir($dir)) { + return; } - } - private static function renderFallbackText(\GdImage $image, string $title, int $color): void - { - $font = 5; - $charWidth = imagefontwidth($font); - $charHeight = imagefontheight($font); - $maxChars = (int) floor((self::WIDTH - 120) / $charWidth); - $lines = wordwrap($title, $maxChars, "\n", true); - $lineArray = explode("\n", $lines); - $lineHeight = $charHeight + 8; - $totalHeight = \count($lineArray) * $lineHeight; - $startY = (int) round((self::HEIGHT - $totalHeight) / 2); + foreach (self::PLATFORMS as $platform => $dims) { + $file = $dir . '/' . $articleId . '_' . $platform . '.jpg'; - foreach ($lineArray as $i => $line) { - $y = $startY + ($i * $lineHeight); - imagestring($image, $font, 60, $y, $line, $color); + if (is_file($file)) { + @unlink($file); + } } } /** - * Word-wrap text for TTF rendering at a given pixel width. + * Return the list of supported platform keys. * * @return string[] */ - private static function wordWrapTtf(string $text, string $fontFile, int $fontSize, int $maxWidth): array + public static function getSupportedPlatforms(): array { - $words = explode(' ', $text); - $lines = []; + return array_keys(self::PLATFORMS); + } + + // ------------------------------------------------------------------ + // Private drawing helpers + // ------------------------------------------------------------------ + + /** + * Load a source image and resize/crop to fill the canvas. + */ + private static function drawSourceImage(\GdImage $canvas, string $path, int $canvasW, int $canvasH): void + { + $source = self::loadImage($path); + + if ($source === null) { + return; + } + + $srcW = imagesx($source); + $srcH = imagesy($source); + + // Centre-crop resize (cover) + $scale = max($canvasW / $srcW, $canvasH / $srcH); + $newW = (int) round($srcW * $scale); + $newH = (int) round($srcH * $scale); + $offX = (int) round(($canvasW - $newW) / 2); + $offY = (int) round(($canvasH - $newH) / 2); + + imagecopyresampled($canvas, $source, $offX, $offY, 0, 0, $newW, $newH, $srcW, $srcH); + imagedestroy($source); + } + + /** + * Draw a vertical linear gradient as a background. + */ + private static function drawGradient(\GdImage $canvas, int $w, int $h, string $startHex, string $endHex): void + { + [$r1, $g1, $b1] = self::hexToRgb($startHex); + [$r2, $g2, $b2] = self::hexToRgb($endHex); + + for ($y = 0; $y < $h; $y++) { + $ratio = $y / max($h - 1, 1); + $r = (int) round($r1 + ($r2 - $r1) * $ratio); + $g = (int) round($g1 + ($g2 - $g1) * $ratio); + $b = (int) round($b1 + ($b2 - $b1) * $ratio); + $color = imagecolorallocate($canvas, $r, $g, $b); + imageline($canvas, 0, $y, $w - 1, $y, $color); + imagecolordeallocate($canvas, $color); + } + } + + /** + * Draw a semi-transparent overlay rectangle. + */ + private static function drawOverlay(\GdImage $canvas, int $w, int $h, string $hex, int $opacity): void + { + if ($opacity < 1) { + return; + } + + $opacity = min($opacity, 100); + [$r, $g, $b] = self::hexToRgb($hex); + + // GD alpha: 0 = opaque, 127 = fully transparent + $alpha = (int) round(127 - ($opacity / 100 * 127)); + $color = imagecolorallocatealpha($canvas, $r, $g, $b, $alpha); + imagefilledrectangle($canvas, 0, 0, $w - 1, $h - 1, $color); + imagecolordeallocate($canvas, $color); + } + + /** + * Draw a logo in the top-right corner, scaled to at most 15% of canvas width. + */ + private static function drawLogo(\GdImage $canvas, string $logoPath, int $canvasW): void + { + $logo = self::loadImage($logoPath); + + if ($logo === null) { + return; + } + + $logoW = imagesx($logo); + $logoH = imagesy($logo); + $maxW = (int) round($canvasW * self::LOGO_MAX_WIDTH_RATIO); + + if ($logoW > $maxW) { + $scale = $maxW / $logoW; + $newW = $maxW; + $newH = (int) round($logoH * $scale); + $scaled = imagecreatetruecolor($newW, $newH); + + imagesavealpha($scaled, true); + $transparent = imagecolorallocatealpha($scaled, 0, 0, 0, 127); + imagefill($scaled, 0, 0, $transparent); + imagecopyresampled($scaled, $logo, 0, 0, 0, 0, $newW, $newH, $logoW, $logoH); + imagedestroy($logo); + $logo = $scaled; + $logoW = $newW; + $logoH = $newH; + } + + $x = $canvasW - $logoW - self::LOGO_INSET; + $y = self::LOGO_INSET; + + imagecopy($canvas, $logo, $x, $y, 0, 0, $logoW, $logoH); + imagedestroy($logo); + } + + /** + * Draw word-wrapped title text onto the canvas. + */ + private static function drawText( + \GdImage $canvas, + string $title, + int $canvasW, + int $canvasH, + string $colorHex, + string $position, + string $fontPath + ): void { + if ($title === '') { + return; + } + + [$r, $g, $b] = self::hexToRgb($colorHex); + $color = imagecolorallocate($canvas, $r, $g, $b); + $padding = (int) round($canvasW * 0.06); + + $useTtf = ($fontPath !== '' && is_file($fontPath) && function_exists('imagettftext')); + + if ($useTtf) { + self::drawTtfText($canvas, $title, $fontPath, $color, $canvasW, $canvasH, $padding, $position); + } else { + self::drawGdText($canvas, $title, $color, $canvasW, $canvasH, $padding, $position); + } + } + + /** + * Render text using a TrueType font with automatic word wrapping. + */ + private static function drawTtfText( + \GdImage $canvas, + string $title, + string $fontPath, + int $color, + int $canvasW, + int $canvasH, + int $padding, + string $position + ): void { + $maxTextW = $canvasW - ($padding * 2); + $fontSize = (int) round($canvasH * 0.055); + $fontSize = max(16, min($fontSize, 64)); + + $lines = self::wordWrapTtf($title, $fontPath, $fontSize, $maxTextW); + $lineHeight = (int) round($fontSize * 1.35); + $totalH = count($lines) * $lineHeight; + + $y = match ($position) { + 'top' => $padding + $fontSize, + 'center' => (int) round(($canvasH - $totalH) / 2) + $fontSize, + default => $canvasH - $padding - $totalH + $fontSize, + }; + + foreach ($lines as $line) { + $box = imagettfbbox($fontSize, 0, $fontPath, $line); + $textW = abs($box[2] - $box[0]); + $x = (int) round(($canvasW - $textW) / 2); + + // Draw text shadow for readability + $shadow = imagecolorallocatealpha($canvas, 0, 0, 0, 60); + imagettftext($canvas, $fontSize, 0, $x + 2, $y + 2, $shadow, $fontPath, $line); + imagecolordeallocate($canvas, $shadow); + + imagettftext($canvas, $fontSize, 0, $x, $y, $color, $fontPath, $line); + $y += $lineHeight; + } + } + + /** + * Render text using the built-in GD bitmap font (fallback when no TTF is available). + */ + private static function drawGdText( + \GdImage $canvas, + string $title, + int $color, + int $canvasW, + int $canvasH, + int $padding, + string $position + ): void { + $font = 5; // largest built-in GD font + $charW = imagefontwidth($font); + $charH = imagefontheight($font); + $maxChars = (int) floor(($canvasW - $padding * 2) / $charW); + $maxChars = max($maxChars, 10); + + $wrapped = wordwrap($title, $maxChars, "\n", true); + $lines = explode("\n", $wrapped); + $lineHeight = $charH + 4; + $totalH = count($lines) * $lineHeight; + + $y = match ($position) { + 'top' => $padding, + 'center' => (int) round(($canvasH - $totalH) / 2), + default => $canvasH - $padding - $totalH, + }; + + foreach ($lines as $line) { + $textW = mb_strlen($line) * $charW; + $x = (int) round(($canvasW - $textW) / 2); + imagestring($canvas, $font, $x, $y, $line, $color); + $y += $lineHeight; + } + } + + /** + * Word-wrap a string to fit within a pixel width using TTF metrics. + * + * @return string[] + */ + private static function wordWrapTtf(string $text, string $fontPath, int $fontSize, int $maxWidth): array + { + $words = preg_split('/\s+/', $text); + $lines = []; $currentLine = ''; foreach ($words as $word) { - $testLine = $currentLine === '' ? $word : $currentLine . ' ' . $word; - $box = imagettfbbox($fontSize, 0, $fontFile, $testLine); - $width = abs($box[2] - $box[0]); + $testLine = $currentLine === '' ? $word : $currentLine . ' ' . $word; + $box = imagettfbbox($fontSize, 0, $fontPath, $testLine); + $lineWidth = abs($box[2] - $box[0]); - if ($width > $maxWidth && $currentLine !== '') { + if ($lineWidth > $maxWidth && $currentLine !== '') { $lines[] = $currentLine; $currentLine = $word; } else { @@ -164,37 +418,43 @@ class SocialImageHelper } /** - * Locate a usable TTF font file -- check common system locations. + * Load an image file (JPEG, PNG, WebP, GIF) and return a GdImage resource. */ - private static function findFont(): ?string + private static function loadImage(string $path): ?\GdImage { - $candidates = [ - JPATH_ROOT . '/media/com_mokosuitecross/fonts/OpenSans-Bold.ttf', - JPATH_ROOT . '/media/com_mokosuitecross/fonts/Roboto-Bold.ttf', - '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', - '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf', - '/usr/share/fonts/TTF/DejaVuSans-Bold.ttf', - 'C:/Windows/Fonts/arial.ttf', - 'C:/Windows/Fonts/segoeui.ttf', - ]; - - foreach ($candidates as $path) { - if (is_file($path)) { - return $path; - } + if (!is_file($path)) { + return null; } - return null; + $info = @getimagesize($path); + + if ($info === false) { + return null; + } + + $image = match ($info[2]) { + IMAGETYPE_JPEG => @imagecreatefromjpeg($path), + IMAGETYPE_PNG => @imagecreatefrompng($path), + IMAGETYPE_GIF => @imagecreatefromgif($path), + IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($path) : false, + default => false, + }; + + return ($image instanceof \GdImage) ? $image : null; } /** - * @return int[] [r, g, b] + * Convert a hex colour string to an [R, G, B] array. + * + * @param string $hex Hex colour (e.g. #FF00AA or FF00AA). + * + * @return int[] [red, green, blue] each 0-255. */ private static function hexToRgb(string $hex): array { $hex = ltrim($hex, '#'); - if (\strlen($hex) === 3) { + if (strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } @@ -204,4 +464,4 @@ class SocialImageHelper (int) hexdec(substr($hex, 4, 2)), ]; } -} +} \ No newline at end of file -- 2.52.0 From 437189830fe84fa43dbef7772fe46d04c395143e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 28 Jun 2026 12:54:27 -0500 Subject: [PATCH 03/10] feat: add social image generator with GD text overlay (#157) Replace complex multi-platform compositing with simpler spec-compliant implementation: - SocialImageHelper: 1200x630 OG images with solid background, title overlay using TTF fonts (or GD fallback), and site name watermark - SocialImageController: AJAX endpoint with CSRF + ACL checks - Config: enabled toggle, bg/text color, font size, show site name - Content plugin: Generate Social Image button in Share Content panel - Saves to media/com_mokosuitecross/social/ with SHA-256 filename Authored-by: Moko Consulting --- CHANGELOG.md | 6 +- source/packages/com_mokosuitecross/config.xml | 86 +--- .../language/en-GB/com_mokosuitecross.ini | 33 +- .../src/Controller/ImageController.php | 148 ------ .../src/Controller/SocialImageController.php | 90 ++++ .../src/Helper/SocialImageHelper.php | 478 ++++-------------- .../src/Extension/MokoSuiteCrossContent.php | 47 ++ 7 files changed, 291 insertions(+), 597 deletions(-) delete mode 100644 source/packages/com_mokosuitecross/src/Controller/ImageController.php create mode 100644 source/packages/com_mokosuitecross/src/Controller/SocialImageController.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc6d605..86a037a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,9 @@ - **Analytics service filter**: Filter heatmap and stats by service type with configurable date range - **Analytics service breakdown**: Per-service success rate, failure count, and average posts per day - **Analytics AJAX endpoint**: JSON heatmap data for dynamic filtering without page reload -- **Social image generator**: Auto-generate OG/share images with text overlay, logo, and gradient fallback (#157) -- **Social image sizes**: Platform-specific dimensions for Facebook (1200x630), Twitter (1200x675), Instagram (1080x1080), and Stories (1080x1920) -- **Social image config**: Overlay color/opacity, text color/position, gradient colors, logo upload in component options (#157) +- **Social image generator**: Generate branded 1200x630 OG images with article title overlay using PHP GD (#157) +- **Social image config**: Background color, text color, font size, and site name branding options (#157) +- **Generate Social Image button**: One-click image generation in the Share Content panel (#157) - **AI caption generation**: Generate platform-optimized cross-post captions from article content using Claude or OpenAI (#161) - **AI provider config**: New "AI Caption Generation" fieldset in component options with provider, API key, model, and tone settings - **AI Generate button**: One-click AI generation button in the Share Content panel that fills all caption fields diff --git a/source/packages/com_mokosuitecross/config.xml b/source/packages/com_mokosuitecross/config.xml index 29f0fa70..8385117d 100644 --- a/source/packages/com_mokosuitecross/config.xml +++ b/source/packages/com_mokosuitecross/config.xml @@ -266,7 +266,7 @@
-
+
JYES - - - - - - - - - - - - - - - + + + +
diff --git a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini index f2531646..f4ce7f59 100644 --- a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini +++ b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini @@ -573,26 +573,21 @@ COM_MOKOSUITECROSS_AI_ERROR="AI generation failed: %s" ; Social Image Generator COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE="Social Image Generator" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED="Enable Social Image Generator" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED_DESC="Automatically generate OG/share images with text overlay for cross-posted articles. Requires the PHP GD extension." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_COLOR="Overlay Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_COLOR_DESC="Color of the semi-transparent overlay drawn on top of the background image or gradient." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_OPACITY="Overlay Opacity" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_OVERLAY_OPACITY_DESC="Opacity of the overlay from 0 (fully transparent) to 100 (fully opaque). Default is 60." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED="Enable Social Images" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED_DESC="Generate branded OG images with article title overlay for social sharing." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_BG_COLOR="Background Color" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_BG_COLOR_DESC="Hex color for the image background (e.g. #1a1a2e)." COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR="Text Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR_DESC="Color of the article title text rendered on the social image." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_POSITION="Text Position" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_POSITION_DESC="Vertical position of the title text on the generated image." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_BOTTOM="Bottom" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_CENTER="Center" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_POSITION_TOP="Top" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_START="Gradient Start Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_START_DESC="Starting color (top) for the gradient background used when no article image is available." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_END="Gradient End Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_GRADIENT_END_DESC="Ending color (bottom) for the gradient background used when no article image is available." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_LOGO="Logo" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_LOGO_DESC="Logo image placed in the top-right corner of generated social images. Scaled to 15%% of canvas width." -COM_MOKOSUITECROSS_AI_NOT_CONFIGURED="AI is not configured. Go to Options to set up a provider and API key." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR_DESC="Hex color for the title text overlay." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_FONT_SIZE="Font Size" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_FONT_SIZE_DESC="Font size in pixels for the title text (24-96)." +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_SHOW_SITE_NAME="Show Site Name" +COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_SHOW_SITE_NAME_DESC="Display the site name in the bottom-right corner of generated images." +COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATE="Generate Social Image" +COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATING="Generating image..." +COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATED="Social image generated." +COM_MOKOSUITECROSS_SOCIAL_IMAGE_ERROR="Image generation failed: %s" +COM_MOKOSUITECROSS_SOCIAL_IMAGE_NOT_CONFIGURED="Social image generator is not enabled. Go to Options to enable it." ; Analytics COM_MOKOSUITECROSS_SUBMENU_ANALYTICS="Analytics" diff --git a/source/packages/com_mokosuitecross/src/Controller/ImageController.php b/source/packages/com_mokosuitecross/src/Controller/ImageController.php deleted file mode 100644 index 557242d2..00000000 --- a/source/packages/com_mokosuitecross/src/Controller/ImageController.php +++ /dev/null @@ -1,148 +0,0 @@ - - * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. - * @license GNU General Public License version 3 or later; see LICENSE - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; - -defined('_JEXEC') or die; - -use Joomla\CMS\Component\ComponentHelper; -use Joomla\CMS\Factory; -use Joomla\CMS\MVC\Controller\BaseController; -use Joomla\CMS\Session\Session; -use Joomla\CMS\Uri\Uri; -use Joomla\Component\MokoSuiteCross\Administrator\Helper\SocialImageHelper; - -class ImageController extends BaseController -{ - /** - * AJAX endpoint to generate a social image for an article and platform. - * - * Expected GET parameters: - * - article_id (int) The Joomla article ID. - * - platform (string) Platform key (facebook, twitter, instagram, stories). - * - * @return void - */ - public function generate(): void - { - if (!Session::checkToken('get')) { - echo json_encode(['success' => false, 'error' => 'Invalid token']); - $this->app->close(); - - return; - } - - $user = $this->app->getIdentity(); - - if (!$user->authorise('core.edit', 'com_mokosuitecross')) { - echo json_encode(['success' => false, 'error' => 'Permission denied']); - $this->app->close(); - - return; - } - - $articleId = $this->input->getInt('article_id', 0); - $platform = $this->input->getCmd('platform', 'facebook'); - - if ($articleId < 1) { - echo json_encode(['success' => false, 'error' => 'Missing article ID']); - $this->app->close(); - - return; - } - - if (!in_array($platform, SocialImageHelper::getSupportedPlatforms(), true)) { - echo json_encode(['success' => false, 'error' => 'Unsupported platform: ' . $platform]); - $this->app->close(); - - return; - } - - // Load article - $db = Factory::getDbo(); - $query = $db->getQuery(true) - ->select($db->quoteName(['id', 'title', 'images'])) - ->from($db->quoteName('#__content')) - ->where($db->quoteName('id') . ' = ' . $articleId); - $db->setQuery($query); - $article = $db->loadObject(); - - if (!$article) { - echo json_encode(['success' => false, 'error' => 'Article not found']); - $this->app->close(); - - return; - } - - // Extract intro image path from the article images JSON field - $imagePath = ''; - $images = json_decode($article->images ?? '{}', true); - - if (!empty($images['image_intro'])) { - $candidate = JPATH_ROOT . '/' . ltrim($images['image_intro'], '/'); - - if (is_file($candidate)) { - $imagePath = $candidate; - } - } - - if ($imagePath === '' && !empty($images['image_fulltext'])) { - $candidate = JPATH_ROOT . '/' . ltrim($images['image_fulltext'], '/'); - - if (is_file($candidate)) { - $imagePath = $candidate; - } - } - - // Build config from component params - $params = ComponentHelper::getParams('com_mokosuitecross'); - - $logoRelative = $params->get('social_image_logo', ''); - $logoPath = ''; - - if ($logoRelative !== '') { - $candidate = JPATH_ROOT . '/' . ltrim($logoRelative, '/'); - - if (is_file($candidate)) { - $logoPath = $candidate; - } - } - - $config = [ - 'article_id' => $articleId, - 'overlay_color' => $params->get('social_image_overlay_color', '#000000'), - 'overlay_opacity' => (int) $params->get('social_image_overlay_opacity', 60), - 'text_color' => $params->get('social_image_text_color', '#FFFFFF'), - 'text_position' => $params->get('social_image_text_position', 'bottom'), - 'gradient_start' => $params->get('social_image_gradient_start', '#1a1a2e'), - 'gradient_end' => $params->get('social_image_gradient_end', '#16213e'), - 'logo_path' => $logoPath, - 'font_path' => '', - ]; - - try { - $relativePath = SocialImageHelper::generate($article->title, $imagePath, $platform, $config); - $url = Uri::root() . $relativePath; - - $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); - echo json_encode([ - 'success' => true, - 'path' => $relativePath, - 'url' => $url, - ]); - } catch (\RuntimeException $e) { - $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); - echo json_encode(['success' => false, 'error' => $e->getMessage()]); - } - - $this->app->close(); - } -} \ No newline at end of file diff --git a/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php b/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php new file mode 100644 index 00000000..c3010299 --- /dev/null +++ b/source/packages/com_mokosuitecross/src/Controller/SocialImageController.php @@ -0,0 +1,90 @@ + + * @copyright Copyright (C) 2026 Moko Consulting. All rights reserved. + * @license GNU General Public License version 3 or later; see LICENSE + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; + +defined('_JEXEC') or die; + +use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Factory; +use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Session\Session; +use Joomla\Component\MokoSuiteCross\Administrator\Helper\SocialImageHelper; + +class SocialImageController extends BaseController +{ + public function generate(): void + { + if (!Session::checkToken('get')) { + echo json_encode(['success' => false, 'error' => 'Invalid token']); + $this->app->close(); + + return; + } + + $user = $this->app->getIdentity(); + + if (!$user->authorise('core.edit', 'com_mokosuitecross')) { + echo json_encode(['success' => false, 'error' => 'Permission denied']); + $this->app->close(); + + return; + } + + $articleId = $this->input->getInt('article_id', 0); + + if ($articleId < 1) { + echo json_encode(['success' => false, 'error' => 'Missing article ID']); + $this->app->close(); + + return; + } + + $params = ComponentHelper::getParams('com_mokosuitecross'); + + if (!(int) $params->get('social_image_enabled', 0)) { + echo json_encode(['success' => false, 'error' => 'Social image generator is not enabled']); + $this->app->close(); + + return; + } + + $db = Factory::getDbo(); + $query = $db->getQuery(true) + ->select($db->quoteName('title')) + ->from($db->quoteName('#__content')) + ->where($db->quoteName('id') . ' = ' . $articleId); + $db->setQuery($query); + $title = $db->loadResult(); + + if (!$title) { + echo json_encode(['success' => false, 'error' => 'Article not found']); + $this->app->close(); + + return; + } + + $siteName = $this->app->get('sitename', ''); + + $config = [ + 'bg_color' => $params->get('social_image_bg_color', '#1a1a2e'), + 'text_color' => $params->get('social_image_text_color', '#ffffff'), + 'font_size' => $params->get('social_image_font_size', 48), + 'show_site_name' => (bool) $params->get('social_image_show_site_name', 1), + ]; + + $result = SocialImageHelper::generate($title, $siteName, $config); + + $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); + echo json_encode($result); + $this->app->close(); + } +} diff --git a/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php b/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php index 5d06d6d3..701f0395 100644 --- a/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php +++ b/source/packages/com_mokosuitecross/src/Helper/SocialImageHelper.php @@ -13,396 +13,142 @@ namespace Joomla\Component\MokoSuiteCross\Administrator\Helper; defined('_JEXEC') or die; -/** - * Social image generator using PHP GD. - * - * Produces platform-sized OG/share images with text overlay, logo, and gradient fallback. - */ class SocialImageHelper { - /** - * Platform canvas dimensions (width x height). - */ - private const PLATFORMS = [ - 'facebook' => [1200, 630], - 'twitter' => [1200, 675], - 'instagram' => [1080, 1080], - 'stories' => [1080, 1920], - ]; + private const WIDTH = 1200; + private const HEIGHT = 630; /** - * Maximum logo width as a fraction of canvas width. - */ - private const LOGO_MAX_WIDTH_RATIO = 0.15; - - /** - * Logo inset from the top-right corner in pixels. - */ - private const LOGO_INSET = 20; - - /** - * Generate a social image for the given platform. + * Generate a branded social/OG image with text overlay. * - * @param string $title Article title for text overlay. - * @param string $imagePath Absolute path to the source image (may be empty). - * @param string $platform Platform key (facebook, twitter, instagram, stories). - * @param array $config Configuration array: - * - article_id (int) Required for output filename. - * - overlay_color (string) Hex colour, default #000000. - * - overlay_opacity (int) 0-100, default 60. - * - text_color (string) Hex colour, default #FFFFFF. - * - text_position (string) top|center|bottom, default bottom. - * - gradient_start (string) Hex colour, default #1a1a2e. - * - gradient_end (string) Hex colour, default #16213e. - * - logo_path (string) Absolute path to logo image. - * - font_path (string) Absolute path to TTF font. + * @param string $title Article title to render on the image + * @param string $siteName Site name for branding watermark + * @param array $config Rendering config: bg_color, text_color, font_size, show_site_name * - * @return string Relative path to the generated image (from site root). - * - * @throws \RuntimeException If GD is unavailable or generation fails. + * @return array ['success' => bool, 'image_url' => string, 'error' => string] */ - public static function generate(string $title, string $imagePath, string $platform, array $config): string + public static function generate(string $title, string $siteName, array $config): array { - if (!extension_loaded('gd')) { - throw new \RuntimeException('PHP GD extension is required for social image generation.'); + if (!\function_exists('imagecreatetruecolor')) { + return ['success' => false, 'error' => 'PHP GD extension is not available']; } - if (!isset(self::PLATFORMS[$platform])) { - throw new \RuntimeException('Unsupported platform: ' . $platform); + $bgColor = $config['bg_color'] ?? '#1a1a2e'; + $textColor = $config['text_color'] ?? '#ffffff'; + $fontSize = (int) ($config['font_size'] ?? 48); + $showSiteName = (bool) ($config['show_site_name'] ?? true); + + $fontSize = max(24, min(96, $fontSize)); + + $image = imagecreatetruecolor(self::WIDTH, self::HEIGHT); + + if ($image === false) { + return ['success' => false, 'error' => 'Failed to create image canvas']; } - [$canvasW, $canvasH] = self::PLATFORMS[$platform]; + $bgRgb = self::hexToRgb($bgColor); + $textRgb = self::hexToRgb($textColor); - $canvas = imagecreatetruecolor($canvasW, $canvasH); + $bg = imagecolorallocate($image, $bgRgb[0], $bgRgb[1], $bgRgb[2]); + $text = imagecolorallocate($image, $textRgb[0], $textRgb[1], $textRgb[2]); - if ($canvas === false) { - throw new \RuntimeException('Failed to create image canvas.'); - } + imagefilledrectangle($image, 0, 0, self::WIDTH - 1, self::HEIGHT - 1, $bg); - // --- Background: source image or gradient fallback --- - if ($imagePath !== '' && is_file($imagePath)) { - self::drawSourceImage($canvas, $imagePath, $canvasW, $canvasH); + $fontFile = self::findFont(); + + if ($fontFile !== null) { + self::renderTtfText($image, $title, $text, $fontSize, $fontFile); + + if ($showSiteName && $siteName !== '') { + $siteSize = (int) round($fontSize * 0.45); + $siteBox = imagettfbbox($siteSize, 0, $fontFile, $siteName); + $siteX = self::WIDTH - ($siteBox[2] - $siteBox[0]) - 40; + $siteY = self::HEIGHT - 30; + imagettftext($image, $siteSize, 0, $siteX, $siteY, $text, $fontFile, $siteName); + } } else { - self::drawGradient( - $canvas, - $canvasW, - $canvasH, - $config['gradient_start'] ?? '#1a1a2e', - $config['gradient_end'] ?? '#16213e' - ); + self::renderFallbackText($image, $title, $text); + + if ($showSiteName && $siteName !== '') { + $siteX = self::WIDTH - (\strlen($siteName) * imagefontwidth(3)) - 40; + $siteY = self::HEIGHT - 30; + imagestring($image, 3, $siteX, $siteY, $siteName, $text); + } } - // --- Semi-transparent overlay --- - self::drawOverlay( - $canvas, - $canvasW, - $canvasH, - $config['overlay_color'] ?? '#000000', - (int) ($config['overlay_opacity'] ?? 60) - ); - - // --- Logo (top-right) --- - if (!empty($config['logo_path']) && is_file($config['logo_path'])) { - self::drawLogo($canvas, $config['logo_path'], $canvasW); - } - - // --- Title text --- - self::drawText( - $canvas, - $title, - $canvasW, - $canvasH, - $config['text_color'] ?? '#FFFFFF', - $config['text_position'] ?? 'bottom', - $config['font_path'] ?? '' - ); - - // --- Write output --- - $articleId = (int) ($config['article_id'] ?? 0); - $outputDir = JPATH_ROOT . '/images/mokosuitecross'; + $outputDir = JPATH_ROOT . '/media/com_mokosuitecross/social'; if (!is_dir($outputDir)) { mkdir($outputDir, 0755, true); } - $filename = $articleId . '_' . $platform . '.jpg'; - $outputPath = $outputDir . '/' . $filename; + $hash = hash('sha256', $title . $bgColor . $textColor . $fontSize); + $filename = $hash . '.png'; + $filePath = $outputDir . '/' . $filename; - if (!imagejpeg($canvas, $outputPath, 90)) { - imagedestroy($canvas); + if (!imagepng($image, $filePath, 6)) { + imagedestroy($image); - throw new \RuntimeException('Failed to write image: ' . $outputPath); + return ['success' => false, 'error' => 'Failed to save image file']; } - imagedestroy($canvas); + imagedestroy($image); - return 'images/mokosuitecross/' . $filename; + $imageUrl = 'media/com_mokosuitecross/social/' . $filename; + + return ['success' => true, 'image_url' => $imageUrl]; } - /** - * Remove all cached social images for an article. - * - * @param int $articleId The article ID. - * - * @return void - */ - public static function clearCache(int $articleId): void + private static function renderTtfText(\GdImage $image, string $title, int $color, int $fontSize, string $fontFile): void { - $dir = JPATH_ROOT . '/images/mokosuitecross'; + $maxWidth = self::WIDTH - 120; + $lines = self::wordWrapTtf($title, $fontFile, $fontSize, $maxWidth); + $lineHeight = (int) round($fontSize * 1.4); + $totalHeight = \count($lines) * $lineHeight; - if (!is_dir($dir)) { - return; + $startY = (int) round((self::HEIGHT - $totalHeight) / 2) + $fontSize; + + foreach ($lines as $i => $line) { + $y = $startY + ($i * $lineHeight); + imagettftext($image, $fontSize, 0, 60, $y, $color, $fontFile, $line); } + } - foreach (self::PLATFORMS as $platform => $dims) { - $file = $dir . '/' . $articleId . '_' . $platform . '.jpg'; + private static function renderFallbackText(\GdImage $image, string $title, int $color): void + { + $font = 5; + $charWidth = imagefontwidth($font); + $charHeight = imagefontheight($font); + $maxChars = (int) floor((self::WIDTH - 120) / $charWidth); + $lines = wordwrap($title, $maxChars, "\n", true); + $lineArray = explode("\n", $lines); + $lineHeight = $charHeight + 8; + $totalHeight = \count($lineArray) * $lineHeight; + $startY = (int) round((self::HEIGHT - $totalHeight) / 2); - if (is_file($file)) { - @unlink($file); - } + foreach ($lineArray as $i => $line) { + $y = $startY + ($i * $lineHeight); + imagestring($image, $font, 60, $y, $line, $color); } } /** - * Return the list of supported platform keys. + * Word-wrap text for TTF rendering at a given pixel width. * * @return string[] */ - public static function getSupportedPlatforms(): array + private static function wordWrapTtf(string $text, string $fontFile, int $fontSize, int $maxWidth): array { - return array_keys(self::PLATFORMS); - } - - // ------------------------------------------------------------------ - // Private drawing helpers - // ------------------------------------------------------------------ - - /** - * Load a source image and resize/crop to fill the canvas. - */ - private static function drawSourceImage(\GdImage $canvas, string $path, int $canvasW, int $canvasH): void - { - $source = self::loadImage($path); - - if ($source === null) { - return; - } - - $srcW = imagesx($source); - $srcH = imagesy($source); - - // Centre-crop resize (cover) - $scale = max($canvasW / $srcW, $canvasH / $srcH); - $newW = (int) round($srcW * $scale); - $newH = (int) round($srcH * $scale); - $offX = (int) round(($canvasW - $newW) / 2); - $offY = (int) round(($canvasH - $newH) / 2); - - imagecopyresampled($canvas, $source, $offX, $offY, 0, 0, $newW, $newH, $srcW, $srcH); - imagedestroy($source); - } - - /** - * Draw a vertical linear gradient as a background. - */ - private static function drawGradient(\GdImage $canvas, int $w, int $h, string $startHex, string $endHex): void - { - [$r1, $g1, $b1] = self::hexToRgb($startHex); - [$r2, $g2, $b2] = self::hexToRgb($endHex); - - for ($y = 0; $y < $h; $y++) { - $ratio = $y / max($h - 1, 1); - $r = (int) round($r1 + ($r2 - $r1) * $ratio); - $g = (int) round($g1 + ($g2 - $g1) * $ratio); - $b = (int) round($b1 + ($b2 - $b1) * $ratio); - $color = imagecolorallocate($canvas, $r, $g, $b); - imageline($canvas, 0, $y, $w - 1, $y, $color); - imagecolordeallocate($canvas, $color); - } - } - - /** - * Draw a semi-transparent overlay rectangle. - */ - private static function drawOverlay(\GdImage $canvas, int $w, int $h, string $hex, int $opacity): void - { - if ($opacity < 1) { - return; - } - - $opacity = min($opacity, 100); - [$r, $g, $b] = self::hexToRgb($hex); - - // GD alpha: 0 = opaque, 127 = fully transparent - $alpha = (int) round(127 - ($opacity / 100 * 127)); - $color = imagecolorallocatealpha($canvas, $r, $g, $b, $alpha); - imagefilledrectangle($canvas, 0, 0, $w - 1, $h - 1, $color); - imagecolordeallocate($canvas, $color); - } - - /** - * Draw a logo in the top-right corner, scaled to at most 15% of canvas width. - */ - private static function drawLogo(\GdImage $canvas, string $logoPath, int $canvasW): void - { - $logo = self::loadImage($logoPath); - - if ($logo === null) { - return; - } - - $logoW = imagesx($logo); - $logoH = imagesy($logo); - $maxW = (int) round($canvasW * self::LOGO_MAX_WIDTH_RATIO); - - if ($logoW > $maxW) { - $scale = $maxW / $logoW; - $newW = $maxW; - $newH = (int) round($logoH * $scale); - $scaled = imagecreatetruecolor($newW, $newH); - - imagesavealpha($scaled, true); - $transparent = imagecolorallocatealpha($scaled, 0, 0, 0, 127); - imagefill($scaled, 0, 0, $transparent); - imagecopyresampled($scaled, $logo, 0, 0, 0, 0, $newW, $newH, $logoW, $logoH); - imagedestroy($logo); - $logo = $scaled; - $logoW = $newW; - $logoH = $newH; - } - - $x = $canvasW - $logoW - self::LOGO_INSET; - $y = self::LOGO_INSET; - - imagecopy($canvas, $logo, $x, $y, 0, 0, $logoW, $logoH); - imagedestroy($logo); - } - - /** - * Draw word-wrapped title text onto the canvas. - */ - private static function drawText( - \GdImage $canvas, - string $title, - int $canvasW, - int $canvasH, - string $colorHex, - string $position, - string $fontPath - ): void { - if ($title === '') { - return; - } - - [$r, $g, $b] = self::hexToRgb($colorHex); - $color = imagecolorallocate($canvas, $r, $g, $b); - $padding = (int) round($canvasW * 0.06); - - $useTtf = ($fontPath !== '' && is_file($fontPath) && function_exists('imagettftext')); - - if ($useTtf) { - self::drawTtfText($canvas, $title, $fontPath, $color, $canvasW, $canvasH, $padding, $position); - } else { - self::drawGdText($canvas, $title, $color, $canvasW, $canvasH, $padding, $position); - } - } - - /** - * Render text using a TrueType font with automatic word wrapping. - */ - private static function drawTtfText( - \GdImage $canvas, - string $title, - string $fontPath, - int $color, - int $canvasW, - int $canvasH, - int $padding, - string $position - ): void { - $maxTextW = $canvasW - ($padding * 2); - $fontSize = (int) round($canvasH * 0.055); - $fontSize = max(16, min($fontSize, 64)); - - $lines = self::wordWrapTtf($title, $fontPath, $fontSize, $maxTextW); - $lineHeight = (int) round($fontSize * 1.35); - $totalH = count($lines) * $lineHeight; - - $y = match ($position) { - 'top' => $padding + $fontSize, - 'center' => (int) round(($canvasH - $totalH) / 2) + $fontSize, - default => $canvasH - $padding - $totalH + $fontSize, - }; - - foreach ($lines as $line) { - $box = imagettfbbox($fontSize, 0, $fontPath, $line); - $textW = abs($box[2] - $box[0]); - $x = (int) round(($canvasW - $textW) / 2); - - // Draw text shadow for readability - $shadow = imagecolorallocatealpha($canvas, 0, 0, 0, 60); - imagettftext($canvas, $fontSize, 0, $x + 2, $y + 2, $shadow, $fontPath, $line); - imagecolordeallocate($canvas, $shadow); - - imagettftext($canvas, $fontSize, 0, $x, $y, $color, $fontPath, $line); - $y += $lineHeight; - } - } - - /** - * Render text using the built-in GD bitmap font (fallback when no TTF is available). - */ - private static function drawGdText( - \GdImage $canvas, - string $title, - int $color, - int $canvasW, - int $canvasH, - int $padding, - string $position - ): void { - $font = 5; // largest built-in GD font - $charW = imagefontwidth($font); - $charH = imagefontheight($font); - $maxChars = (int) floor(($canvasW - $padding * 2) / $charW); - $maxChars = max($maxChars, 10); - - $wrapped = wordwrap($title, $maxChars, "\n", true); - $lines = explode("\n", $wrapped); - $lineHeight = $charH + 4; - $totalH = count($lines) * $lineHeight; - - $y = match ($position) { - 'top' => $padding, - 'center' => (int) round(($canvasH - $totalH) / 2), - default => $canvasH - $padding - $totalH, - }; - - foreach ($lines as $line) { - $textW = mb_strlen($line) * $charW; - $x = (int) round(($canvasW - $textW) / 2); - imagestring($canvas, $font, $x, $y, $line, $color); - $y += $lineHeight; - } - } - - /** - * Word-wrap a string to fit within a pixel width using TTF metrics. - * - * @return string[] - */ - private static function wordWrapTtf(string $text, string $fontPath, int $fontSize, int $maxWidth): array - { - $words = preg_split('/\s+/', $text); - $lines = []; + $words = explode(' ', $text); + $lines = []; $currentLine = ''; foreach ($words as $word) { - $testLine = $currentLine === '' ? $word : $currentLine . ' ' . $word; - $box = imagettfbbox($fontSize, 0, $fontPath, $testLine); - $lineWidth = abs($box[2] - $box[0]); + $testLine = $currentLine === '' ? $word : $currentLine . ' ' . $word; + $box = imagettfbbox($fontSize, 0, $fontFile, $testLine); + $width = abs($box[2] - $box[0]); - if ($lineWidth > $maxWidth && $currentLine !== '') { + if ($width > $maxWidth && $currentLine !== '') { $lines[] = $currentLine; $currentLine = $word; } else { @@ -418,43 +164,37 @@ class SocialImageHelper } /** - * Load an image file (JPEG, PNG, WebP, GIF) and return a GdImage resource. + * Locate a usable TTF font file -- check common system locations. */ - private static function loadImage(string $path): ?\GdImage + private static function findFont(): ?string { - if (!is_file($path)) { - return null; + $candidates = [ + JPATH_ROOT . '/media/com_mokosuitecross/fonts/OpenSans-Bold.ttf', + JPATH_ROOT . '/media/com_mokosuitecross/fonts/Roboto-Bold.ttf', + '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', + '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf', + '/usr/share/fonts/TTF/DejaVuSans-Bold.ttf', + 'C:/Windows/Fonts/arial.ttf', + 'C:/Windows/Fonts/segoeui.ttf', + ]; + + foreach ($candidates as $path) { + if (is_file($path)) { + return $path; + } } - $info = @getimagesize($path); - - if ($info === false) { - return null; - } - - $image = match ($info[2]) { - IMAGETYPE_JPEG => @imagecreatefromjpeg($path), - IMAGETYPE_PNG => @imagecreatefrompng($path), - IMAGETYPE_GIF => @imagecreatefromgif($path), - IMAGETYPE_WEBP => function_exists('imagecreatefromwebp') ? @imagecreatefromwebp($path) : false, - default => false, - }; - - return ($image instanceof \GdImage) ? $image : null; + return null; } /** - * Convert a hex colour string to an [R, G, B] array. - * - * @param string $hex Hex colour (e.g. #FF00AA or FF00AA). - * - * @return int[] [red, green, blue] each 0-255. + * @return int[] [r, g, b] */ private static function hexToRgb(string $hex): array { $hex = ltrim($hex, '#'); - if (strlen($hex) === 3) { + if (\strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } @@ -464,4 +204,4 @@ class SocialImageHelper (int) hexdec(substr($hex, 4, 2)), ]; } -} \ No newline at end of file +} diff --git a/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php b/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php index 3b3c7e28..99fe1bb1 100644 --- a/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php +++ b/source/packages/plg_content_mokosuitecross/src/Extension/MokoSuiteCrossContent.php @@ -257,6 +257,53 @@ XML; $form->load($aiXml); $form->setFieldAttribute('mokosuitecross_ai_generate', 'description', $aiButtonHtml, 'attribs'); } + // Social Image Generator button (#157) + $siParams = ComponentHelper::getParams('com_mokosuitecross'); + $siEnabled = (bool) $siParams->get('social_image_enabled', 0); + + if ($siEnabled && $articleId > 0) { + $siToken = Session::getFormToken(); + $siUrl = Uri::base() . 'index.php?option=com_mokosuitecross&task=socialimage.generate&format=raw&article_id=' . $articleId . '&' . $siToken . '=1'; + + $siButtonHtml = '
' + . '' + . '' + . '' + . '
' + . ''; + + $siXml = ' +
+ +
'; + $form->load($siXml); + $form->setFieldAttribute('mokosuitecross_si_generate', 'description', $siButtonHtml, 'attribs'); + } // Cross-post history panel for existing articles -- 2.52.0 From febaa856c5b9a87e2ff75cd6d88a6904e9be6557 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Mon, 29 Jun 2026 11:34:43 +0000 Subject: [PATCH 04/10] chore(version): pre-release bump to 01.12.03-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.12.03.sql | 1 + source/packages/plg_content_mokosuitecross/mokosuitecross.xml | 2 +- source/packages/plg_mokosuitecross_activitypub/activitypub.xml | 2 +- source/packages/plg_mokosuitecross_blogger/blogger.xml | 2 +- source/packages/plg_mokosuitecross_bluesky/bluesky.xml | 2 +- source/packages/plg_mokosuitecross_brevo/brevo.xml | 2 +- .../plg_mokosuitecross_constantcontact/constantcontact.xml | 2 +- source/packages/plg_mokosuitecross_convertkit/convertkit.xml | 2 +- source/packages/plg_mokosuitecross_devto/devto.xml | 2 +- source/packages/plg_mokosuitecross_discord/discord.xml | 2 +- source/packages/plg_mokosuitecross_facebook/facebook.xml | 2 +- source/packages/plg_mokosuitecross_ghost/ghost.xml | 2 +- .../plg_mokosuitecross_googlebusiness/googlebusiness.xml | 2 +- source/packages/plg_mokosuitecross_googlechat/googlechat.xml | 2 +- source/packages/plg_mokosuitecross_hashnode/hashnode.xml | 2 +- source/packages/plg_mokosuitecross_instagram/instagram.xml | 2 +- source/packages/plg_mokosuitecross_linkedin/linkedin.xml | 2 +- source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml | 2 +- source/packages/plg_mokosuitecross_mastodon/mastodon.xml | 2 +- source/packages/plg_mokosuitecross_matrix/matrix.xml | 2 +- source/packages/plg_mokosuitecross_medium/medium.xml | 2 +- .../plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml | 2 +- .../plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml | 2 +- source/packages/plg_mokosuitecross_nostr/nostr.xml | 2 +- source/packages/plg_mokosuitecross_ntfy/ntfy.xml | 2 +- source/packages/plg_mokosuitecross_pinterest/pinterest.xml | 2 +- source/packages/plg_mokosuitecross_reddit/reddit.xml | 2 +- source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml | 2 +- source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml | 2 +- source/packages/plg_mokosuitecross_slack/slack.xml | 2 +- source/packages/plg_mokosuitecross_teams/teams.xml | 2 +- source/packages/plg_mokosuitecross_telegram/telegram.xml | 2 +- source/packages/plg_mokosuitecross_threads/threads.xml | 2 +- source/packages/plg_mokosuitecross_tiktok/tiktok.xml | 2 +- source/packages/plg_mokosuitecross_tumblr/tumblr.xml | 2 +- source/packages/plg_mokosuitecross_twitter/twitter.xml | 2 +- source/packages/plg_mokosuitecross_webhook/webhook.xml | 2 +- source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml | 2 +- source/packages/plg_mokosuitecross_wordpress/wordpress.xml | 2 +- source/packages/plg_mokosuitecross_youtube/youtube.xml | 2 +- source/packages/plg_system_mokosuitecross/mokosuitecross.xml | 2 +- .../plg_system_mokosuitecross_events/mokosuitecross_events.xml | 2 +- .../mokosuitecross_gallery.xml | 2 +- source/packages/plg_task_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/plg_webservices_mokosuitecross/mokosuitecross.xml | 2 +- source/pkg_mokosuitecross.xml | 2 +- 52 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.12.03.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 4da990cc..bfa4245b 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.12.01 +# VERSION: 01.12.03 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 8d215df6..d3eba2a6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.12.01 + VERSION: 01.12.03 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index d31cc6c1..c7a62529 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.12.01 + VERSION: 01.12.03 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index b8dac7e1..ba45acdb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteCross - + Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 6. diff --git a/SECURITY.md b/SECURITY.md index 86fb034e..b1fd805d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.12.01 +VERSION: 01.12.03 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index 6f39b1cc..f2008a92 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.03.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.03.sql new file mode 100644 index 00000000..910c11c4 --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.03.sql @@ -0,0 +1 @@ +/* 01.12.03 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index 63484ab1..22526fdb 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml index cea204cb..3ebb454a 100644 --- a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml +++ b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ActivityPub (Fediverse) - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_blogger/blogger.xml b/source/packages/plg_mokosuitecross_blogger/blogger.xml index 71215f67..d426099b 100644 --- a/source/packages/plg_mokosuitecross_blogger/blogger.xml +++ b/source/packages/plg_mokosuitecross_blogger/blogger.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Blogger - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml index b4aa9a3a..1b2edf5b 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_brevo/brevo.xml b/source/packages/plg_mokosuitecross_brevo/brevo.xml index 4e867426..d65a15ed 100644 --- a/source/packages/plg_mokosuitecross_brevo/brevo.xml +++ b/source/packages/plg_mokosuitecross_brevo/brevo.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Brevo (Sendinblue) - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml index 3c9a78fd..c821b2e9 100644 --- a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml +++ b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Constant Contact - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml index 28fc8a03..30f241e7 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_devto/devto.xml b/source/packages/plg_mokosuitecross_devto/devto.xml index 3e159a71..746f879d 100644 --- a/source/packages/plg_mokosuitecross_devto/devto.xml +++ b/source/packages/plg_mokosuitecross_devto/devto.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Dev.to - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_discord/discord.xml b/source/packages/plg_mokosuitecross_discord/discord.xml index c3b046e3..7572d0f6 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_facebook/facebook.xml b/source/packages/plg_mokosuitecross_facebook/facebook.xml index 3e2c17ee..4b171622 100644 --- a/source/packages/plg_mokosuitecross_facebook/facebook.xml +++ b/source/packages/plg_mokosuitecross_facebook/facebook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Facebook / Meta - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ghost/ghost.xml b/source/packages/plg_mokosuitecross_ghost/ghost.xml index 88244cfe..be3ed7d3 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml index 4cc3c281..07c2aa50 100644 --- a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml +++ b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Business Profile - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml index ab3b8545..2b32c4d8 100644 --- a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml +++ b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Chat - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml index 417cd7ca..585cffdb 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_instagram/instagram.xml b/source/packages/plg_mokosuitecross_instagram/instagram.xml index 904bea03..66559d0f 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.12.01 + 01.12.03 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml index e7c65487..dea1557c 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml index 285e97f5..15b29c12 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml index 6c01e6bb..874d5c28 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_matrix/matrix.xml b/source/packages/plg_mokosuitecross_matrix/matrix.xml index df0605b7..e073c885 100644 --- a/source/packages/plg_mokosuitecross_matrix/matrix.xml +++ b/source/packages/plg_mokosuitecross_matrix/matrix.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Matrix / Element - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_medium/medium.xml b/source/packages/plg_mokosuitecross_medium/medium.xml index ee756c90..98e38ae6 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml index f367ff4a..87e8269d 100644 --- a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml +++ b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteCalendar Events - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml index 3218a8c1..291a05b3 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_nostr/nostr.xml b/source/packages/plg_mokosuitecross_nostr/nostr.xml index c3c7de40..b90fcd03 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml index 58ab4eb3..e95d2ef9 100644 --- a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml +++ b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ntfy Push Notifications - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml index 29be32db..8dd7b416 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_reddit/reddit.xml b/source/packages/plg_mokosuitecross_reddit/reddit.xml index e17e17c2..0b27eea6 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml index 3e12bc90..045db4c1 100644 --- a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml +++ b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml @@ -1,7 +1,7 @@ MokoSuiteCross - RSS Feed - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml index dfe652ee..50b7f139 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_slack/slack.xml b/source/packages/plg_mokosuitecross_slack/slack.xml index 94bd0c8a..42989440 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_teams/teams.xml b/source/packages/plg_mokosuitecross_teams/teams.xml index 2601ee8c..f7fc311e 100644 --- a/source/packages/plg_mokosuitecross_teams/teams.xml +++ b/source/packages/plg_mokosuitecross_teams/teams.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Microsoft Teams - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_telegram/telegram.xml b/source/packages/plg_mokosuitecross_telegram/telegram.xml index 573b958a..c3ab5dae 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_threads/threads.xml b/source/packages/plg_mokosuitecross_threads/threads.xml index c42a035f..2066db29 100644 --- a/source/packages/plg_mokosuitecross_threads/threads.xml +++ b/source/packages/plg_mokosuitecross_threads/threads.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Threads (Meta) - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml index 2efa6728..09c0ab24 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml index b6a7b73a..51c99aa0 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_twitter/twitter.xml b/source/packages/plg_mokosuitecross_twitter/twitter.xml index ee4fca3b..57a16719 100644 --- a/source/packages/plg_mokosuitecross_twitter/twitter.xml +++ b/source/packages/plg_mokosuitecross_twitter/twitter.xml @@ -1,7 +1,7 @@ MokoSuiteCross - X / Twitter - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_webhook/webhook.xml b/source/packages/plg_mokosuitecross_webhook/webhook.xml index f0bbae02..0b17ec7f 100644 --- a/source/packages/plg_mokosuitecross_webhook/webhook.xml +++ b/source/packages/plg_mokosuitecross_webhook/webhook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Generic Webhook - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml index 40b2cc02..c2ecb4a0 100644 --- a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml +++ b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WhatsApp Business - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml index 682671e7..83bb0143 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_youtube/youtube.xml b/source/packages/plg_mokosuitecross_youtube/youtube.xml index c131195b..8cbf526b 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.12.01 + 01.12.03 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml index 04f79e7a..08956cd5 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml index fa9c1393..2ec4e5f2 100644 --- a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml +++ b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Events - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml index 402175ef..16816fbd 100644 --- a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml +++ b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Gallery - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml index 0dd1302e..2488ad13 100644 --- a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Task - MokoSuiteCross Queue Processor - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml index 11601451..c3f3a7c9 100644 --- a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Web Services - MokoSuiteCross - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index da1e0d08..80e2cfae 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.12.01 + 01.12.03 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From e702eb8d9eab40916a9177ce966bca77b9ff8f1e Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 28 Jun 2026 12:00:10 -0500 Subject: [PATCH 05/10] feat: add best time to post analytics with engagement heatmap (#165) Authored-by: Moko Consulting --- .../language/en-GB/com_mokosuitecross.ini | 8 + .../com_mokosuitecross/sql/install.mysql.sql | 21 ++ .../sql/updates/mysql/01.08.54.sql | 24 +- .../src/Controller/AnalyticsController.php | 77 ++++- .../src/Helper/AnalyticsHelper.php | 284 ++++++++++++------ .../tmpl/dashboard/default.php | 169 +++++++++++ 6 files changed, 484 insertions(+), 99 deletions(-) diff --git a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini index f4ce7f59..eb318bd0 100644 --- a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini +++ b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini @@ -614,6 +614,14 @@ COM_MOKOSUITECROSS_ANALYTICS_LEGEND_NONE="No data" COM_MOKOSUITECROSS_PERIOD_180_DAYS="Last 180 days" COM_MOKOSUITECROSS_PERIOD_365_DAYS="Last 365 days" + +; Analytics +COM_MOKOSUITECROSS_ANALYTICS="Analytics" +COM_MOKOSUITECROSS_ANALYTICS_BEST_TIMES="Best Times to Post" +COM_MOKOSUITECROSS_ANALYTICS_HEATMAP="Engagement Heatmap" +COM_MOKOSUITECROSS_ANALYTICS_NO_DATA="Not enough data yet. Analytics will appear after posts collect engagement metrics." +COM_MOKOSUITECROSS_ANALYTICS_ENGAGEMENT_RATE="Engagement Rate" +COM_MOKOSUITECROSS_ANALYTICS_ALL_PLATFORMS="All Platforms" ; Category Rules COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES="Category Rules" COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES_NOTE="Category Routing" diff --git a/source/packages/com_mokosuitecross/sql/install.mysql.sql b/source/packages/com_mokosuitecross/sql/install.mysql.sql index 0a788f58..e40c8a08 100644 --- a/source/packages/com_mokosuitecross/sql/install.mysql.sql +++ b/source/packages/com_mokosuitecross/sql/install.mysql.sql @@ -96,6 +96,27 @@ INSERT INTO `#__mokosuitecross_templates` (`service_type`, `title`, `template_bo ('instagram', 'Instagram Default', '{social}\n\n{hashtags}', 1, 21, NOW()), ('youtube', 'YouTube Default', '{social}\n\n{url}', 1, 22, NOW()); + +CREATE TABLE IF NOT EXISTS `#__mokosuitecross_analytics` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `post_id` int unsigned NOT NULL, + `service_id` int unsigned NOT NULL, + `service_type` varchar(50) NOT NULL DEFAULT '', + `posted_at` datetime DEFAULT NULL, + `day_of_week` tinyint unsigned NOT NULL DEFAULT 0, + `hour_of_day` tinyint unsigned NOT NULL DEFAULT 0, + `impressions` int unsigned NOT NULL DEFAULT 0, + `engagements` int unsigned NOT NULL DEFAULT 0, + `clicks` int unsigned NOT NULL DEFAULT 0, + `shares` int unsigned NOT NULL DEFAULT 0, + `engagement_rate` decimal(5,2) NOT NULL DEFAULT 0.00, + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_service_type` (`service_type`), + KEY `idx_day_hour` (`day_of_week`, `hour_of_day`), + KEY `idx_post` (`post_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + CREATE TABLE IF NOT EXISTS `#__mokosuitecross_category_rules` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `category_id` int(10) unsigned NOT NULL, diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.54.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.54.sql index ac182a97..8cba2fa6 100644 --- a/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.54.sql +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.54.sql @@ -1 +1,23 @@ -/* 01.08.54 — no schema changes */ +-- MokoSuiteCross 01.08.54 -- Best time to post analytics +-- Copyright (C) 2026 Moko Consulting. All rights reserved. +-- SPDX-License-Identifier: GPL-3.0-or-later + +CREATE TABLE IF NOT EXISTS `#__mokosuitecross_analytics` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `post_id` int unsigned NOT NULL, + `service_id` int unsigned NOT NULL, + `service_type` varchar(50) NOT NULL DEFAULT '', + `posted_at` datetime DEFAULT NULL, + `day_of_week` tinyint unsigned NOT NULL DEFAULT 0, + `hour_of_day` tinyint unsigned NOT NULL DEFAULT 0, + `impressions` int unsigned NOT NULL DEFAULT 0, + `engagements` int unsigned NOT NULL DEFAULT 0, + `clicks` int unsigned NOT NULL DEFAULT 0, + `shares` int unsigned NOT NULL DEFAULT 0, + `engagement_rate` decimal(5,2) NOT NULL DEFAULT 0.00, + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_service_type` (`service_type`), + KEY `idx_day_hour` (`day_of_week`, `hour_of_day`), + KEY `idx_post` (`post_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/source/packages/com_mokosuitecross/src/Controller/AnalyticsController.php b/source/packages/com_mokosuitecross/src/Controller/AnalyticsController.php index fce9f59b..d7eb2e1e 100644 --- a/source/packages/com_mokosuitecross/src/Controller/AnalyticsController.php +++ b/source/packages/com_mokosuitecross/src/Controller/AnalyticsController.php @@ -14,11 +14,84 @@ namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; defined('_JEXEC') or die; use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Session\Session; +use Joomla\Component\MokoSuiteCross\Administrator\Helper\AnalyticsHelper; class AnalyticsController extends BaseController { - public function display($cachable = false, $urlparams = []): static + /** + * Return heatmap grid data as JSON. + * + * Query params: service_type (string), days (int, default 90) + */ + public function heatmap(): void { - return parent::display($cachable, $urlparams); + if (!Session::checkToken('get')) { + echo json_encode(['success' => false, 'error' => 'Invalid token']); + $this->app->close(); + + return; + } + + $user = $this->app->getIdentity(); + + if (!$user->authorise('core.manage', 'com_mokosuitecross')) { + echo json_encode(['success' => false, 'error' => 'Permission denied']); + $this->app->close(); + + return; + } + + $serviceType = $this->input->getCmd('service_type', ''); + $days = $this->input->getInt('days', 90); + + $grid = AnalyticsHelper::getHeatmapData($serviceType, $days); + $bestTimes = AnalyticsHelper::getBestTimes($serviceType, 3); + + $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); + echo json_encode([ + 'success' => true, + 'grid' => $grid, + 'best_times' => $bestTimes, + ]); + $this->app->close(); + } + + /** + * Return the top posting times as JSON. + * + * Query params: service_type (string), limit (int, default 5) + */ + public function besttimes(): void + { + if (!Session::checkToken('get')) { + echo json_encode(['success' => false, 'error' => 'Invalid token']); + $this->app->close(); + + return; + } + + $user = $this->app->getIdentity(); + + if (!$user->authorise('core.manage', 'com_mokosuitecross')) { + echo json_encode(['success' => false, 'error' => 'Permission denied']); + $this->app->close(); + + return; + } + + $serviceType = $this->input->getCmd('service_type', ''); + $limit = $this->input->getInt('limit', 5); + + $bestTimes = AnalyticsHelper::getBestTimes($serviceType, $limit); + $serviceBreakdown = AnalyticsHelper::getServiceBreakdown(); + + $this->app->setHeader('Content-Type', 'application/json; charset=utf-8'); + echo json_encode([ + 'success' => true, + 'best_times' => $bestTimes, + 'service_breakdown' => $serviceBreakdown, + ]); + $this->app->close(); } } diff --git a/source/packages/com_mokosuitecross/src/Helper/AnalyticsHelper.php b/source/packages/com_mokosuitecross/src/Helper/AnalyticsHelper.php index a2b324ff..47e917a9 100644 --- a/source/packages/com_mokosuitecross/src/Helper/AnalyticsHelper.php +++ b/source/packages/com_mokosuitecross/src/Helper/AnalyticsHelper.php @@ -17,144 +17,236 @@ use Joomla\CMS\Factory; class AnalyticsHelper { - private static array $dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + /** + * Record or update engagement metrics for a post. + * + * @param int $postId The post ID + * @param int $serviceId The service ID + * @param string $serviceType The service type (e.g. twitter, facebook) + * @param array $metrics Engagement metrics: impressions, engagements, clicks, shares, posted_at + * + * @return bool True on success + */ + public static function recordEngagement(int $postId, int $serviceId, string $serviceType, array $metrics): bool + { + $db = Factory::getDbo(); - public static function getPostingHeatmap(string $serviceType = '', int $days = 90): array + $postedAt = $metrics['posted_at'] ?? null; + + if ($postedAt) { + $timestamp = strtotime($postedAt); + $dayOfWeek = (int) date('w', $timestamp); + $hourOfDay = (int) date('G', $timestamp); + } else { + $dayOfWeek = 0; + $hourOfDay = 0; + } + + $impressions = (int) ($metrics['impressions'] ?? 0); + $engagements = (int) ($metrics['engagements'] ?? 0); + $clicks = (int) ($metrics['clicks'] ?? 0); + $shares = (int) ($metrics['shares'] ?? 0); + + $engagementRate = $impressions > 0 + ? round(($engagements / $impressions) * 100, 2) + : 0.00; + + // Check if a row already exists for this post + $query = $db->getQuery(true) + ->select($db->quoteName('id')) + ->from($db->quoteName('#__mokosuitecross_analytics')) + ->where($db->quoteName('post_id') . ' = ' . $postId) + ->where($db->quoteName('service_id') . ' = ' . $serviceId); + $db->setQuery($query); + $existingId = $db->loadResult(); + + if ($existingId) { + $query = $db->getQuery(true) + ->update($db->quoteName('#__mokosuitecross_analytics')) + ->set($db->quoteName('impressions') . ' = ' . $impressions) + ->set($db->quoteName('engagements') . ' = ' . $engagements) + ->set($db->quoteName('clicks') . ' = ' . $clicks) + ->set($db->quoteName('shares') . ' = ' . $shares) + ->set($db->quoteName('engagement_rate') . ' = ' . $engagementRate) + ->where($db->quoteName('id') . ' = ' . (int) $existingId); + $db->setQuery($query); + $db->execute(); + + return true; + } + + $record = (object) [ + 'post_id' => $postId, + 'service_id' => $serviceId, + 'service_type' => $serviceType, + 'posted_at' => $postedAt, + 'day_of_week' => $dayOfWeek, + 'hour_of_day' => $hourOfDay, + 'impressions' => $impressions, + 'engagements' => $engagements, + 'clicks' => $clicks, + 'shares' => $shares, + 'engagement_rate' => $engagementRate, + 'created' => Factory::getDate()->toSql(), + ]; + + $db->insertObject('#__mokosuitecross_analytics', $record); + + return true; + } + + /** + * Get heatmap data as a 7x24 grid of average engagement rates. + * + * @param string $serviceType Optional service type filter + * @param int $days Number of days to look back (0 = all time) + * + * @return array 7x24 grid: [ day_of_week => [ hour_of_day => avg_engagement_rate ] ] + */ + public static function getHeatmapData(string $serviceType = '', int $days = 90): array { $db = Factory::getDbo(); $query = $db->getQuery(true) - ->select('DAYOFWEEK(' . $db->quoteName('p.posted_at') . ') - 1 AS dow') - ->select('HOUR(' . $db->quoteName('p.posted_at') . ') AS hr') - ->select('COUNT(*) AS cnt') - ->from($db->quoteName('#__mokosuitecross_posts', 'p')) - ->where($db->quoteName('p.status') . ' = ' . $db->quote('posted')) - ->where($db->quoteName('p.posted_at') . ' IS NOT NULL'); - - if ($days > 0) { - $since = Factory::getDate('now - ' . $days . ' days')->toSql(); - $query->where($db->quoteName('p.posted_at') . ' >= ' . $db->quote($since)); - } + ->select([ + $db->quoteName('day_of_week'), + $db->quoteName('hour_of_day'), + 'AVG(' . $db->quoteName('engagement_rate') . ') AS avg_rate', + 'COUNT(*) AS post_count', + ]) + ->from($db->quoteName('#__mokosuitecross_analytics')) + ->group($db->quoteName('day_of_week')) + ->group($db->quoteName('hour_of_day')) + ->order($db->quoteName('day_of_week') . ' ASC') + ->order($db->quoteName('hour_of_day') . ' ASC'); if ($serviceType !== '') { - $query->join('INNER', $db->quoteName('#__mokosuitecross_services', 's') . ' ON ' . $db->quoteName('s.id') . ' = ' . $db->quoteName('p.service_id')) - ->where($db->quoteName('s.service_type') . ' = ' . $db->quote($serviceType)); + $query->where($db->quoteName('service_type') . ' = ' . $db->quote($serviceType)); } - $query->group('dow, hr') - ->order('dow ASC, hr ASC'); + if ($days > 0) { + $cutoff = Factory::getDate('-' . $days . ' days')->toSql(); + $query->where($db->quoteName('posted_at') . ' >= ' . $db->quote($cutoff)); + } $db->setQuery($query); $rows = $db->loadObjectList(); + // Build 7x24 grid initialised to zero $grid = []; for ($d = 0; $d < 7; $d++) { - $grid[$d] = array_fill(0, 24, 0); + for ($h = 0; $h < 24; $h++) { + $grid[$d][$h] = ['avg_rate' => 0.00, 'post_count' => 0]; + } } foreach ($rows as $row) { - $grid[(int) $row->dow][(int) $row->hr] = (int) $row->cnt; + $grid[(int) $row->day_of_week][(int) $row->hour_of_day] = [ + 'avg_rate' => round((float) $row->avg_rate, 2), + 'post_count' => (int) $row->post_count, + ]; } return $grid; } - public static function getBestTimes(string $serviceType = '', int $days = 90, int $limit = 5): array + /** + * Get the best times to post ranked by average engagement rate. + * + * @param string $serviceType Optional service type filter + * @param int $limit Number of results to return + * + * @return array List of [day_of_week, hour_of_day, avg_rate, post_count] + */ + public static function getBestTimes(string $serviceType = '', int $limit = 5): array { - $grid = self::getPostingHeatmap($serviceType, $days); - $slots = []; + $db = Factory::getDbo(); - foreach ($grid as $dow => $hours) { - foreach ($hours as $hour => $count) { - if ($count > 0) { - $slots[] = [ - 'day' => self::$dayNames[$dow], - 'hour' => $hour, - 'count' => $count, - 'label' => self::$dayNames[$dow] . ' ' . self::formatHour($hour), - ]; - } - } + $query = $db->getQuery(true) + ->select([ + $db->quoteName('day_of_week'), + $db->quoteName('hour_of_day'), + 'AVG(' . $db->quoteName('engagement_rate') . ') AS avg_rate', + 'COUNT(*) AS post_count', + ]) + ->from($db->quoteName('#__mokosuitecross_analytics')) + ->group($db->quoteName('day_of_week')) + ->group($db->quoteName('hour_of_day')) + ->having('COUNT(*) >= 1') + ->order('avg_rate DESC'); + + if ($serviceType !== '') { + $query->where($db->quoteName('service_type') . ' = ' . $db->quote($serviceType)); } - usort($slots, static fn($a, $b) => $b['count'] <=> $a['count']); + $db->setQuery($query, 0, $limit); + $rows = $db->loadAssocList(); - return \array_slice($slots, 0, $limit); + $dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + + $results = []; + + foreach ($rows as $row) { + $hour = (int) $row['hour_of_day']; + $ampm = $hour < 12 ? 'AM' : 'PM'; + $hour12 = $hour % 12 ?: 12; + + $results[] = [ + 'day_of_week' => (int) $row['day_of_week'], + 'day_name' => $dayNames[(int) $row['day_of_week']], + 'hour_of_day' => $hour, + 'hour_label' => $hour12 . ':00 ' . $ampm, + 'avg_rate' => round((float) $row['avg_rate'], 2), + 'post_count' => (int) $row['post_count'], + ]; + } + + return $results; } + /** + * Get engagement stats grouped by service type. + * + * @param int $days Number of days to look back (0 = all time) + * + * @return array List of [service_type, total_posts, avg_engagement_rate, total_impressions, total_engagements] + */ public static function getServiceBreakdown(int $days = 30): array { $db = Factory::getDbo(); $query = $db->getQuery(true) - ->select($db->quoteName('s.service_type')) - ->select($db->quoteName('s.title', 'service_title')) - ->select('COUNT(*) AS total') - ->select('SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('posted') . ' THEN 1 ELSE 0 END) AS success') - ->select('SUM(CASE WHEN ' . $db->quoteName('p.status') . ' = ' . $db->quote('failed') . ' THEN 1 ELSE 0 END) AS failed') - ->from($db->quoteName('#__mokosuitecross_posts', 'p')) - ->join('INNER', $db->quoteName('#__mokosuitecross_services', 's') . ' ON ' . $db->quoteName('s.id') . ' = ' . $db->quoteName('p.service_id')); + ->select([ + $db->quoteName('service_type'), + 'COUNT(*) AS total_posts', + 'AVG(' . $db->quoteName('engagement_rate') . ') AS avg_engagement_rate', + 'SUM(' . $db->quoteName('impressions') . ') AS total_impressions', + 'SUM(' . $db->quoteName('engagements') . ') AS total_engagements', + 'SUM(' . $db->quoteName('clicks') . ') AS total_clicks', + 'SUM(' . $db->quoteName('shares') . ') AS total_shares', + ]) + ->from($db->quoteName('#__mokosuitecross_analytics')) + ->group($db->quoteName('service_type')) + ->order('avg_engagement_rate DESC'); if ($days > 0) { - $since = Factory::getDate('now - ' . $days . ' days')->toSql(); - $query->where($db->quoteName('p.created') . ' >= ' . $db->quote($since)); + $cutoff = Factory::getDate('-' . $days . ' days')->toSql(); + $query->where($db->quoteName('posted_at') . ' >= ' . $db->quote($cutoff)); } - $query->group($db->quoteName(['s.service_type', 's.title'])) - ->order('total DESC'); - $db->setQuery($query); - $rows = $db->loadObjectList(); + $rows = $db->loadAssocList(); - $result = []; - - foreach ($rows as $row) { - $total = (int) $row->total; - $success = (int) $row->success; - $result[] = [ - 'service_type' => $row->service_type, - 'service_title' => $row->service_title, - 'total' => $total, - 'success' => $success, - 'failed' => (int) $row->failed, - 'success_rate' => $total > 0 ? round(($success / $total) * 100, 1) : 0.0, - 'avg_per_day' => $days > 0 ? round($total / $days, 1) : 0.0, - ]; + foreach ($rows as &$row) { + $row['avg_engagement_rate'] = round((float) $row['avg_engagement_rate'], 2); + $row['total_posts'] = (int) $row['total_posts']; + $row['total_impressions'] = (int) $row['total_impressions']; + $row['total_engagements'] = (int) $row['total_engagements']; + $row['total_clicks'] = (int) $row['total_clicks']; + $row['total_shares'] = (int) $row['total_shares']; } - return $result; + return $rows; } - - public static function getServiceTypes(): array - { - $db = Factory::getDbo(); - - $query = $db->getQuery(true) - ->select('DISTINCT ' . $db->quoteName('service_type')) - ->from($db->quoteName('#__mokosuitecross_services')) - ->where($db->quoteName('published') . ' = 1') - ->order($db->quoteName('service_type') . ' ASC'); - - $db->setQuery($query); - - return $db->loadColumn() ?: []; - } - - private static function formatHour(int $hour): string - { - if ($hour === 0) { - return '12:00 AM'; - } - - if ($hour < 12) { - return $hour . ':00 AM'; - } - - if ($hour === 12) { - return '12:00 PM'; - } - - return ($hour - 12) . ':00 PM'; - } -} +} \ No newline at end of file diff --git a/source/packages/com_mokosuitecross/tmpl/dashboard/default.php b/source/packages/com_mokosuitecross/tmpl/dashboard/default.php index 4ac74e7c..42eb5ff5 100644 --- a/source/packages/com_mokosuitecross/tmpl/dashboard/default.php +++ b/source/packages/com_mokosuitecross/tmpl/dashboard/default.php @@ -220,6 +220,175 @@ $queueProcessing = $componentParams->get('queue_processing', 'scheduler'); + +
+
+
+ +
+
+
+

+ + + +
+
+
+
-- 2.52.0 From 9da83f1d40b897d6ec45520fe27efcebe45bd03f Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Mon, 29 Jun 2026 11:37:09 +0000 Subject: [PATCH 06/10] chore(version): pre-release bump to 01.12.04-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.12.04.sql | 1 + source/packages/plg_content_mokosuitecross/mokosuitecross.xml | 2 +- source/packages/plg_mokosuitecross_activitypub/activitypub.xml | 2 +- source/packages/plg_mokosuitecross_blogger/blogger.xml | 2 +- source/packages/plg_mokosuitecross_bluesky/bluesky.xml | 2 +- source/packages/plg_mokosuitecross_brevo/brevo.xml | 2 +- .../plg_mokosuitecross_constantcontact/constantcontact.xml | 2 +- source/packages/plg_mokosuitecross_convertkit/convertkit.xml | 2 +- source/packages/plg_mokosuitecross_devto/devto.xml | 2 +- source/packages/plg_mokosuitecross_discord/discord.xml | 2 +- source/packages/plg_mokosuitecross_facebook/facebook.xml | 2 +- source/packages/plg_mokosuitecross_ghost/ghost.xml | 2 +- .../plg_mokosuitecross_googlebusiness/googlebusiness.xml | 2 +- source/packages/plg_mokosuitecross_googlechat/googlechat.xml | 2 +- source/packages/plg_mokosuitecross_hashnode/hashnode.xml | 2 +- source/packages/plg_mokosuitecross_instagram/instagram.xml | 2 +- source/packages/plg_mokosuitecross_linkedin/linkedin.xml | 2 +- source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml | 2 +- source/packages/plg_mokosuitecross_mastodon/mastodon.xml | 2 +- source/packages/plg_mokosuitecross_matrix/matrix.xml | 2 +- source/packages/plg_mokosuitecross_medium/medium.xml | 2 +- .../plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml | 2 +- .../plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml | 2 +- source/packages/plg_mokosuitecross_nostr/nostr.xml | 2 +- source/packages/plg_mokosuitecross_ntfy/ntfy.xml | 2 +- source/packages/plg_mokosuitecross_pinterest/pinterest.xml | 2 +- source/packages/plg_mokosuitecross_reddit/reddit.xml | 2 +- source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml | 2 +- source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml | 2 +- source/packages/plg_mokosuitecross_slack/slack.xml | 2 +- source/packages/plg_mokosuitecross_teams/teams.xml | 2 +- source/packages/plg_mokosuitecross_telegram/telegram.xml | 2 +- source/packages/plg_mokosuitecross_threads/threads.xml | 2 +- source/packages/plg_mokosuitecross_tiktok/tiktok.xml | 2 +- source/packages/plg_mokosuitecross_tumblr/tumblr.xml | 2 +- source/packages/plg_mokosuitecross_twitter/twitter.xml | 2 +- source/packages/plg_mokosuitecross_webhook/webhook.xml | 2 +- source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml | 2 +- source/packages/plg_mokosuitecross_wordpress/wordpress.xml | 2 +- source/packages/plg_mokosuitecross_youtube/youtube.xml | 2 +- source/packages/plg_system_mokosuitecross/mokosuitecross.xml | 2 +- .../plg_system_mokosuitecross_events/mokosuitecross_events.xml | 2 +- .../mokosuitecross_gallery.xml | 2 +- source/packages/plg_task_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/plg_webservices_mokosuitecross/mokosuitecross.xml | 2 +- source/pkg_mokosuitecross.xml | 2 +- 52 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.12.04.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index bfa4245b..23df8793 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.12.03 +# VERSION: 01.12.04 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index d3eba2a6..90222f8e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.12.03 + VERSION: 01.12.04 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index c7a62529..a5044372 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.12.03 + VERSION: 01.12.04 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index ba45acdb..b556716d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteCross - + Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 6. diff --git a/SECURITY.md b/SECURITY.md index b1fd805d..d09cf8f7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.12.03 +VERSION: 01.12.04 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index f2008a92..09188693 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.04.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.04.sql new file mode 100644 index 00000000..fcf5592a --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.04.sql @@ -0,0 +1 @@ +/* 01.12.04 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index 22526fdb..7036e6bb 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml index 3ebb454a..70434a79 100644 --- a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml +++ b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ActivityPub (Fediverse) - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_blogger/blogger.xml b/source/packages/plg_mokosuitecross_blogger/blogger.xml index d426099b..5a6e0679 100644 --- a/source/packages/plg_mokosuitecross_blogger/blogger.xml +++ b/source/packages/plg_mokosuitecross_blogger/blogger.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Blogger - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml index 1b2edf5b..06a12435 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_brevo/brevo.xml b/source/packages/plg_mokosuitecross_brevo/brevo.xml index d65a15ed..768c907a 100644 --- a/source/packages/plg_mokosuitecross_brevo/brevo.xml +++ b/source/packages/plg_mokosuitecross_brevo/brevo.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Brevo (Sendinblue) - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml index c821b2e9..97e5a386 100644 --- a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml +++ b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Constant Contact - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml index 30f241e7..d2b28413 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_devto/devto.xml b/source/packages/plg_mokosuitecross_devto/devto.xml index 746f879d..9cb6792a 100644 --- a/source/packages/plg_mokosuitecross_devto/devto.xml +++ b/source/packages/plg_mokosuitecross_devto/devto.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Dev.to - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_discord/discord.xml b/source/packages/plg_mokosuitecross_discord/discord.xml index 7572d0f6..94cbf1eb 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_facebook/facebook.xml b/source/packages/plg_mokosuitecross_facebook/facebook.xml index 4b171622..2cda66e8 100644 --- a/source/packages/plg_mokosuitecross_facebook/facebook.xml +++ b/source/packages/plg_mokosuitecross_facebook/facebook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Facebook / Meta - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ghost/ghost.xml b/source/packages/plg_mokosuitecross_ghost/ghost.xml index be3ed7d3..e5fd20a2 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml index 07c2aa50..c1ab9937 100644 --- a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml +++ b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Business Profile - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml index 2b32c4d8..5dd91d25 100644 --- a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml +++ b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Chat - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml index 585cffdb..e8cda0e5 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_instagram/instagram.xml b/source/packages/plg_mokosuitecross_instagram/instagram.xml index 66559d0f..aebaa878 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.12.03 + 01.12.04 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml index dea1557c..fad5b33b 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml index 15b29c12..a3388cf7 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml index 874d5c28..a46ae5bf 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_matrix/matrix.xml b/source/packages/plg_mokosuitecross_matrix/matrix.xml index e073c885..8ba76c9f 100644 --- a/source/packages/plg_mokosuitecross_matrix/matrix.xml +++ b/source/packages/plg_mokosuitecross_matrix/matrix.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Matrix / Element - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_medium/medium.xml b/source/packages/plg_mokosuitecross_medium/medium.xml index 98e38ae6..556f74ab 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml index 87e8269d..e31999cb 100644 --- a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml +++ b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteCalendar Events - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml index 291a05b3..63fd0f93 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_nostr/nostr.xml b/source/packages/plg_mokosuitecross_nostr/nostr.xml index b90fcd03..3779a267 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml index e95d2ef9..a933dd95 100644 --- a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml +++ b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ntfy Push Notifications - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml index 8dd7b416..38173596 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_reddit/reddit.xml b/source/packages/plg_mokosuitecross_reddit/reddit.xml index 0b27eea6..b647c0bd 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml index 045db4c1..ed2b01c4 100644 --- a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml +++ b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml @@ -1,7 +1,7 @@ MokoSuiteCross - RSS Feed - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml index 50b7f139..2dc1e1c5 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_slack/slack.xml b/source/packages/plg_mokosuitecross_slack/slack.xml index 42989440..f800efac 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_teams/teams.xml b/source/packages/plg_mokosuitecross_teams/teams.xml index f7fc311e..5e0f602d 100644 --- a/source/packages/plg_mokosuitecross_teams/teams.xml +++ b/source/packages/plg_mokosuitecross_teams/teams.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Microsoft Teams - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_telegram/telegram.xml b/source/packages/plg_mokosuitecross_telegram/telegram.xml index c3ab5dae..5abed5ee 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_threads/threads.xml b/source/packages/plg_mokosuitecross_threads/threads.xml index 2066db29..9267679e 100644 --- a/source/packages/plg_mokosuitecross_threads/threads.xml +++ b/source/packages/plg_mokosuitecross_threads/threads.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Threads (Meta) - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml index 09c0ab24..2bd9634a 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml index 51c99aa0..8a791da9 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_twitter/twitter.xml b/source/packages/plg_mokosuitecross_twitter/twitter.xml index 57a16719..6e6f1e8f 100644 --- a/source/packages/plg_mokosuitecross_twitter/twitter.xml +++ b/source/packages/plg_mokosuitecross_twitter/twitter.xml @@ -1,7 +1,7 @@ MokoSuiteCross - X / Twitter - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_webhook/webhook.xml b/source/packages/plg_mokosuitecross_webhook/webhook.xml index 0b17ec7f..ffca9dbc 100644 --- a/source/packages/plg_mokosuitecross_webhook/webhook.xml +++ b/source/packages/plg_mokosuitecross_webhook/webhook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Generic Webhook - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml index c2ecb4a0..58f5139b 100644 --- a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml +++ b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WhatsApp Business - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml index 83bb0143..e8b09da6 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_youtube/youtube.xml b/source/packages/plg_mokosuitecross_youtube/youtube.xml index 8cbf526b..f38c3a63 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.12.03 + 01.12.04 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml index 08956cd5..4998e370 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml index 2ec4e5f2..bdcd619f 100644 --- a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml +++ b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Events - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml index 16816fbd..b77fe592 100644 --- a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml +++ b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Gallery - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml index 2488ad13..a9eb109b 100644 --- a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Task - MokoSuiteCross Queue Processor - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml index c3f3a7c9..7031fbb1 100644 --- a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Web Services - MokoSuiteCross - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index 80e2cfae..dade274e 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.12.03 + 01.12.04 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 0d49195f5287b65aabddaeaa6be2bfdaf060edea Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sun, 28 Jun 2026 11:51:05 -0500 Subject: [PATCH 07/10] feat: add visual post calendar with drag-drop rescheduling (#160) Authored-by: Moko Consulting --- .../language/en-GB/com_mokosuitecross.ini | 67 +---- .../src/Controller/CalendarController.php | 248 +++++++++++++++++- .../src/Helper/MokoSuiteCrossHelper.php | 1 + .../src/View/Calendar/HtmlView.php | 42 ++- .../tmpl/calendar/default.php | 240 +++++++++-------- .../tmpl/dashboard/default.php | 173 +----------- 6 files changed, 416 insertions(+), 355 deletions(-) diff --git a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini index eb318bd0..45830408 100644 --- a/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini +++ b/source/packages/com_mokosuitecross/language/en-GB/com_mokosuitecross.ini @@ -570,65 +570,22 @@ COM_MOKOSUITECROSS_AI_GENERATE_DESC="Generate platform-optimized captions from t COM_MOKOSUITECROSS_AI_GENERATING="Generating captions..." COM_MOKOSUITECROSS_AI_GENERATED="AI captions generated successfully." COM_MOKOSUITECROSS_AI_ERROR="AI generation failed: %s" +COM_MOKOSUITECROSS_AI_NOT_CONFIGURED="AI is not configured. Go to Options to set up a provider and API key." -; Social Image Generator -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE="Social Image Generator" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED="Enable Social Images" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_ENABLED_DESC="Generate branded OG images with article title overlay for social sharing." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_BG_COLOR="Background Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_BG_COLOR_DESC="Hex color for the image background (e.g. #1a1a2e)." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR="Text Color" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_TEXT_COLOR_DESC="Hex color for the title text overlay." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_FONT_SIZE="Font Size" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_FONT_SIZE_DESC="Font size in pixels for the title text (24-96)." -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_SHOW_SITE_NAME="Show Site Name" -COM_MOKOSUITECROSS_CONFIG_SOCIAL_IMAGE_SHOW_SITE_NAME_DESC="Display the site name in the bottom-right corner of generated images." -COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATE="Generate Social Image" -COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATING="Generating image..." -COM_MOKOSUITECROSS_SOCIAL_IMAGE_GENERATED="Social image generated." -COM_MOKOSUITECROSS_SOCIAL_IMAGE_ERROR="Image generation failed: %s" -COM_MOKOSUITECROSS_SOCIAL_IMAGE_NOT_CONFIGURED="Social image generator is not enabled. Go to Options to enable it." - -; Analytics -COM_MOKOSUITECROSS_SUBMENU_ANALYTICS="Analytics" -COM_MOKOSUITECROSS_ANALYTICS_PERIOD="Time Period" -COM_MOKOSUITECROSS_ANALYTICS_SERVICE_FILTER="Service" -COM_MOKOSUITECROSS_ANALYTICS_ALL_SERVICES="All Services" -COM_MOKOSUITECROSS_ANALYTICS_BEST_TIMES="Best Times to Post" -COM_MOKOSUITECROSS_ANALYTICS_HEATMAP="Engagement Heatmap" -COM_MOKOSUITECROSS_ANALYTICS_HOURLY="Hourly Distribution" -COM_MOKOSUITECROSS_ANALYTICS_DAILY="Day of Week Distribution" -COM_MOKOSUITECROSS_ANALYTICS_NO_DATA="Not enough posting data to generate recommendations. Post at least 3 times per time slot over the selected period." -COM_MOKOSUITECROSS_ANALYTICS_POSTS_SUCCESS="%d of %d successful" -COM_MOKOSUITECROSS_ANALYTICS_DAY_SUN="Sun" -COM_MOKOSUITECROSS_ANALYTICS_DAY_MON="Mon" -COM_MOKOSUITECROSS_ANALYTICS_DAY_TUE="Tue" -COM_MOKOSUITECROSS_ANALYTICS_DAY_WED="Wed" -COM_MOKOSUITECROSS_ANALYTICS_DAY_THU="Thu" -COM_MOKOSUITECROSS_ANALYTICS_DAY_FRI="Fri" -COM_MOKOSUITECROSS_ANALYTICS_DAY_SAT="Sat" -COM_MOKOSUITECROSS_ANALYTICS_LEGEND_HIGH="High success rate" -COM_MOKOSUITECROSS_ANALYTICS_LEGEND_MEDIUM="Medium success rate" -COM_MOKOSUITECROSS_ANALYTICS_LEGEND_LOW="Low success rate" -COM_MOKOSUITECROSS_ANALYTICS_LEGEND_NONE="No data" -COM_MOKOSUITECROSS_PERIOD_180_DAYS="Last 180 days" -COM_MOKOSUITECROSS_PERIOD_365_DAYS="Last 365 days" - - -; Analytics -COM_MOKOSUITECROSS_ANALYTICS="Analytics" -COM_MOKOSUITECROSS_ANALYTICS_BEST_TIMES="Best Times to Post" -COM_MOKOSUITECROSS_ANALYTICS_HEATMAP="Engagement Heatmap" -COM_MOKOSUITECROSS_ANALYTICS_NO_DATA="Not enough data yet. Analytics will appear after posts collect engagement metrics." -COM_MOKOSUITECROSS_ANALYTICS_ENGAGEMENT_RATE="Engagement Rate" -COM_MOKOSUITECROSS_ANALYTICS_ALL_PLATFORMS="All Platforms" ; Category Rules COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES="Category Rules" COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES_NOTE="Category Routing" COM_MOKOSUITECROSS_CONFIG_CATEGORY_RULES_NOTE_DESC="Category routing rules let you map Joomla categories to specific cross-post services. When rules exist for a category, only those services receive posts. When no rules exist, all services are used (default behaviour). Rules are managed in the database table #__mokosuitecross_category_rules. A full admin UI will be added in a future release." -; Calendar View -COM_MOKOSUITECROSS_CALENDAR_PREV_MONTH="Previous" -COM_MOKOSUITECROSS_CALENDAR_NEXT_MONTH="Next" +; Post Calendar +COM_MOKOSUITECROSS_CALENDAR="Post Calendar" +COM_MOKOSUITECROSS_CALENDAR_DESC="Visual calendar of scheduled and posted content" +COM_MOKOSUITECROSS_SUBMENU_CALENDAR="Calendar" COM_MOKOSUITECROSS_CALENDAR_TODAY="Today" -COM_MOKOSUITECROSS_SUBMENU_CALENDAR="Post Calendar" +COM_MOKOSUITECROSS_CALENDAR_MONTH="Month" +COM_MOKOSUITECROSS_CALENDAR_WEEK="Week" +COM_MOKOSUITECROSS_CALENDAR_LIST="List" +COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_SUCCESS="Post rescheduled successfully" +COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR="Failed to reschedule post" +COM_MOKOSUITECROSS_CALENDAR_CANNOT_RESCHEDULE="Only scheduled or queued posts can be rescheduled" +COM_MOKOSUITECROSS_CALENDAR_LOAD_ERROR="Failed to load calendar events" diff --git a/source/packages/com_mokosuitecross/src/Controller/CalendarController.php b/source/packages/com_mokosuitecross/src/Controller/CalendarController.php index e36b117e..3e88763e 100644 --- a/source/packages/com_mokosuitecross/src/Controller/CalendarController.php +++ b/source/packages/com_mokosuitecross/src/Controller/CalendarController.php @@ -13,12 +13,256 @@ namespace Joomla\Component\MokoSuiteCross\Administrator\Controller; defined('_JEXEC') or die; +use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Session\Session; +use Joomla\Component\MokoSuiteCross\Administrator\Table\PostTable; +/** + * Calendar controller -- provides AJAX endpoints for the visual post calendar. + * + * Endpoints: + * task=calendar.events -- GET JSON feed for FullCalendar (filtered by start/end) + * task=calendar.reschedule -- POST reschedule a post to a new date/time + */ class CalendarController extends BaseController { - public function display($cachable = false, $urlparams = []): static + /** + * Return posts as FullCalendar-compatible JSON events. + * + * Query params: start, end (ISO 8601 date range from FullCalendar). + * + * @return void + */ + public function events(): void { - return parent::display($cachable, $urlparams); + $app = $this->app; + $db = Factory::getDbo(); + + // ACL check + if (!$app->getIdentity()->authorise('core.manage', 'com_mokosuitecross')) { + $this->sendJsonResponse(['error' => 'Forbidden'], 403); + + return; + } + + // FullCalendar sends start/end as ISO date strings + $start = $this->input->getString('start', ''); + $end = $this->input->getString('end', ''); + + $query = $db->getQuery(true) + ->select([ + 'p.' . $db->quoteName('id'), + 'p.' . $db->quoteName('article_id'), + 'p.' . $db->quoteName('service_id'), + 'p.' . $db->quoteName('status'), + 'p.' . $db->quoteName('scheduled_at'), + 'p.' . $db->quoteName('posted_at'), + 'p.' . $db->quoteName('created'), + 'p.' . $db->quoteName('message'), + 'a.' . $db->quoteName('title', 'article_title'), + 's.' . $db->quoteName('title', 'service_title'), + 's.' . $db->quoteName('service_type'), + ]) + ->from($db->quoteName('#__mokosuitecross_posts', 'p')) + ->leftJoin( + $db->quoteName('#__content', 'a') + . ' ON ' . $db->quoteName('a.id') . ' = ' . $db->quoteName('p.article_id') + ) + ->leftJoin( + $db->quoteName('#__mokosuitecross_services', 's') + . ' ON ' . $db->quoteName('s.id') . ' = ' . $db->quoteName('p.service_id') + ) + ->order($db->quoteName('p.created') . ' DESC'); + + // Filter by date range when provided + if ($start !== '') { + $dateExpr = 'COALESCE(p.scheduled_at, p.posted_at, p.created)'; + $query->where($dateExpr . ' >= ' . $db->quote($start)); + } + + if ($end !== '') { + $dateExpr = 'COALESCE(p.scheduled_at, p.posted_at, p.created)'; + $query->where($dateExpr . ' <= ' . $db->quote($end)); + } + + $db->setQuery($query); + $rows = $db->loadObjectList() ?: []; + + // Map status to colour + $statusColors = [ + 'posted' => '#28a745', + 'scheduled' => '#007bff', + 'queued' => '#ffc107', + 'failed' => '#dc3545', + 'posting' => '#17a2b8', + ]; + + $events = []; + + foreach ($rows as $row) { + // Pick the best date for the calendar event + $eventDate = $row->scheduled_at ?: ($row->posted_at ?: $row->created); + + // Skip rows with no usable date + if (empty($eventDate) || $eventDate === '0000-00-00 00:00:00') { + continue; + } + + $title = ($row->article_title ?: 'Post #' . $row->id); + + if ($row->service_title) { + $title .= ' - ' . $row->service_title; + } + + $events[] = [ + 'id' => (int) $row->id, + 'title' => $title, + 'start' => $eventDate, + 'color' => $statusColors[$row->status] ?? '#6c757d', + 'url' => 'index.php?option=com_mokosuitecross&task=post.edit&id=' . (int) $row->id, + 'extendedProps' => [ + 'status' => $row->status, + 'service_type' => $row->service_type ?? '', + 'article_id' => (int) $row->article_id, + 'service_id' => (int) $row->service_id, + 'message' => mb_substr($row->message ?? '', 0, 200), + ], + ]; + } + + $this->sendJsonResponse($events, 200); + } + + /** + * Reschedule a post to a new date/time via drag-drop. + * + * POST params: post_id (int), new_date (ISO 8601 datetime string). + * + * @return void + */ + public function reschedule(): void + { + $app = $this->app; + + // CSRF check + if (!Session::checkToken('post')) { + $this->sendJsonResponse(['error' => Text::_('JINVALID_TOKEN')], 403); + + return; + } + + // ACL check + if (!$app->getIdentity()->authorise('core.edit', 'com_mokosuitecross')) { + $this->sendJsonResponse(['error' => 'Forbidden'], 403); + + return; + } + + $postId = $this->input->getInt('post_id', 0); + $newDate = $this->input->getString('new_date', ''); + + if ($postId < 1 || $newDate === '') { + $this->sendJsonResponse( + ['error' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR')], + 400 + ); + + return; + } + + // Validate the date format + try { + $dateObj = Factory::getDate($newDate); + } catch (\Exception $e) { + $this->sendJsonResponse( + ['error' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR')], + 400 + ); + + return; + } + + // Load the post using Table bind/check/store pattern + $db = Factory::getDbo(); + $table = new PostTable($db); + + if (!$table->load($postId)) { + $this->sendJsonResponse( + ['error' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR')], + 404 + ); + + return; + } + + // Only allow rescheduling of scheduled or queued posts + $allowedStatuses = ['scheduled', 'queued']; + + if (!in_array($table->status, $allowedStatuses, true)) { + $this->sendJsonResponse( + ['error' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR')], + 400 + ); + + return; + } + + // Update the post + $data = [ + 'scheduled_at' => $dateObj->toSql(), + 'status' => 'scheduled', + 'modified' => Factory::getDate()->toSql(), + ]; + + if (!$table->bind($data) || !$table->check() || !$table->store()) { + $this->sendJsonResponse( + ['error' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_ERROR')], + 500 + ); + + return; + } + + // Log the reschedule + $log = (object) [ + 'post_id' => $postId, + 'service_id' => (int) $table->service_id, + 'level' => 'info', + 'message' => sprintf('Post rescheduled to %s via calendar drag-drop', $dateObj->toSql()), + 'context' => '{}', + 'created' => Factory::getDate()->toSql(), + ]; + + $db->insertObject('#__mokosuitecross_logs', $log); + + $this->sendJsonResponse( + [ + 'success' => true, + 'message' => Text::_('COM_MOKOSUITECROSS_CALENDAR_RESCHEDULE_SUCCESS'), + ], + 200 + ); + } + + /** + * Send a JSON response and close the application. + * + * @param array $data Response data + * @param int $httpCode HTTP status code + * + * @return void + */ + private function sendJsonResponse(array $data, int $httpCode): void + { + $app = $this->app; + + $app->setHeader('Content-Type', 'application/json; charset=utf-8'); + $app->setHeader('Status', (string) $httpCode); + + echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + + $app->close(); } } diff --git a/source/packages/com_mokosuitecross/src/Helper/MokoSuiteCrossHelper.php b/source/packages/com_mokosuitecross/src/Helper/MokoSuiteCrossHelper.php index 84b66d4d..e9bce359 100644 --- a/source/packages/com_mokosuitecross/src/Helper/MokoSuiteCrossHelper.php +++ b/source/packages/com_mokosuitecross/src/Helper/MokoSuiteCrossHelper.php @@ -40,6 +40,7 @@ class MokoSuiteCrossHelper 'posts' => 'COM_MOKOSUITECROSS_SUBMENU_POSTS', 'services' => 'COM_MOKOSUITECROSS_SUBMENU_SERVICES', 'templates' => 'COM_MOKOSUITECROSS_SUBMENU_TEMPLATES', + 'calendar' => 'COM_MOKOSUITECROSS_SUBMENU_CALENDAR', 'logs' => 'COM_MOKOSUITECROSS_SUBMENU_LOGS', 'calendar' => 'COM_MOKOSUITECROSS_SUBMENU_CALENDAR', 'analytics' => 'COM_MOKOSUITECROSS_SUBMENU_ANALYTICS', diff --git a/source/packages/com_mokosuitecross/src/View/Calendar/HtmlView.php b/source/packages/com_mokosuitecross/src/View/Calendar/HtmlView.php index 58706228..caacba03 100644 --- a/source/packages/com_mokosuitecross/src/View/Calendar/HtmlView.php +++ b/source/packages/com_mokosuitecross/src/View/Calendar/HtmlView.php @@ -14,52 +14,48 @@ namespace Joomla\Component\MokoSuiteCross\Administrator\View\Calendar; defined('_JEXEC') or die; use Joomla\CMS\Factory; +use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Router\Route; use Joomla\CMS\Toolbar\ToolbarHelper; use Joomla\Component\MokoSuiteCross\Administrator\Helper\MokoSuiteCrossHelper; class HtmlView extends BaseHtmlView { - public int $year; - public int $month; - public array $events; public $sidebar; + public $ajaxUrl; public function display($tpl = null): void { - $input = Factory::getApplication()->input; + // ACL check + $canDo = MokoSuiteCrossHelper::getActions(); - $this->year = $input->getInt('year', (int) date('Y')); - $this->month = $input->getInt('month', (int) date('n')); - - if ($this->month < 1 || $this->month > 12) { - $this->month = (int) date('n'); + if (!$canDo->get('core.manage')) { + throw new \RuntimeException(Text::_('JERROR_ALERTNOAUTHOR'), 403); } - if ($this->year < 2000 || $this->year > 2100) { - $this->year = (int) date('Y'); - } - - $model = $this->getModel(); - $this->events = $model->getEvents($this->year, $this->month); + // Build AJAX URL for FullCalendar event source + $this->ajaxUrl = Route::_('index.php?option=com_mokosuitecross&task=calendar.events&format=json', false); $this->addToolbar(); MokoSuiteCrossHelper::addSubmenu('calendar'); $this->sidebar = \Joomla\CMS\HTML\Sidebar::render(); + // Set document title + Factory::getApplication()->getDocument()->setTitle( + Text::_('COM_MOKOSUITECROSS_CALENDAR') . ' - ' . Text::_('COM_MOKOSUITECROSS') + ); + parent::display($tpl); } protected function addToolbar(): void { - $canDo = MokoSuiteCrossHelper::getActions(); - - ToolbarHelper::title('MokoSuiteCross -- Post Calendar', 'calendar'); - ToolbarHelper::back('JTOOLBAR_BACK', 'index.php?option=com_mokosuitecross&view=dashboard'); - - if ($canDo->get('core.admin')) { - ToolbarHelper::preferences('com_mokosuitecross'); - } + ToolbarHelper::title( + Text::_('COM_MOKOSUITECROSS') . ' - ' . Text::_('COM_MOKOSUITECROSS_CALENDAR'), + 'calendar' + ); + ToolbarHelper::back('JTOOLBAR_BACK', Route::_('index.php?option=com_mokosuitecross&view=dashboard', false)); } } diff --git a/source/packages/com_mokosuitecross/tmpl/calendar/default.php b/source/packages/com_mokosuitecross/tmpl/calendar/default.php index dc0e2187..240484fb 100644 --- a/source/packages/com_mokosuitecross/tmpl/calendar/default.php +++ b/source/packages/com_mokosuitecross/tmpl/calendar/default.php @@ -12,118 +12,150 @@ defined('_JEXEC') or die; use Joomla\CMS\Language\Text; -use Joomla\CMS\Router\Route; +use Joomla\CMS\Session\Session; /** @var \Joomla\Component\MokoSuiteCross\Administrator\View\Calendar\HtmlView $this */ -$year = $this->year; -$month = $this->month; -$events = $this->events; -$today = date('Y-m-d'); - -$prevMonth = $month - 1; -$prevYear = $year; - -if ($prevMonth < 1) { - $prevMonth = 12; - $prevYear--; -} - -$nextMonth = $month + 1; -$nextYear = $year; - -if ($nextMonth > 12) { - $nextMonth = 1; - $nextYear++; -} - -$monthName = date('F', mktime(0, 0, 0, $month, 1, $year)); -$daysInMonth = (int) date('t', mktime(0, 0, 0, $month, 1, $year)); -$firstWeekday = ((int) date('N', mktime(0, 0, 0, $month, 1, $year))) - 1; - -$statusClass = static function (string $status): string { - return match ($status) { - 'posted' => 'bg-success', - 'failed' => 'bg-danger', - default => 'bg-warning text-dark', - }; -}; +$token = Session::getFormToken(); +$ajaxUrl = $this->ajaxUrl; ?> -
- - - - -

- - - - + + +
+ + + +
-
- - - - - - - - - - - - - - - while ($day <= $daysInMonth) : ?> - - - - + diff --git a/source/packages/com_mokosuitecross/tmpl/dashboard/default.php b/source/packages/com_mokosuitecross/tmpl/dashboard/default.php index 42eb5ff5..b5289951 100644 --- a/source/packages/com_mokosuitecross/tmpl/dashboard/default.php +++ b/source/packages/com_mokosuitecross/tmpl/dashboard/default.php @@ -220,175 +220,6 @@ $queueProcessing = $componentParams->get('queue_processing', 'scheduler'); - -
-
-
- -
-
-
-

- -
 
- - - - - - - - - - - - - - - - - - -
-
- : - - - - - -
-
- -
-
-
-
@@ -451,9 +282,9 @@ $queueProcessing = $componentParams->get('queue_processing', 'scheduler'); class="list-group-item list-group-item-action"> - - +
-- 2.52.0 From 7de7cea10e43e3c0db060a87d1bc36581cefa7d9 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Mon, 29 Jun 2026 11:39:00 +0000 Subject: [PATCH 08/10] chore(version): pre-release bump to 01.12.05-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.12.05.sql | 1 + source/packages/plg_content_mokosuitecross/mokosuitecross.xml | 2 +- source/packages/plg_mokosuitecross_activitypub/activitypub.xml | 2 +- source/packages/plg_mokosuitecross_blogger/blogger.xml | 2 +- source/packages/plg_mokosuitecross_bluesky/bluesky.xml | 2 +- source/packages/plg_mokosuitecross_brevo/brevo.xml | 2 +- .../plg_mokosuitecross_constantcontact/constantcontact.xml | 2 +- source/packages/plg_mokosuitecross_convertkit/convertkit.xml | 2 +- source/packages/plg_mokosuitecross_devto/devto.xml | 2 +- source/packages/plg_mokosuitecross_discord/discord.xml | 2 +- source/packages/plg_mokosuitecross_facebook/facebook.xml | 2 +- source/packages/plg_mokosuitecross_ghost/ghost.xml | 2 +- .../plg_mokosuitecross_googlebusiness/googlebusiness.xml | 2 +- source/packages/plg_mokosuitecross_googlechat/googlechat.xml | 2 +- source/packages/plg_mokosuitecross_hashnode/hashnode.xml | 2 +- source/packages/plg_mokosuitecross_instagram/instagram.xml | 2 +- source/packages/plg_mokosuitecross_linkedin/linkedin.xml | 2 +- source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml | 2 +- source/packages/plg_mokosuitecross_mastodon/mastodon.xml | 2 +- source/packages/plg_mokosuitecross_matrix/matrix.xml | 2 +- source/packages/plg_mokosuitecross_medium/medium.xml | 2 +- .../plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml | 2 +- .../plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml | 2 +- source/packages/plg_mokosuitecross_nostr/nostr.xml | 2 +- source/packages/plg_mokosuitecross_ntfy/ntfy.xml | 2 +- source/packages/plg_mokosuitecross_pinterest/pinterest.xml | 2 +- source/packages/plg_mokosuitecross_reddit/reddit.xml | 2 +- source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml | 2 +- source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml | 2 +- source/packages/plg_mokosuitecross_slack/slack.xml | 2 +- source/packages/plg_mokosuitecross_teams/teams.xml | 2 +- source/packages/plg_mokosuitecross_telegram/telegram.xml | 2 +- source/packages/plg_mokosuitecross_threads/threads.xml | 2 +- source/packages/plg_mokosuitecross_tiktok/tiktok.xml | 2 +- source/packages/plg_mokosuitecross_tumblr/tumblr.xml | 2 +- source/packages/plg_mokosuitecross_twitter/twitter.xml | 2 +- source/packages/plg_mokosuitecross_webhook/webhook.xml | 2 +- source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml | 2 +- source/packages/plg_mokosuitecross_wordpress/wordpress.xml | 2 +- source/packages/plg_mokosuitecross_youtube/youtube.xml | 2 +- source/packages/plg_system_mokosuitecross/mokosuitecross.xml | 2 +- .../plg_system_mokosuitecross_events/mokosuitecross_events.xml | 2 +- .../mokosuitecross_gallery.xml | 2 +- source/packages/plg_task_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/plg_webservices_mokosuitecross/mokosuitecross.xml | 2 +- source/pkg_mokosuitecross.xml | 2 +- 52 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.12.05.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 23df8793..6d12ff5f 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.12.04 +# VERSION: 01.12.05 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 90222f8e..403514ac 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.12.04 + VERSION: 01.12.05 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index a5044372..52cb3cec 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.12.04 + VERSION: 01.12.05 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index b556716d..454b8316 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteCross - + Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 6. diff --git a/SECURITY.md b/SECURITY.md index d09cf8f7..bb604538 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.12.04 +VERSION: 01.12.05 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index 09188693..956c9f34 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.05.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.05.sql new file mode 100644 index 00000000..1d4d5a69 --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.05.sql @@ -0,0 +1 @@ +/* 01.12.05 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index 7036e6bb..45ef311f 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml index 70434a79..6fbe8263 100644 --- a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml +++ b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ActivityPub (Fediverse) - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_blogger/blogger.xml b/source/packages/plg_mokosuitecross_blogger/blogger.xml index 5a6e0679..c2c6adf8 100644 --- a/source/packages/plg_mokosuitecross_blogger/blogger.xml +++ b/source/packages/plg_mokosuitecross_blogger/blogger.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Blogger - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml index 06a12435..341235a2 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_brevo/brevo.xml b/source/packages/plg_mokosuitecross_brevo/brevo.xml index 768c907a..546fa023 100644 --- a/source/packages/plg_mokosuitecross_brevo/brevo.xml +++ b/source/packages/plg_mokosuitecross_brevo/brevo.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Brevo (Sendinblue) - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml index 97e5a386..ff918f17 100644 --- a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml +++ b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Constant Contact - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml index d2b28413..f76bac23 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_devto/devto.xml b/source/packages/plg_mokosuitecross_devto/devto.xml index 9cb6792a..acc79e92 100644 --- a/source/packages/plg_mokosuitecross_devto/devto.xml +++ b/source/packages/plg_mokosuitecross_devto/devto.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Dev.to - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_discord/discord.xml b/source/packages/plg_mokosuitecross_discord/discord.xml index 94cbf1eb..bdc2b5b3 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_facebook/facebook.xml b/source/packages/plg_mokosuitecross_facebook/facebook.xml index 2cda66e8..dbfa04ac 100644 --- a/source/packages/plg_mokosuitecross_facebook/facebook.xml +++ b/source/packages/plg_mokosuitecross_facebook/facebook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Facebook / Meta - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ghost/ghost.xml b/source/packages/plg_mokosuitecross_ghost/ghost.xml index e5fd20a2..0d5807fb 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml index c1ab9937..a3d0ba9b 100644 --- a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml +++ b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Business Profile - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml index 5dd91d25..974b32b2 100644 --- a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml +++ b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Chat - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml index e8cda0e5..a47f1918 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_instagram/instagram.xml b/source/packages/plg_mokosuitecross_instagram/instagram.xml index aebaa878..7b48f393 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.12.04 + 01.12.05 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml index fad5b33b..7708db43 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml index a3388cf7..6092f196 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml index a46ae5bf..056ed040 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_matrix/matrix.xml b/source/packages/plg_mokosuitecross_matrix/matrix.xml index 8ba76c9f..4a5a2ae7 100644 --- a/source/packages/plg_mokosuitecross_matrix/matrix.xml +++ b/source/packages/plg_mokosuitecross_matrix/matrix.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Matrix / Element - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_medium/medium.xml b/source/packages/plg_mokosuitecross_medium/medium.xml index 556f74ab..7d4e45e5 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml index e31999cb..051d0043 100644 --- a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml +++ b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteCalendar Events - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml index 63fd0f93..d5dc49db 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_nostr/nostr.xml b/source/packages/plg_mokosuitecross_nostr/nostr.xml index 3779a267..2b647076 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml index a933dd95..2f51182f 100644 --- a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml +++ b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ntfy Push Notifications - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml index 38173596..19f2a5f0 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_reddit/reddit.xml b/source/packages/plg_mokosuitecross_reddit/reddit.xml index b647c0bd..5ca3a585 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml index ed2b01c4..e61c175c 100644 --- a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml +++ b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml @@ -1,7 +1,7 @@ MokoSuiteCross - RSS Feed - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml index 2dc1e1c5..3089b5e6 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_slack/slack.xml b/source/packages/plg_mokosuitecross_slack/slack.xml index f800efac..07401f3d 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_teams/teams.xml b/source/packages/plg_mokosuitecross_teams/teams.xml index 5e0f602d..c986fc7c 100644 --- a/source/packages/plg_mokosuitecross_teams/teams.xml +++ b/source/packages/plg_mokosuitecross_teams/teams.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Microsoft Teams - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_telegram/telegram.xml b/source/packages/plg_mokosuitecross_telegram/telegram.xml index 5abed5ee..a8b03136 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_threads/threads.xml b/source/packages/plg_mokosuitecross_threads/threads.xml index 9267679e..d4cc1505 100644 --- a/source/packages/plg_mokosuitecross_threads/threads.xml +++ b/source/packages/plg_mokosuitecross_threads/threads.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Threads (Meta) - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml index 2bd9634a..732657e3 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml index 8a791da9..1adf95d7 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_twitter/twitter.xml b/source/packages/plg_mokosuitecross_twitter/twitter.xml index 6e6f1e8f..b5882553 100644 --- a/source/packages/plg_mokosuitecross_twitter/twitter.xml +++ b/source/packages/plg_mokosuitecross_twitter/twitter.xml @@ -1,7 +1,7 @@ MokoSuiteCross - X / Twitter - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_webhook/webhook.xml b/source/packages/plg_mokosuitecross_webhook/webhook.xml index ffca9dbc..6b3763b4 100644 --- a/source/packages/plg_mokosuitecross_webhook/webhook.xml +++ b/source/packages/plg_mokosuitecross_webhook/webhook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Generic Webhook - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml index 58f5139b..97820e3d 100644 --- a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml +++ b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WhatsApp Business - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml index e8b09da6..6ee68354 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_youtube/youtube.xml b/source/packages/plg_mokosuitecross_youtube/youtube.xml index f38c3a63..ba6a34ba 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.12.04 + 01.12.05 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml index 4998e370..8e3f2377 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml index bdcd619f..717d5471 100644 --- a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml +++ b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Events - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml index b77fe592..d175b992 100644 --- a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml +++ b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Gallery - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml index a9eb109b..48acb322 100644 --- a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Task - MokoSuiteCross Queue Processor - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml index 7031fbb1..b43f16fd 100644 --- a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Web Services - MokoSuiteCross - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index dade274e..1bdd850b 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.12.04 + 01.12.05 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 08a898cd7e2ebfdc7c0d616390bceaa4a6c01558 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 28 Jun 2026 17:06:03 +0000 Subject: [PATCH 09/10] chore(version): pre-release bump to 01.08.59-dev [skip ci] --- .../packages/com_mokosuitecross/sql/updates/mysql/01.08.59.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.08.59.sql diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.59.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.59.sql new file mode 100644 index 00000000..bdb022b9 --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.59.sql @@ -0,0 +1 @@ +/* 01.08.59 — no schema changes */ -- 2.52.0 From 423f2f4ccefd97bc0955e274a29c1e5e706092d9 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Mon, 29 Jun 2026 11:40:17 +0000 Subject: [PATCH 10/10] chore(version): pre-release bump to 01.12.06-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CODE_OF_CONDUCT.md | 2 +- GOVERNANCE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.12.06.sql | 1 + source/packages/plg_content_mokosuitecross/mokosuitecross.xml | 2 +- source/packages/plg_mokosuitecross_activitypub/activitypub.xml | 2 +- source/packages/plg_mokosuitecross_blogger/blogger.xml | 2 +- source/packages/plg_mokosuitecross_bluesky/bluesky.xml | 2 +- source/packages/plg_mokosuitecross_brevo/brevo.xml | 2 +- .../plg_mokosuitecross_constantcontact/constantcontact.xml | 2 +- source/packages/plg_mokosuitecross_convertkit/convertkit.xml | 2 +- source/packages/plg_mokosuitecross_devto/devto.xml | 2 +- source/packages/plg_mokosuitecross_discord/discord.xml | 2 +- source/packages/plg_mokosuitecross_facebook/facebook.xml | 2 +- source/packages/plg_mokosuitecross_ghost/ghost.xml | 2 +- .../plg_mokosuitecross_googlebusiness/googlebusiness.xml | 2 +- source/packages/plg_mokosuitecross_googlechat/googlechat.xml | 2 +- source/packages/plg_mokosuitecross_hashnode/hashnode.xml | 2 +- source/packages/plg_mokosuitecross_instagram/instagram.xml | 2 +- source/packages/plg_mokosuitecross_linkedin/linkedin.xml | 2 +- source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml | 2 +- source/packages/plg_mokosuitecross_mastodon/mastodon.xml | 2 +- source/packages/plg_mokosuitecross_matrix/matrix.xml | 2 +- source/packages/plg_mokosuitecross_medium/medium.xml | 2 +- .../plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml | 2 +- .../plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml | 2 +- source/packages/plg_mokosuitecross_nostr/nostr.xml | 2 +- source/packages/plg_mokosuitecross_ntfy/ntfy.xml | 2 +- source/packages/plg_mokosuitecross_pinterest/pinterest.xml | 2 +- source/packages/plg_mokosuitecross_reddit/reddit.xml | 2 +- source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml | 2 +- source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml | 2 +- source/packages/plg_mokosuitecross_slack/slack.xml | 2 +- source/packages/plg_mokosuitecross_teams/teams.xml | 2 +- source/packages/plg_mokosuitecross_telegram/telegram.xml | 2 +- source/packages/plg_mokosuitecross_threads/threads.xml | 2 +- source/packages/plg_mokosuitecross_tiktok/tiktok.xml | 2 +- source/packages/plg_mokosuitecross_tumblr/tumblr.xml | 2 +- source/packages/plg_mokosuitecross_twitter/twitter.xml | 2 +- source/packages/plg_mokosuitecross_webhook/webhook.xml | 2 +- source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml | 2 +- source/packages/plg_mokosuitecross_wordpress/wordpress.xml | 2 +- source/packages/plg_mokosuitecross_youtube/youtube.xml | 2 +- source/packages/plg_system_mokosuitecross/mokosuitecross.xml | 2 +- .../plg_system_mokosuitecross_events/mokosuitecross_events.xml | 2 +- .../mokosuitecross_gallery.xml | 2 +- source/packages/plg_task_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/plg_webservices_mokosuitecross/mokosuitecross.xml | 2 +- source/pkg_mokosuitecross.xml | 2 +- 52 files changed, 52 insertions(+), 51 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.12.06.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 6d12ff5f..45b504ba 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: mokocli.Automation -# VERSION: 01.12.05 +# VERSION: 01.12.06 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 403514ac..0b138bf6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ - VERSION: 01.12.05 + VERSION: 01.12.06 PATH: ./CODE_OF_CONDUCT.md BRIEF: Community expectations and enforcement guidelines NOTE: Adapted with attribution from the Contributor Covenant v2.1 diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 52cb3cec..b63745a6 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.Template-Joomla INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/Template-Joomla - VERSION: 01.12.05 + VERSION: 01.12.06 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for Template-Joomla --> diff --git a/README.md b/README.md index 454b8316..be11b24e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MokoSuiteCross - + Cross-posting Joomla content to social media, email marketing, and chat platforms for Joomla 6. diff --git a/SECURITY.md b/SECURITY.md index bb604538..d6f34957 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla INGROUP: Template-Joomla.Documentation REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla PATH: /SECURITY.md -VERSION: 01.12.05 +VERSION: 01.12.06 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index 956c9f34..c7d04198 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.06.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.06.sql new file mode 100644 index 00000000..60f87db8 --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.12.06.sql @@ -0,0 +1 @@ +/* 01.12.06 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index 45ef311f..041b96fc 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml index 6fbe8263..e7719b2d 100644 --- a/source/packages/plg_mokosuitecross_activitypub/activitypub.xml +++ b/source/packages/plg_mokosuitecross_activitypub/activitypub.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ActivityPub (Fediverse) - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_blogger/blogger.xml b/source/packages/plg_mokosuitecross_blogger/blogger.xml index c2c6adf8..45ccdf0e 100644 --- a/source/packages/plg_mokosuitecross_blogger/blogger.xml +++ b/source/packages/plg_mokosuitecross_blogger/blogger.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Blogger - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml index 341235a2..508a2830 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_brevo/brevo.xml b/source/packages/plg_mokosuitecross_brevo/brevo.xml index 546fa023..1cc70890 100644 --- a/source/packages/plg_mokosuitecross_brevo/brevo.xml +++ b/source/packages/plg_mokosuitecross_brevo/brevo.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Brevo (Sendinblue) - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml index ff918f17..907fd45f 100644 --- a/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml +++ b/source/packages/plg_mokosuitecross_constantcontact/constantcontact.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Constant Contact - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml index f76bac23..84d782b7 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_devto/devto.xml b/source/packages/plg_mokosuitecross_devto/devto.xml index acc79e92..a652514c 100644 --- a/source/packages/plg_mokosuitecross_devto/devto.xml +++ b/source/packages/plg_mokosuitecross_devto/devto.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Dev.to - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_discord/discord.xml b/source/packages/plg_mokosuitecross_discord/discord.xml index bdc2b5b3..9df9f2b7 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_facebook/facebook.xml b/source/packages/plg_mokosuitecross_facebook/facebook.xml index dbfa04ac..438e1310 100644 --- a/source/packages/plg_mokosuitecross_facebook/facebook.xml +++ b/source/packages/plg_mokosuitecross_facebook/facebook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Facebook / Meta - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ghost/ghost.xml b/source/packages/plg_mokosuitecross_ghost/ghost.xml index 0d5807fb..6529df4b 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml index a3d0ba9b..9fb1041e 100644 --- a/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml +++ b/source/packages/plg_mokosuitecross_googlebusiness/googlebusiness.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Business Profile - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml index 974b32b2..3310cf2d 100644 --- a/source/packages/plg_mokosuitecross_googlechat/googlechat.xml +++ b/source/packages/plg_mokosuitecross_googlechat/googlechat.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Google Chat - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml index a47f1918..09bc269d 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_instagram/instagram.xml b/source/packages/plg_mokosuitecross_instagram/instagram.xml index 7b48f393..decfe385 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.12.05 + 01.12.06 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml index 7708db43..6d02d4b5 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml index 6092f196..9c03c94b 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml index 056ed040..0df0dc1d 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_matrix/matrix.xml b/source/packages/plg_mokosuitecross_matrix/matrix.xml index 4a5a2ae7..a6440cc2 100644 --- a/source/packages/plg_mokosuitecross_matrix/matrix.xml +++ b/source/packages/plg_mokosuitecross_matrix/matrix.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Matrix / Element - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_medium/medium.xml b/source/packages/plg_mokosuitecross_medium/medium.xml index 7d4e45e5..423d2256 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml index 051d0043..ac3a5648 100644 --- a/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml +++ b/source/packages/plg_mokosuitecross_mokosuitecalendar/mokosuitecalendar.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteCalendar Events - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml index d5dc49db..53345c03 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_nostr/nostr.xml b/source/packages/plg_mokosuitecross_nostr/nostr.xml index 2b647076..7112ca47 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml index 2f51182f..7ac4d4e6 100644 --- a/source/packages/plg_mokosuitecross_ntfy/ntfy.xml +++ b/source/packages/plg_mokosuitecross_ntfy/ntfy.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ntfy Push Notifications - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml index 19f2a5f0..c1d4b67b 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_reddit/reddit.xml b/source/packages/plg_mokosuitecross_reddit/reddit.xml index 5ca3a585..29de5059 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml index e61c175c..fb62c347 100644 --- a/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml +++ b/source/packages/plg_mokosuitecross_rssfeed/rssfeed.xml @@ -1,7 +1,7 @@ MokoSuiteCross - RSS Feed - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml index 3089b5e6..b88b5d0c 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_slack/slack.xml b/source/packages/plg_mokosuitecross_slack/slack.xml index 07401f3d..be2ad516 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_teams/teams.xml b/source/packages/plg_mokosuitecross_teams/teams.xml index c986fc7c..057e3246 100644 --- a/source/packages/plg_mokosuitecross_teams/teams.xml +++ b/source/packages/plg_mokosuitecross_teams/teams.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Microsoft Teams - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_telegram/telegram.xml b/source/packages/plg_mokosuitecross_telegram/telegram.xml index a8b03136..4af6dadd 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_threads/threads.xml b/source/packages/plg_mokosuitecross_threads/threads.xml index d4cc1505..3384d217 100644 --- a/source/packages/plg_mokosuitecross_threads/threads.xml +++ b/source/packages/plg_mokosuitecross_threads/threads.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Threads (Meta) - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml index 732657e3..29e85c4f 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml index 1adf95d7..08169c9a 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_twitter/twitter.xml b/source/packages/plg_mokosuitecross_twitter/twitter.xml index b5882553..0820978b 100644 --- a/source/packages/plg_mokosuitecross_twitter/twitter.xml +++ b/source/packages/plg_mokosuitecross_twitter/twitter.xml @@ -1,7 +1,7 @@ MokoSuiteCross - X / Twitter - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_webhook/webhook.xml b/source/packages/plg_mokosuitecross_webhook/webhook.xml index 6b3763b4..78958ed7 100644 --- a/source/packages/plg_mokosuitecross_webhook/webhook.xml +++ b/source/packages/plg_mokosuitecross_webhook/webhook.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Generic Webhook - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml index 97820e3d..445ef3c8 100644 --- a/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml +++ b/source/packages/plg_mokosuitecross_whatsapp/whatsapp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WhatsApp Business - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml index 6ee68354..e6e51a90 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_mokosuitecross_youtube/youtube.xml b/source/packages/plg_mokosuitecross_youtube/youtube.xml index ba6a34ba..20de935b 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.12.05 + 01.12.06 2026-06-23 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml index 8e3f2377..3efd8c89 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml index 717d5471..71c04013 100644 --- a/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml +++ b/source/packages/plg_system_mokosuitecross_events/mokosuitecross_events.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Events - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml index d175b992..8accd397 100644 --- a/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml +++ b/source/packages/plg_system_mokosuitecross_gallery/mokosuitecross_gallery.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross Gallery - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml index 48acb322..51ae0a89 100644 --- a/source/packages/plg_task_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_task_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Task - MokoSuiteCross Queue Processor - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml index b43f16fd..78a7711c 100644 --- a/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_webservices_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Web Services - MokoSuiteCross - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index 1bdd850b..9893f414 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.12.05 + 01.12.06 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0