From e183b62aba949ba10b6878cc8f684addc8ff5b50 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 27 Jun 2026 15:21:45 -0500 Subject: [PATCH 1/2] feat: implement Nostr NIP-01 WebSocket relay publishing (#129) - BIP-340 Schnorr signatures over secp256k1 (pure PHP, requires ext-gmp) - Kind-1 text note events with SHA-256 event ID and tagged hashes - Raw WebSocket client via stream_socket_client (zero external deps) - Multi-relay failover: tries each relay until one accepts - Public key derivation from private key for account display - Validates 64-char hex private key format and wss:// relay URLs Authored-by: Moko Consulting --- CHANGELOG.md | 6 + .../en-GB/plg_mokosuitecross_nostr.ini | 2 +- .../src/Extension/NostrService.php | 389 +++++++++++++++++- 3 files changed, 379 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee1637a..293b410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog ## [Unreleased] +### Added +- **Nostr plugin**: Full NIP-01 WebSocket relay publishing with BIP-340 Schnorr signatures (pure PHP, requires ext-gmp) +- **Nostr**: Publishes kind-1 text note events to multiple relays with automatic failover +- **Nostr**: Raw WebSocket client using stream_socket_client (no external dependencies) +- **Nostr**: Public key derivation and event signing via secp256k1 elliptic curve math + ### Fixed - Webservices plugin Joomla 6 compatibility — `onBeforeApiRoute` receives `BeforeApiRouteEvent` object, extract router via `$event->getRouter()` diff --git a/source/packages/plg_mokosuitecross_nostr/language/en-GB/plg_mokosuitecross_nostr.ini b/source/packages/plg_mokosuitecross_nostr/language/en-GB/plg_mokosuitecross_nostr.ini index 3c87b21..e5de006 100644 --- a/source/packages/plg_mokosuitecross_nostr/language/en-GB/plg_mokosuitecross_nostr.ini +++ b/source/packages/plg_mokosuitecross_nostr/language/en-GB/plg_mokosuitecross_nostr.ini @@ -1,2 +1,2 @@ PLG_MOKOSUITECROSS_NOSTR="MokoSuiteCross - Nostr" -PLG_MOKOSUITECROSS_NOSTR_DESCRIPTION="Cross-post Joomla articles to Nostr." +PLG_MOKOSUITECROSS_NOSTR_DESCRIPTION="Cross-post Joomla articles to Nostr relays via NIP-01 WebSocket protocol. Requires PHP ext-gmp for secp256k1 Schnorr signing." diff --git a/source/packages/plg_mokosuitecross_nostr/src/Extension/NostrService.php b/source/packages/plg_mokosuitecross_nostr/src/Extension/NostrService.php index 56a5eed..2f27c9e 100644 --- a/source/packages/plg_mokosuitecross_nostr/src/Extension/NostrService.php +++ b/source/packages/plg_mokosuitecross_nostr/src/Extension/NostrService.php @@ -20,12 +20,18 @@ use Joomla\Event\SubscriberInterface; /** * Nostr service plugin for MokoSuiteCross. * - * Nostr uses NIP-01 WebSocket relays for event publishing. - * This is a stub — full WebSocket implementation is deferred. - * Events are signed with the private key and sent to configured relays. + * Publishes kind-1 text note events to NIP-01 WebSocket relays. + * Uses BIP-340 Schnorr signatures over secp256k1 (requires ext-gmp). + * + * Credentials: private_key (64-char hex nsec), relays (comma-separated wss:// URLs) */ class NostrService extends CMSPlugin implements SubscriberInterface, MokoSuiteCrossServiceInterface { + private const SECP256K1_P = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'; + private const SECP256K1_N = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'; + private const SECP256K1_GX = '79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'; + private const SECP256K1_GY = '483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8'; + public static function getSubscribedEvents(): array { return ['onMokoSuiteCrossGetServices' => 'onMokoSuiteCrossGetServices']; @@ -43,6 +49,10 @@ class NostrService extends CMSPlugin implements SubscriberInterface, MokoSuiteCr public function publish(string $message, array $media, array $credentials, array $params): array { + if (!extension_loaded('gmp')) { + return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'PHP ext-gmp is required for Nostr signing.']]; + } + $privateKey = $credentials['private_key'] ?? ''; $relays = $credentials['relays'] ?? ''; @@ -50,48 +60,393 @@ class NostrService extends CMSPlugin implements SubscriberInterface, MokoSuiteCr return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'Missing private key or relay URLs.']]; } - // Nostr requires WebSocket connections to relays (wss://). - // Full NIP-01 event signing and relay publishing is not yet implemented. + $privateKey = strtolower(trim($privateKey)); + + if (!preg_match('/^[0-9a-f]{64}$/', $privateKey)) { + return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'Private key must be 64 hex characters.']]; + } + + $pubkey = $this->getPublicKey($privateKey); + + $event = $this->createEvent($pubkey, $message); + $event['sig'] = $this->schnorrSign($event['id'], $privateKey); + + $relayList = array_filter(array_map('trim', explode(',', $relays))); + $lastError = ''; + $published = false; + + foreach ($relayList as $relayUrl) { + $result = $this->publishToRelay($relayUrl, $event); + + if ($result['success']) { + $published = true; + break; + } + + $lastError = $result['error']; + } + + if (!$published) { + return ['success' => false, 'platform_post_id' => '', 'response' => ['error' => 'All relays failed. Last: ' . $lastError]]; + } + return [ - 'success' => false, - 'platform_post_id' => '', - 'response' => ['error' => 'Nostr WebSocket relay publishing is not yet implemented. This service will be available in a future release.'], + 'success' => true, + 'platform_post_id' => $event['id'], + 'response' => ['event_id' => $event['id'], 'relay' => $relayUrl ?? ''], ]; } public function validateCredentials(array $credentials): array { - $privateKey = $credentials['private_key'] ?? ''; + if (!extension_loaded('gmp')) { + return ['valid' => false, 'message' => 'PHP ext-gmp is required for Nostr.', 'account_name' => '']; + } + + $privateKey = strtolower(trim($credentials['private_key'] ?? '')); $relays = $credentials['relays'] ?? ''; if (empty($privateKey)) { return ['valid' => false, 'message' => 'Private key is required.', 'account_name' => '']; } + if (!preg_match('/^[0-9a-f]{64}$/', $privateKey)) { + return ['valid' => false, 'message' => 'Private key must be 64 hex characters.', 'account_name' => '']; + } + if (empty($relays)) { return ['valid' => false, 'message' => 'At least one relay URL is required.', 'account_name' => '']; } - // Validate that relay URLs look like WebSocket URLs $relayList = array_filter(array_map('trim', explode(',', $relays))); - $valid = true; foreach ($relayList as $relay) { if (!str_starts_with($relay, 'wss://') && !str_starts_with($relay, 'ws://')) { - $valid = false; - break; + return ['valid' => false, 'message' => 'Relay URLs must start with wss:// or ws://.', 'account_name' => '']; } } - if (!$valid) { - return ['valid' => false, 'message' => 'Relay URLs must start with wss:// or ws://.', 'account_name' => '']; - } + $pubkey = $this->getPublicKey($privateKey); + $npub = substr($pubkey, 0, 16) . '...'; - return ['valid' => true, 'message' => 'Credentials configured (' . count($relayList) . ' relay(s))', 'account_name' => 'Nostr']; + return ['valid' => true, 'message' => count($relayList) . ' relay(s) configured', 'account_name' => 'npub:' . $npub]; } public function getSupportedMediaTypes(): array { return []; } + + // -- NIP-01 event creation -- + + private function createEvent(string $pubkey, string $content, int $kind = 1, array $tags = []): array + { + $createdAt = time(); + $serialized = json_encode([0, $pubkey, $createdAt, $kind, $tags, $content], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $id = hash('sha256', $serialized); + + return [ + 'id' => $id, + 'pubkey' => $pubkey, + 'created_at' => $createdAt, + 'kind' => $kind, + 'tags' => $tags, + 'content' => $content, + 'sig' => '', + ]; + } + + // -- WebSocket relay publishing -- + + private function publishToRelay(string $relayUrl, array $event): array + { + $parsed = parse_url($relayUrl); + + if (!$parsed || !isset($parsed['host'])) { + return ['success' => false, 'error' => 'Invalid relay URL']; + } + + $scheme = $parsed['scheme'] ?? 'wss'; + $host = $parsed['host']; + $port = $parsed['port'] ?? ($scheme === 'wss' ? 443 : 80); + $path = $parsed['path'] ?? '/'; + $useTls = ($scheme === 'wss'); + + $address = ($useTls ? 'tls://' : 'tcp://') . $host . ':' . $port; + $context = stream_context_create(['ssl' => ['verify_peer' => true, 'verify_peer_name' => true]]); + + $socket = @stream_socket_client($address, $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context); + + if (!$socket) { + return ['success' => false, 'error' => "Connection failed: {$errstr} ({$errno})"]; + } + + stream_set_timeout($socket, 10); + + // WebSocket upgrade handshake + $wsKey = base64_encode(random_bytes(16)); + $handshake = "GET {$path} HTTP/1.1\r\n" + . "Host: {$host}\r\n" + . "Upgrade: websocket\r\n" + . "Connection: Upgrade\r\n" + . "Sec-WebSocket-Key: {$wsKey}\r\n" + . "Sec-WebSocket-Version: 13\r\n" + . "\r\n"; + + fwrite($socket, $handshake); + + $response = ''; + + while (($line = fgets($socket)) !== false) { + $response .= $line; + + if (trim($line) === '') { + break; + } + } + + if (strpos($response, '101') === false) { + fclose($socket); + + return ['success' => false, 'error' => 'WebSocket upgrade failed']; + } + + // Send EVENT message + $payload = json_encode(['EVENT', $event], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $this->wsWrite($socket, $payload); + + // Read OK response (with timeout) + $reply = $this->wsRead($socket); + fclose($socket); + + if ($reply === null) { + return ['success' => false, 'error' => 'No response from relay']; + } + + $decoded = json_decode($reply, true); + + if (!is_array($decoded) || ($decoded[0] ?? '') !== 'OK') { + $msg = is_array($decoded) ? ($decoded[3] ?? $decoded[2] ?? 'Unknown error') : 'Invalid response'; + + return ['success' => false, 'error' => (string) $msg]; + } + + // ["OK", event_id, true/false, message] + $accepted = $decoded[2] ?? false; + + if (!$accepted) { + return ['success' => false, 'error' => $decoded[3] ?? 'Relay rejected event']; + } + + return ['success' => true, 'error' => '']; + } + + private function wsWrite($socket, string $data): void + { + $len = strlen($data); + $frame = chr(0x81); // text frame, FIN bit set + $mask = random_bytes(4); + + if ($len < 126) { + $frame .= chr($len | 0x80); // mask bit set + } elseif ($len < 65536) { + $frame .= chr(126 | 0x80) . pack('n', $len); + } else { + $frame .= chr(127 | 0x80) . pack('J', $len); + } + + $frame .= $mask; + + for ($i = 0; $i < $len; $i++) { + $frame .= $data[$i] ^ $mask[$i % 4]; + } + + fwrite($socket, $frame); + } + + private function wsRead($socket): ?string + { + $header = fread($socket, 2); + + if ($header === false || strlen($header) < 2) { + return null; + } + + $len = ord($header[1]) & 0x7F; + + if ($len === 126) { + $ext = fread($socket, 2); + $len = unpack('n', $ext)[1]; + } elseif ($len === 127) { + $ext = fread($socket, 8); + $len = unpack('J', $ext)[1]; + } + + $masked = (ord($header[1]) & 0x80) !== 0; + $mask = $masked ? fread($socket, 4) : ''; + $data = ''; + + while (strlen($data) < $len) { + $chunk = fread($socket, $len - strlen($data)); + + if ($chunk === false) { + break; + } + + $data .= $chunk; + } + + if ($masked) { + for ($i = 0; $i < strlen($data); $i++) { + $data[$i] = $data[$i] ^ $mask[$i % 4]; + } + } + + return $data; + } + + // -- BIP-340 Schnorr signature over secp256k1 -- + + private function getPublicKey(string $privateKeyHex): string + { + $d = gmp_init($privateKeyHex, 16); + $G = [gmp_init(self::SECP256K1_GX, 16), gmp_init(self::SECP256K1_GY, 16)]; + + $point = $this->ecMultiply($G, $d); + + return str_pad(gmp_strval($point[0], 16), 64, '0', STR_PAD_LEFT); + } + + private function schnorrSign(string $messageHex, string $privateKeyHex): string + { + $p = gmp_init(self::SECP256K1_P, 16); + $n = gmp_init(self::SECP256K1_N, 16); + $G = [gmp_init(self::SECP256K1_GX, 16), gmp_init(self::SECP256K1_GY, 16)]; + + $d = gmp_init($privateKeyHex, 16); + $P = $this->ecMultiply($G, $d); + $px = str_pad(gmp_strval($P[0], 16), 64, '0', STR_PAD_LEFT); + + // BIP-340: if P.y is odd, negate d + if (gmp_testbit($P[1], 0)) { + $d = gmp_sub($n, $d); + } + + $dBytes = hex2bin(str_pad(gmp_strval($d, 16), 64, '0', STR_PAD_LEFT)); + $auxRand = random_bytes(32); + $t = $dBytes ^ $this->taggedHash('BIP0340/aux', $auxRand); + $pxBytes = hex2bin($px); + $msgBytes = hex2bin($messageHex); + + $rand = $this->taggedHash('BIP0340/nonce', $t . $pxBytes . $msgBytes); + $k0 = gmp_mod(gmp_init(bin2hex($rand), 16), $n); + + if (gmp_cmp($k0, 0) === 0) { + return str_repeat('00', 64); + } + + $R = $this->ecMultiply($G, $k0); + $k = gmp_testbit($R[1], 0) ? gmp_sub($n, $k0) : $k0; + + $rx = str_pad(gmp_strval($R[0], 16), 64, '0', STR_PAD_LEFT); + $rxBytes = hex2bin($rx); + + $eHash = $this->taggedHash('BIP0340/challenge', $rxBytes . $pxBytes . $msgBytes); + $e = gmp_mod(gmp_init(bin2hex($eHash), 16), $n); + + $s = gmp_mod(gmp_add($k, gmp_mul($e, $d)), $n); + + return $rx . str_pad(gmp_strval($s, 16), 64, '0', STR_PAD_LEFT); + } + + private function taggedHash(string $tag, string $data): string + { + $tagHash = hash('sha256', $tag, true); + + return hash('sha256', $tagHash . $tagHash . $data, true); + } + + // -- secp256k1 elliptic curve arithmetic -- + + private function ecMultiply(array $point, \GMP $scalar): array + { + $result = null; + $addend = $point; + $n = gmp_init(self::SECP256K1_N, 16); + + $scalar = gmp_mod($scalar, $n); + + while (gmp_cmp($scalar, 0) > 0) { + if (gmp_testbit($scalar, 0)) { + $result = $result === null ? $addend : $this->ecAdd($result, $addend); + } + + $addend = $this->ecDouble($addend); + $scalar = gmp_div_q($scalar, 2); + } + + return $result; + } + + private function ecAdd(array $p1, array $p2): array + { + $prime = gmp_init(self::SECP256K1_P, 16); + + if (gmp_cmp($p1[0], $p2[0]) === 0 && gmp_cmp($p1[1], $p2[1]) === 0) { + return $this->ecDouble($p1); + } + + $dx = gmp_mod(gmp_sub($p2[0], $p1[0]), $prime); + + if (gmp_cmp($dx, 0) < 0) { + $dx = gmp_add($dx, $prime); + } + + $dy = gmp_mod(gmp_sub($p2[1], $p1[1]), $prime); + + if (gmp_cmp($dy, 0) < 0) { + $dy = gmp_add($dy, $prime); + } + + $slope = gmp_mod(gmp_mul($dy, gmp_invert($dx, $prime)), $prime); + $x3 = gmp_mod(gmp_sub(gmp_sub(gmp_mul($slope, $slope), $p1[0]), $p2[0]), $prime); + $y3 = gmp_mod(gmp_sub(gmp_mul($slope, gmp_sub($p1[0], $x3)), $p1[1]), $prime); + + if (gmp_cmp($x3, 0) < 0) { + $x3 = gmp_add($x3, $prime); + } + + if (gmp_cmp($y3, 0) < 0) { + $y3 = gmp_add($y3, $prime); + } + + return [$x3, $y3]; + } + + private function ecDouble(array $point): array + { + $prime = gmp_init(self::SECP256K1_P, 16); + + // secp256k1 has a=0, so slope = 3*x^2 / (2*y) + $num = gmp_mod(gmp_mul(3, gmp_mul($point[0], $point[0])), $prime); + $denom = gmp_mod(gmp_mul(2, $point[1]), $prime); + + if (gmp_cmp($denom, 0) < 0) { + $denom = gmp_add($denom, $prime); + } + + $slope = gmp_mod(gmp_mul($num, gmp_invert($denom, $prime)), $prime); + $x3 = gmp_mod(gmp_sub(gmp_mul($slope, $slope), gmp_mul(2, $point[0])), $prime); + $y3 = gmp_mod(gmp_sub(gmp_mul($slope, gmp_sub($point[0], $x3)), $point[1]), $prime); + + if (gmp_cmp($x3, 0) < 0) { + $x3 = gmp_add($x3, $prime); + } + + if (gmp_cmp($y3, 0) < 0) { + $y3 = gmp_add($y3, $prime); + } + + return [$x3, $y3]; + } } -- 2.52.0 From 13683adfba3fe131d4bd3c045d31b4a4b4874aa8 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 27 Jun 2026 20:22:09 +0000 Subject: [PATCH 2/2] chore(version): auto-bump patch 01.08.11-dev [skip ci] --- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- README.md | 2 +- source/packages/com_mokosuitecross/mokosuitecross.xml | 2 +- .../packages/com_mokosuitecross/sql/updates/mysql/01.08.11.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 +- 50 files changed, 50 insertions(+), 49 deletions(-) create mode 100644 source/packages/com_mokosuitecross/sql/updates/mysql/01.08.11.sql diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 88230d4..d6137d3 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.08.10 +# VERSION: 01.08.11 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 293b410..5df0894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,7 +74,7 @@ ## [01.03.00] --- 2026-06-21 - + All notable changes to MokoSuiteCross will be documented in this file. diff --git a/README.md b/README.md index d3a3914..2b2cb76 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 5/6. diff --git a/source/packages/com_mokosuitecross/mokosuitecross.xml b/source/packages/com_mokosuitecross/mokosuitecross.xml index 612b8e3..4570432 100644 --- a/source/packages/com_mokosuitecross/mokosuitecross.xml +++ b/source/packages/com_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ com_mokosuitecross - 01.08.10 + 01.08.11 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.11.sql b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.11.sql new file mode 100644 index 0000000..8657ee8 --- /dev/null +++ b/source/packages/com_mokosuitecross/sql/updates/mysql/01.08.11.sql @@ -0,0 +1 @@ +/* 01.08.11 — no schema changes */ diff --git a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml index 9b62be9..e1a5e3c 100644 --- a/source/packages/plg_content_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_content_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ Content - MokoSuiteCross - 01.08.10 + 01.08.11 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 77a159e..eedddf0 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.08.10 + 01.08.11 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 f12770a..3856971 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.08.10 + 01.08.11 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 d367bc1..0358560 100644 --- a/source/packages/plg_mokosuitecross_bluesky/bluesky.xml +++ b/source/packages/plg_mokosuitecross_bluesky/bluesky.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Bluesky - 01.08.10 + 01.08.11 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 006780b..2026819 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.08.10 + 01.08.11 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 0ab3978..009bd26 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.08.10 + 01.08.11 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 ce932e2..e4ee22a 100644 --- a/source/packages/plg_mokosuitecross_convertkit/convertkit.xml +++ b/source/packages/plg_mokosuitecross_convertkit/convertkit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - ConvertKit - 01.08.10 + 01.08.11 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 d21c606..0643dca 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.08.10 + 01.08.11 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 884982d..170c84b 100644 --- a/source/packages/plg_mokosuitecross_discord/discord.xml +++ b/source/packages/plg_mokosuitecross_discord/discord.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Discord - 01.08.10 + 01.08.11 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 10d6129..0356951 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.08.10 + 01.08.11 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 d28a66f..c921f3e 100644 --- a/source/packages/plg_mokosuitecross_ghost/ghost.xml +++ b/source/packages/plg_mokosuitecross_ghost/ghost.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Ghost - 01.08.10 + 01.08.11 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 1e58d25..35e6009 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.08.10 + 01.08.11 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 2be6025..375fc87 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.08.10 + 01.08.11 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 ba2462b..9744eb0 100644 --- a/source/packages/plg_mokosuitecross_hashnode/hashnode.xml +++ b/source/packages/plg_mokosuitecross_hashnode/hashnode.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Hashnode - 01.08.10 + 01.08.11 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 a50e0d5..72c8bd5 100644 --- a/source/packages/plg_mokosuitecross_instagram/instagram.xml +++ b/source/packages/plg_mokosuitecross_instagram/instagram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Instagram - 01.08.10 + 01.08.11 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 6bc1374..23539cb 100644 --- a/source/packages/plg_mokosuitecross_linkedin/linkedin.xml +++ b/source/packages/plg_mokosuitecross_linkedin/linkedin.xml @@ -1,7 +1,7 @@ MokoSuiteCross - LinkedIn - 01.08.10 + 01.08.11 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 37ecbdb..e85dd86 100644 --- a/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml +++ b/source/packages/plg_mokosuitecross_mailchimp/mailchimp.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mailchimp - 01.08.10 + 01.08.11 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 4406140..2b9679a 100644 --- a/source/packages/plg_mokosuitecross_mastodon/mastodon.xml +++ b/source/packages/plg_mokosuitecross_mastodon/mastodon.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Mastodon - 01.08.10 + 01.08.11 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 62560a0..4beb1c3 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.08.10 + 01.08.11 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 11544cc..467bdc9 100644 --- a/source/packages/plg_mokosuitecross_medium/medium.xml +++ b/source/packages/plg_mokosuitecross_medium/medium.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Medium - 01.08.10 + 01.08.11 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 e70f0f9..7c5ec03 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.08.10 + 01.08.11 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 9b688ea..7f4226d 100644 --- a/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml +++ b/source/packages/plg_mokosuitecross_mokosuitegallery/mokosuitegallery.xml @@ -1,7 +1,7 @@ MokoSuiteCross - MokoSuiteGallery - 01.08.10 + 01.08.11 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 484c850..4503a6c 100644 --- a/source/packages/plg_mokosuitecross_nostr/nostr.xml +++ b/source/packages/plg_mokosuitecross_nostr/nostr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Nostr - 01.08.10 + 01.08.11 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 117949b..da8f58a 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.08.10 + 01.08.11 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 bdc3999..fa347a2 100644 --- a/source/packages/plg_mokosuitecross_pinterest/pinterest.xml +++ b/source/packages/plg_mokosuitecross_pinterest/pinterest.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Pinterest - 01.08.10 + 01.08.11 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 2c6f6fd..3b37a93 100644 --- a/source/packages/plg_mokosuitecross_reddit/reddit.xml +++ b/source/packages/plg_mokosuitecross_reddit/reddit.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Reddit - 01.08.10 + 01.08.11 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 894007c..cc364fe 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.08.10 + 01.08.11 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 699b548..5aa48d0 100644 --- a/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml +++ b/source/packages/plg_mokosuitecross_sendgrid/sendgrid.xml @@ -1,7 +1,7 @@ MokoSuiteCross - SendGrid - 01.08.10 + 01.08.11 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 edc586f..b90269c 100644 --- a/source/packages/plg_mokosuitecross_slack/slack.xml +++ b/source/packages/plg_mokosuitecross_slack/slack.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Slack - 01.08.10 + 01.08.11 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 5372ea8..a3c16b9 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.08.10 + 01.08.11 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 942db84..a7981af 100644 --- a/source/packages/plg_mokosuitecross_telegram/telegram.xml +++ b/source/packages/plg_mokosuitecross_telegram/telegram.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Telegram - 01.08.10 + 01.08.11 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 6b46830..2163f56 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.08.10 + 01.08.11 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 dd577f1..b7fe5bd 100644 --- a/source/packages/plg_mokosuitecross_tiktok/tiktok.xml +++ b/source/packages/plg_mokosuitecross_tiktok/tiktok.xml @@ -1,7 +1,7 @@ MokoSuiteCross - TikTok - 01.08.10 + 01.08.11 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 7ffea01..ac25acf 100644 --- a/source/packages/plg_mokosuitecross_tumblr/tumblr.xml +++ b/source/packages/plg_mokosuitecross_tumblr/tumblr.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Tumblr - 01.08.10 + 01.08.11 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 30083f9..ff4df90 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.08.10 + 01.08.11 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 38d22b0..4ffe607 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.08.10 + 01.08.11 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 5dadfad..bbd2b86 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.08.10 + 01.08.11 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 0d78ea0..961229a 100644 --- a/source/packages/plg_mokosuitecross_wordpress/wordpress.xml +++ b/source/packages/plg_mokosuitecross_wordpress/wordpress.xml @@ -1,7 +1,7 @@ MokoSuiteCross - WordPress - 01.08.10 + 01.08.11 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 8516f8b..c6e195e 100644 --- a/source/packages/plg_mokosuitecross_youtube/youtube.xml +++ b/source/packages/plg_mokosuitecross_youtube/youtube.xml @@ -1,7 +1,7 @@ MokoSuiteCross - Youtube - 01.08.10 + 01.08.11 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 8157b1e..5ca20f4 100644 --- a/source/packages/plg_system_mokosuitecross/mokosuitecross.xml +++ b/source/packages/plg_system_mokosuitecross/mokosuitecross.xml @@ -1,7 +1,7 @@ System - MokoSuiteCross - 01.08.10 + 01.08.11 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 0b4f4ed..ecae48a 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.08.10 + 01.08.11 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 abb514f..cb414f8 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.08.10 + 01.08.11 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 1189278..abd791a 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.08.10 + 01.08.11 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 44e6bae..5f6ef1e 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.08.10 + 01.08.11 2026-05-28 Moko Consulting hello@mokoconsulting.tech diff --git a/source/pkg_mokosuitecross.xml b/source/pkg_mokosuitecross.xml index 80f47a3..bf3d576 100644 --- a/source/pkg_mokosuitecross.xml +++ b/source/pkg_mokosuitecross.xml @@ -2,7 +2,7 @@ MokoSuiteCross mokosuitecross - 01.08.10 + 01.08.11 2026-05-28 Moko Consulting hello@mokoconsulting.tech -- 2.52.0