Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] alternate hreflang #97

Open
wants to merge 4 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.buildpath
/.project
/.idea
10 changes: 10 additions & 0 deletions Classes/Generator/AbstractSitemapGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,14 @@ protected function createRenderer() {
* @return void
*/
abstract protected function generateSitemapContent();

/**
* @return array
* @throws \InvalidArgumentException
*/
protected function getAlternateSysLanguageIds() {
/** @var \DmitryDulepov\DdGooglesitemap\Helper\SysLanguageHelper $instance */
$instance = GeneralUtility::makeInstance('DmitryDulepov\\DdGooglesitemap\\Helper\\SysLanguageHelper');
return $instance->getSysLanguages();
}
}
107 changes: 87 additions & 20 deletions Classes/Generator/PagesSitemapGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

namespace DmitryDulepov\DdGooglesitemap\Generator;

use DmitryDulepov\DdGooglesitemap\Renderers\AbstractSitemapRenderer;
use DmitryDulepov\DdGooglesitemap\Renderers\AbstractExtendedSitemapRenderer;
use \TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;

Expand Down Expand Up @@ -55,7 +55,7 @@ class PagesSitemapGenerator extends AbstractSitemapGenerator {
/**
* A sitemap renderer
*
* @var AbstractSitemapRenderer
* @var AbstractExtendedSitemapRenderer
*/
protected $renderer;

Expand All @@ -70,7 +70,7 @@ class PagesSitemapGenerator extends AbstractSitemapGenerator {
protected $hookObjects;

/**
* Initializes the instance of this class. This constructir sets starting
* Initializes the instance of this class. This constructor sets starting
* point for the sitemap to the current page id
*/
public function __construct() {
Expand Down Expand Up @@ -120,7 +120,8 @@ public function __construct() {
/**
* Generates sitemap for pages (<url> entries in the sitemap)
*
* @return void
* @return void
* @throws \InvalidArgumentException
*/
protected function generateSitemapContent() {
// Workaround: we want the sysfolders back into the menu list!
Expand Down Expand Up @@ -154,7 +155,7 @@ protected function generateSitemapContent() {
// Notice: no sorting (for speed)!
$GLOBALS['TSFE']->sys_page->sys_language_uid = $GLOBALS['TSFE']->config['config']['sys_language_uid'];
$morePages = $GLOBALS['TSFE']->sys_page->getMenu($pageInfo['uid'], '*', '', '', false);
$this->removeNonTranslatedPages($morePages);
$morePages = $this->filterNonTranslatedPages($morePages);
$this->pageList = array_merge($this->pageList, array_values($morePages));
unset($morePages);
}
Expand All @@ -178,21 +179,49 @@ protected function getLastMod(array $pageInfo) {
/**
* Exclude pages from given list
*
* @deprecated use filterNonTranslatedPages
*
* @param array $pages
* @return void
*/
protected function removeNonTranslatedPages(array &$pages) {
$language = (int)$GLOBALS['TSFE']->config['config']['sys_language_uid'];
foreach ($pages as $pageUid => $page) {
// Hide page in default language
if ($language === 0 && GeneralUtility::hideIfDefaultLanguage($page['l18n_cfg'])) {
unset($pages[$pageUid]);
$pages = $this->filterNonTranslatedPages($pages);
}

/**
* Get only translated pages
*
* @param array $pages
* @param int|null $languageUid
* @return array
*/
protected function filterNonTranslatedPages(array $pages, $languageUid = null) {
if($languageUid === null) {
$languageUid = (int)$GLOBALS['TSFE']->config['config']['sys_language_uid'];
}

$filterFunction = function ($page) use($languageUid) {
if ($languageUid === 0 && GeneralUtility::hideIfDefaultLanguage($page['l18n_cfg'])) {
return false;
}
elseif ($language !== 0 && !isset($page['_PAGES_OVERLAY']) && GeneralUtility::hideIfNotTranslated($page['l18n_cfg'])) {
// Hide page if no translation is set
unset($pages[$pageUid]);

if ($languageUid !== 0 && !isset($page['_PAGES_OVERLAY']) && GeneralUtility::hideIfNotTranslated($page['l18n_cfg'])) {
return false;
}

return true;
};

// requested language === current language
if ($languageUid === (int)$GLOBALS['TSFE']->config['config']['sys_language_uid']) {
return array_filter($pages, $filterFunction);
}

// other language: load overlay information
$overlayPages = $GLOBALS['TSFE']->sys_page->getPagesOverlay($pages, $languageUid);
$overlayPages = array_filter($overlayPages, $filterFunction);

return array_intersect_key($pages, $overlayPages);
}

/**
Expand All @@ -208,14 +237,18 @@ protected function shouldIncludePageInSitemap(array $pageInfo) {
/**
* Outputs information about single page
*
* @param array $pageInfo Page information (needs 'uid' and 'SYS_LASTCHANGED' columns)
* @return void
* @param array $pageInfo Page information (needs 'uid' and 'SYS_LASTCHANGED' columns)
* @return void
* @throws \InvalidArgumentException
*/
protected function writeSingleUrl(array $pageInfo) {
if ($this->shouldIncludePageInSitemap($pageInfo) && ($url = $this->getPageLink($pageInfo['uid']))) {
echo $this->renderer->renderEntry($url, $pageInfo['title'],
$this->getLastMod($pageInfo),
$this->getChangeFrequency($pageInfo), '', $pageInfo['tx_ddgooglesitemap_priority']);
$this->getChangeFrequency($pageInfo), '', $pageInfo['tx_ddgooglesitemap_priority'],
array(
'hreflangs' => $this->getAlternateLinks($pageInfo)
));

// Post-process current page and possibly append data
// @see http://forge.typo3.org/issues/45637
Expand Down Expand Up @@ -280,18 +313,52 @@ protected function calculateChangeFrequency(array $pageInfo) {
($average <= 14*24*60*60 ? 'weekly' : 'monthly'))));
}

/**
* @param array $pageInfo
* @return array
* @throws \InvalidArgumentException
*/
protected function getAlternateLinks($pageInfo) {
$links = array();
$pageUid = $pageInfo['uid'];
$alternativeLanguages = $this->getAlternateSysLanguageIds();
if (!empty($alternativeLanguages)) {
foreach ($alternativeLanguages as $languageUid => $locale) {
$translatedPage = $this->filterNonTranslatedPages(array($pageInfo), $languageUid);
if(empty($translatedPage)) {
continue;
}

// can generate url and it different to target url
if (($url = $this->getPageLink($pageUid, $languageUid))) {
$links[$locale] = $url;
}
}
}

return $links;
}

/**
* Creates a link to a single page
*
* @param array $pageId Page ID
* @param int $pageId Page ID
* @param int $languageId Language Id
* @return string Full URL of the page including host name (escaped)
*/
protected function getPageLink($pageId) {
protected function getPageLink($pageId, $languageId = null) {
$conf = array(
'parameter' => $pageId,
'returnLast' => 'url',
'forceAbsoluteUrl' => 1
);
$link = htmlspecialchars($this->cObj->typoLink('', $conf));
return GeneralUtility::locationHeaderUrl($link);

if ($languageId !== null) {
$conf['additionalParams'] = '&L=' . $languageId;
// cHash is important for e.g. realUrl
$conf['useCacheHash'] = true;
}

return htmlspecialchars($this->cObj->typoLink('', $conf));
}
}
139 changes: 139 additions & 0 deletions Classes/Helper/SysLanguageHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
/***************************************************************
* Copyright notice
*
* (c) 2008-2014 Dmitry Dulepov <dmitry.dulepov@gmail.com>
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/

namespace DmitryDulepov\DdGooglesitemap\Helper;

use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* Class SysLanguageHelper
*
* @package DmitryDulepov\DdGooglesitemap\Helper
*/
class SysLanguageHelper implements SingletonInterface {

/**
* @var array of sys languages [uid => languageCode]
*/
protected $sysLanguages = null;

/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManager
* @inject
*/
protected $objectManager;

/**
* @return \TYPO3\CMS\Frontend\Page\PageRepository
* @throws \InvalidArgumentException
*/
protected function getPageRepository() {
if(TYPO3_MODE === 'BE') {
\TYPO3\CMS\Frontend\Utility\EidUtility::initTCA();
if (!is_object($GLOBALS['TT'])) {
$GLOBALS['TT'] = new \TYPO3\CMS\Core\TimeTracker\NullTimeTracker;
$GLOBALS['TT']->start();
}

$GLOBALS['TSFE'] = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Controller\\TypoScriptFrontendController', $GLOBALS['TYPO3_CONF_VARS'], GeneralUtility::_GP('id'), '');
$GLOBALS['TSFE']->sys_page = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\Page\\PageRepository');
$GLOBALS['TSFE']->sys_page->init(TRUE);
$GLOBALS['TSFE']->connectToDB();
$GLOBALS['TSFE']->initFEuser();
$GLOBALS['TSFE']->determineId();
$GLOBALS['TSFE']->initTemplate();
$GLOBALS['TSFE']->getConfigArray();
}

return $GLOBALS['TSFE']->sys_page;
}

/**
* @return array
* @throws \InvalidArgumentException
*/
public function getSysLanguages() {
if ($this->sysLanguages === null) {
$this->sysLanguages = array();
$sys_languages = $this->getPageRepository()->getRecordsByField('sys_language', 'hidden', 0);

// empty table
if (!is_array($sys_languages)) {
$sys_languages = array();
}

// default language not in table, so add manually
array_unshift($sys_languages, array('language_isocode' => 'x-default', 'uid' => 0));

foreach ($sys_languages as $language) {
// use iso code as default
$setLocale = $language['language_isocode'];

// check typoscript config
/** @var \TYPO3\CMS\Core\TypoScript\TemplateService $templateService */
$templateService = GeneralUtility::makeInstance(
'TYPO3\\CMS\\Core\\TypoScript\\TemplateService'
);
$templateService->matchAlternative[] = '[globalVar = GP:L = ' . $language['uid'] . ']';
$templateService->init();
if(TYPO3_MODE === 'FE') {
$templateService->start($GLOBALS['TSFE']->rootLine);
}
// apply language condition
$templateService->generateConfig();

// allow custom modifications. We not change TSFE->config array and this could be important.
// Add possibility, that extension could add their modification on setup-array
if (is_array(
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['dd_google_sitemap/sys_language_helper']['alternateSysLanguageIdsPostProc']
)) {
$params = array('templateService' => $templateService);
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['dd_google_sitemap/sys_language_helper']['alternateSysLanguageIdsPostProc'] as $funcRef) {
GeneralUtility::callUserFunction($funcRef, $params, $this);
}
}

if (isset($templateService->setup['config.']['locale_all'])) {
// could contain charset
list($locale_all) = explode('.', $templateService->setup['config.']['locale_all'], 2);

if (\strpos($locale_all, '_') === 2) {
list($lang, $region) = explode('_', $locale_all, 3);
$setLocale = strtolower($lang . '-' . $region);
} else {
$setLocale = $locale_all;
}
} elseif (isset($templateService->setup['config.']['language'])) {
$setLocale = $templateService->setup['config.']['language'];
}

$this->sysLanguages[(int)$language['uid']] = $setLocale;
}

}

return $this->sysLanguages;
}
}
Loading