custom/plugins/BilobaIntlTranslation/src/Components/TextProcessors/Links.php line 31

Open in your IDE?
  1. <?php
  2. /**
  3. * (c) 2018 - Biloba IT Balleyer & Lohrmann GbR
  4. * All Rights reserved
  5. *
  6. * Contact: Biloba IT <kontakt@biloba-it.de>
  7. */
  8. namespace Biloba\IntlTranslation\Components\TextProcessors;
  9. use Psr\Log\LoggerInterface;
  10. use Biloba\IntlTranslation\Struct\TranslationContext;
  11. use Shopware\Core\System\SystemConfig\SystemConfigService;
  12. use Shopware\Core\Framework\Context;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  14. class Links extends AbstractTextProcessor {
  15. private $container;
  16. /**
  17. * @var SystemConfigService
  18. */
  19. private $systemConfigService;
  20. /**
  21. * @var LoggerInterface
  22. */
  23. private $log;
  24. private $config;
  25. public function __construct($container = null, $systemConfigService = null, LoggerInterface $log) {
  26. $this->container = $container;
  27. $this->systemConfigService = $systemConfigService;
  28. $this->log = $log;
  29. $this->config = $this->systemConfigService->get('BilobaIntlTranslation.config');
  30. }
  31. /**
  32. * Tries to find a appropriate translation for a given url. If no alternative link is found, returns null.
  33. *
  34. * @param $url string The original url
  35. * @param $to string The translation target language in ISO format
  36. * @return string|null
  37. */
  38. protected function getLinkAlternative($url, $to) {
  39. $this->log->debug('Get alternative link for ' . $url . ' ' . $to);
  40. //setup curl
  41. $ch = curl_init();
  42. curl_setopt_array($ch, [
  43. CURLOPT_FOLLOWLOCATION => true,
  44. CURLOPT_RETURNTRANSFER => true,
  45. CURLOPT_URL => $url
  46. ]);
  47. //exec curl & store the response
  48. $response = curl_exec($ch);
  49. $info = curl_getinfo($ch);
  50. $parameter = explode('?', $url);
  51. if(count($parameter) == 2) {
  52. $parameter = '?' . $parameter[1];
  53. }
  54. else {
  55. $parameter = '';
  56. }
  57. //check if the request was successfull
  58. if($info['http_code'] == 200) {
  59. //get all link tags from the result html page
  60. preg_match_all('/<link ([^>]+)>/i', $response, $linkTags);
  61. foreach($linkTags[0] as $index=>$full) {
  62. //get all tag attributes & clean them
  63. preg_match_all('/([a-z\-]+)=("([^"]+)"|\'([^\']+)\')/i', strtolower($linkTags[1][$index]), $matches);
  64. foreach($matches[1] as $j=>$key) {
  65. $attributes[$key] = trim(rtrim(isset($matches[3][$j]) ? $matches[3][$j] : $matches[2][$j]));
  66. }
  67. //check if all required attributes are set for this link
  68. if(isset($attributes['rel'], $attributes['hreflang'], $attributes['href'])) {
  69. // alternative iso check
  70. $aryTo = explode('-', $to);
  71. //check if the current link matches the required lanuage
  72. if($attributes['rel'] == 'alternate' && strpos($attributes['hreflang'], strtolower($to)) === 0) {
  73. $this->log->debug('Found alternative link for ' . $url . ' => ' . $attributes['href']);
  74. return $attributes['href'] . $parameter;
  75. }
  76. elseif(sizeof($aryTo) > 0 && $attributes['rel'] == 'alternate' && strpos($attributes['hreflang'], strtolower($aryTo[0])) === 0) {
  77. $this->log->debug('Found alternative link for ' . $url . ' => ' . $attributes['href']);
  78. return $attributes['href'] . $parameter;
  79. }
  80. }
  81. }
  82. } else {
  83. $this->log->debug('Request to ' . $url . ' failed!', $info);
  84. }
  85. return null;
  86. }
  87. /**
  88. * Translates all link hrefs. Checks the <link rel="alternate" ...> tags of the link target. If nothing is set
  89. * the original target is preserved.
  90. *
  91. * @param $text string The text that is translated
  92. * @param $to string The translation target language in ISO format
  93. * @return string
  94. */
  95. protected function translateLinks($text, $to) {
  96. return preg_replace_callback('/href=("([^"]+)"|\'([^\']+)\')/i', function($match) use($to) {
  97. $this->log->debug("Found link " . $match[0]);
  98. //check if we should use double qoutes
  99. $doubleQuotes = false;
  100. if(strpos($match[1], '"') === 0) {
  101. $doubleQuotes = true;
  102. }
  103. //get the url from the match
  104. $url = (isset($match[3]) && $match[3] != "") ? $match[3] : $match[2];
  105. //check if its a relative url
  106. $parsedUrl = parse_url($url);
  107. if(!isset($parsedUrl['host'])) {
  108. $url = $this->convertRelativUrl($url);
  109. }
  110. //fetch alternative url
  111. $alternate = $this->getLinkAlternative($url, $to);
  112. //return the translated href attribute
  113. if($doubleQuotes) {
  114. return 'href="' . ($alternate ? $alternate : $url) . '"';
  115. } else {
  116. return 'href=\'' . ($alternate ? $alternate : $url) . '\'';
  117. }
  118. }, $text);
  119. }
  120. /**
  121. * Converts /this/is/a/url to http://host/this/is/a/url
  122. *
  123. * @param $url string The Url to convert
  124. * @return string
  125. */
  126. protected function convertRelativUrl($url) {
  127. $matchedUrl = null;
  128. $shopId = $this->config['DefaultSalesChannel'];
  129. $salesChannelRepository = $this->container->get('sales_channel.repository');
  130. $criteria = new Criteria([$shopId]);
  131. $criteria->addAssociation('domains');
  132. $salesChannel = $salesChannelRepository->search($criteria, Context::createDefaultContext())->get($shopId);
  133. $httpsPrefix = "https";
  134. foreach($salesChannel->getDomains()->getElements() as $key=>$domain) {
  135. // checking if url starts with https
  136. if(str_starts_with($domain->getUrl(), $httpsPrefix)) {
  137. // match http and https urls
  138. preg_match('/^(https?):\/\/[^\s\/$.?#0-9].[^\s]*$/', $domain->getUrl(), $matches);
  139. // fetch match at index 0, so we can get the full url
  140. $matchedUrl = $matches[0];
  141. }
  142. }
  143. return $matchedUrl . $url;
  144. }
  145. public function postTranslate(TranslationContext $context, string $text) :string {
  146. if($text == null) {
  147. return '';
  148. }
  149. // load plugin config
  150. $config = $this->config['TranslateLinks'];
  151. // only run processor if enabled
  152. if(!$config){
  153. return $text;
  154. }
  155. // only run processor if given value is html
  156. if(!$this->hasTextHtml($text)) {
  157. return $text;
  158. }
  159. return $this->translateLinks($text, $context->getTargetLanguage()->getLocale()->getCode());
  160. }
  161. }