* @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\Helper; defined('_JEXEC') or die; use Joomla\CMS\Component\ComponentHelper; /** * Shortens URLs via Bitly, Rebrandly, or YOURLS. * * Returns the original URL on any failure so cross-posts are never broken. */ class LinkShortenerHelper { /** * Shorten a URL using the configured provider. * * @param string $url The URL to shorten * * @return string Shortened URL, or the original on failure/disabled */ public static function shorten(string $url): string { $params = ComponentHelper::getParams('com_mokosuitecross'); $provider = $params->get('link_shortener', 'none'); if ($provider === 'none' || empty($url)) { return $url; } $apiKey = $params->get('link_shortener_api_key', ''); switch ($provider) { case 'bitly': return self::shortenWithBitly($url, $apiKey); case 'rebrandly': return self::shortenWithRebrandly($url, $apiKey); case 'yourls': $apiUrl = $params->get('link_shortener_yourls_url', ''); $token = $params->get('link_shortener_yourls_token', ''); return self::shortenWithYourls($url, $apiUrl, $token); default: return $url; } } /** * Shorten via Bitly API v4. */ public static function shortenWithBitly(string $url, string $apiKey): string { if (empty($apiKey)) { return $url; } $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://api-ssl.bitly.com/v4/shorten', CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['long_url' => $url]), CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $apiKey, 'Content-Type: application/json', ], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($response === false || $httpCode < 200 || $httpCode >= 300) { return $url; } $data = json_decode($response, true); return $data['link'] ?? $url; } /** * Shorten via Rebrandly API. */ public static function shortenWithRebrandly(string $url, string $apiKey, string $workspace = ''): string { if (empty($apiKey)) { return $url; } $headers = [ 'apikey: ' . $apiKey, 'Content-Type: application/json', ]; if (!empty($workspace)) { $headers[] = 'workspace: ' . $workspace; } $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://api.rebrandly.com/v1/links', CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['destination' => $url]), CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($response === false || $httpCode < 200 || $httpCode >= 300) { return $url; } $data = json_decode($response, true); $short = $data['shortUrl'] ?? ''; return !empty($short) ? 'https://' . ltrim($short, 'https://') : $url; } /** * Shorten via YOURLS API (self-hosted). */ public static function shortenWithYourls(string $url, string $apiUrl, string $signatureToken): string { if (empty($apiUrl) || empty($signatureToken)) { return $url; } $endpoint = rtrim($apiUrl, '/') . '?' . http_build_query([ 'action' => 'shorturl', 'format' => 'json', 'signature' => $signatureToken, 'url' => $url, ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($response === false || $httpCode < 200 || $httpCode >= 300) { return $url; } $data = json_decode($response, true); return $data['shorturl'] ?? $url; } }