diff --git a/.ddev/config.yaml b/.ddev/config.yaml
index 8347238cc..f0b5e171f 100644
--- a/.ddev/config.yaml
+++ b/.ddev/config.yaml
@@ -40,7 +40,7 @@ web_environment:
- PANTHER_CHROME_ARGUMENTS='--disable-dev-shm-usage --ignore-certificate-errors'
- PANTHER_NO_SANDBOX=1
- PANTHER_NO_HEADLESS=0
-nodejs_version: "16"
+nodejs_version: "18"
webimage_extra_packages:
- pngquant
- jpegoptim
diff --git a/.github/workflows/main-ci.yaml b/.github/workflows/main-ci.yaml
index 84033692c..229b90477 100644
--- a/.github/workflows/main-ci.yaml
+++ b/.github/workflows/main-ci.yaml
@@ -43,6 +43,9 @@ jobs:
strategy:
matrix:
include:
+ - ibexa_version: 4.6.7
+ php: 8.1
+ node: 18.x
- ibexa_version: 4.5.*
php: 8.1
node: 14.x
diff --git a/bin/ci-should b/bin/ci-should
index e665670dd..0647e9af5 100755
--- a/bin/ci-should
+++ b/bin/ci-should
@@ -43,7 +43,7 @@ $requiredIbexaVersion = $config['required_ibexa_version'] ?? null;
if ( $requiredIbexaVersion && $ibexaVersion )
{
if(!Semver::satisfies( $ibexaVersion, $requiredIbexaVersion )) {
- exit( 0 );
+ exit( 1 );
}
}
exit( ( $config[$action] ?? false ) === true ? 0 : 1 );
diff --git a/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.en.yml b/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
new file mode 100644
index 000000000..8bd1e7752
--- /dev/null
+++ b/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
@@ -0,0 +1 @@
+menu_manager: Menu manager
\ No newline at end of file
diff --git a/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml b/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
new file mode 100644
index 000000000..0f5f4b9d4
--- /dev/null
+++ b/components/MenuManagerBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
@@ -0,0 +1 @@
+menu_manager: Gestion des menus
\ No newline at end of file
diff --git a/components/SEOBundle/bundle/Controller/Admin/RedirectController.php b/components/SEOBundle/bundle/Controller/Admin/RedirectController.php
index 65ab76e49..adcf08e01 100644
--- a/components/SEOBundle/bundle/Controller/Admin/RedirectController.php
+++ b/components/SEOBundle/bundle/Controller/Admin/RedirectController.php
@@ -80,7 +80,7 @@ public function listAction(
$urlExists = $urlWildcardService->translate($destination);
$errors[] = $translator->trans('nova.redirect.create.exists', ['url' => $destination], 'redirect');
} catch (Exception $e) {
- $e->getMessage();
+ $errors[] = $e->getMessage();
}
if (('' !== $source || '' !== $destination) && ($source !== $destination) && (null === $urlExists)) {
@@ -109,7 +109,7 @@ public function listAction(
}
$page = $request->query->get('page') ?? 1;
- $pagerfanta = new Pagerfanta(new ArrayAdapter($urlWildcardService->loadAll()));
+ $pagerfanta = new Pagerfanta(new ArrayAdapter((array) $urlWildcardService->loadAll()));
$pagerfanta->setMaxPerPage(self::URL_LIMIT);
$pagerfanta->setCurrentPage(min($page, $pagerfanta->getNbPages()));
diff --git a/components/SEOBundle/bundle/Controller/SitemapController.php b/components/SEOBundle/bundle/Controller/SitemapController.php
index a27581e85..c71857c87 100644
--- a/components/SEOBundle/bundle/Controller/SitemapController.php
+++ b/components/SEOBundle/bundle/Controller/SitemapController.php
@@ -16,16 +16,19 @@
use DOMDocument;
use DOMElement;
use Ibexa\Bundle\Core\Controller;
+use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
-use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit;
use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult;
use Ibexa\Contracts\Core\Variation\VariationHandler;
use Ibexa\Core\Helper\FieldHelper;
use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter;
+use Novactive\Bundle\eZSEOBundle\Core\Helper\SiteMapHelper;
use Novactive\Bundle\eZSEOBundle\Core\Sitemap\QueryFactory;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RouterInterface;
+use Throwable;
class SitemapController extends Controller
{
@@ -35,6 +38,15 @@ class SitemapController extends Controller
/** @var VariationHandler */
protected $imageVariationService;
+ /** @var RouterInterface */
+ protected $router;
+
+ /** @var SiteMapHelper */
+ protected $siteMapHelper;
+
+ /** @var Repository */
+ private $repository;
+
/**
* How many in a Sitemap.
*
@@ -42,10 +54,18 @@ class SitemapController extends Controller
*/
public const PACKET_MAX = 1000;
- public function __construct(FieldHelper $fieldHelper, VariationHandler $imageVariationService)
- {
+ public function __construct(
+ FieldHelper $fieldHelper,
+ VariationHandler $imageVariationService,
+ RouterInterface $router,
+ SiteMapHelper $siteMapHelper,
+ Repository $repository
+ ) {
$this->fieldHelper = $fieldHelper;
$this->imageVariationService = $imageVariationService;
+ $this->router = $router;
+ $this->siteMapHelper = $siteMapHelper;
+ $this->repository = $repository;
}
/**
@@ -53,33 +73,41 @@ public function __construct(FieldHelper $fieldHelper, VariationHandler $imageVar
*/
public function indexAction(QueryFactory $queryFactory): Response
{
- $searchService = $this->getRepository()->getSearchService();
- $query = $queryFactory();
- $query->limit = 0;
- $resultCount = $searchService->findLocations($query)->totalCount;
-
+ $isMultisiteAccess = $this->getConfigResolver()
+ ->getParameter('multi_siteaccess_sitemap', 'nova_ezseo') ?? false;
// Dom Doc
$sitemap = new DOMDocument('1.0', 'UTF-8');
$sitemap->formatOutput = true;
-
- // create an index if we are greater than th PACKET_MAX
- if ($resultCount > static::PACKET_MAX) {
+ // Create an index for multi site
+ if ($isMultisiteAccess) {
$root = $sitemap->createElement('sitemapindex');
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$sitemap->appendChild($root);
- $this->fillSitemapIndex($sitemap, $resultCount, $root);
+ $this->fillSitemapMultiSiteIndex($sitemap, $root, $queryFactory);
} else {
- // if we are less or equal than the PACKET_SIZE, redo the search with no limit and list directly the urlmap
- $query->limit = $resultCount;
- $results = $searchService->findLocations($query);
- $root = $sitemap->createElement('urlset');
- $root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
- $root->setAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
- $this->fillSitemap($sitemap, $root, $results);
- $sitemap->appendChild($root);
- }
+ $searchService = $this->getRepository()->getSearchService();
+ $query = $queryFactory();
+ $query->limit = 0;
+ $resultCount = $searchService->findLocations($query)->totalCount;
+ // create an index if we are greater than th PACKET_MAX
+ if ($resultCount > static::PACKET_MAX) {
+ $root = $sitemap->createElement('sitemapindex');
+ $root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
+ $sitemap->appendChild($root);
+ $this->fillSitemapIndex($sitemap, $resultCount, $root);
+ } else {
+ // if we are less or equal than the PACKET_SIZE, redo the search with no limit and list directly the urlmap
+ $query->limit = $resultCount;
+ $results = $searchService->findLocations($query);
+ $root = $sitemap->createElement('urlset');
+ $root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
+ $root->setAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
+ $this->fillSitemap($sitemap, $root, $results);
+ $sitemap->appendChild($root);
+ }
+ }
$response = new Response($sitemap->saveXML(), 200, ['Content-type' => 'text/xml']);
$response->setSharedMaxAge(86400);
@@ -93,19 +121,27 @@ public function indexAction(QueryFactory $queryFactory): Response
*/
public function pageAction(QueryFactory $queryFactory, int $page = 1): Response
{
+ $isMultisiteAccess = $this->getConfigResolver()
+ ->getParameter('multi_siteaccess_sitemap', 'nova_ezseo') ?? false;
+
$sitemap = new DOMDocument('1.0', 'UTF-8');
$root = $sitemap->createElement('urlset');
$sitemap->formatOutput = true;
$root->setAttribute('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9');
$root->setAttribute('xmlns:image', 'http://www.google.com/schemas/sitemap-image/1.1');
- $sitemap->appendChild($root);
- $query = $queryFactory();
- $query->limit = static::PACKET_MAX;
- $query->offset = static::PACKET_MAX * ($page - 1);
- $searchService = $this->getRepository()->getSearchService();
- $results = $searchService->findLocations($query);
- $this->fillSitemap($sitemap, $root, $results);
+ // Create an index for multi site
+ if ($isMultisiteAccess) {
+ $this->multisiteAccessPage($sitemap, $root, $queryFactory, $page);
+ } else {
+ $sitemap->appendChild($root);
+ $query = $queryFactory();
+ $query->limit = static::PACKET_MAX;
+ $query->offset = static::PACKET_MAX * ($page - 1);
+ $searchService = $this->getRepository()->getSearchService();
+ $results = $searchService->findLocations($query);
+ $this->fillSitemap($sitemap, $root, $results);
+ }
$response = new Response($sitemap->saveXML(), 200, ['Content-type' => 'text/xml']);
$response->setSharedMaxAge(86400);
@@ -113,6 +149,41 @@ public function pageAction(QueryFactory $queryFactory, int $page = 1): Response
return $response;
}
+ public function multisiteAccessPage(
+ DOMDocument $sitemap,
+ DOMElement $root,
+ QueryFactory $queryFactory,
+ int $page = 1
+ ): void {
+ $schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9" .
+ " http://www.w3.org/1999/xhtml http://www.w3.org/2002/08/xhtml/xhtml1-strict.xsd".
+ " http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1".
+ " http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" .
+ " http://www.google.com/schemas/sitemap-video/1.1";
+ $root->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
+ $root->setAttribute('xmlns:xhtml', 'http://www.w3.org/1999/xhtml');
+ $root->setAttribute('xmlns:video', 'http://www.google.com/schemas/sitemap-video/1.1');
+ $root->setAttribute('xsi:schemaLocation', $schemaLocation);
+ $sitemap->appendChild($root);
+ //Get The site Access of selected Host and local
+ $currentSiteAccess = $this->siteMapHelper->getCurrentSiteAccess();
+ if ($currentSiteAccess) {
+ $rootLocationId = $this->siteMapHelper->getCurrentSiteAccessRootLocationId();
+ $mainLanguage = $this->siteMapHelper->getCurrentSiteAccessMainLanguage();
+ $query = $queryFactory(
+ $rootLocationId,
+ [$mainLanguage],
+ false
+ );
+ $query->limit = static::PACKET_MAX;
+ $query->offset = static::PACKET_MAX * ($page - 1);
+ $searchService = $this->getRepository()->getSearchService();
+
+ $results = $searchService->findLocations($query);
+ $this->fillMultiLanguagesSitemap($sitemap, $root, $results);
+ }
+ }
+
/**
* Fill a sitemap.
*/
@@ -120,7 +191,6 @@ protected function fillSitemap(DOMDocument $sitemap, DOMElement $root, SearchRes
{
foreach ($results->searchHits as $searchHit) {
/**
- * @var SearchHit
* @var Location $location
*/
$location = $searchHit->valueObject;
@@ -131,9 +201,7 @@ protected function fillSitemap(DOMDocument $sitemap, DOMElement $root, SearchRes
UrlGeneratorInterface::ABSOLUTE_URL
);
} catch (\Exception $exception) {
- if ($this->has('logger')) {
- $this->get('logger')->error('NovaeZSEO: '.$exception->getMessage());
- }
+ $this->siteMapHelper->logException($exception);
continue;
}
@@ -146,36 +214,8 @@ protected function fillSitemap(DOMDocument $sitemap, DOMElement $root, SearchRes
$lastmod = $sitemap->createElement('lastmod', $modified);
$urlElt = $sitemap->createElement('url');
- // Inject the image tags if config is enabl
-
- $displayImage = $this->getConfigResolver()->getParameter('display_images_in_sitemap', 'nova_ezseo');
- if (true === $displayImage) {
- $content = $this->getRepository()->getContentService()->loadContentByContentInfo(
- $location->contentInfo
- );
- foreach ($content->getFields() as $field) {
- $fieldTypeIdentifier = $content->getContentType()->getFieldDefinition(
- $field->fieldDefIdentifier
- )->fieldTypeIdentifier;
-
- if ('ezimage' !== $fieldTypeIdentifier) {
- continue;
- }
-
- if ($this->fieldHelper->isFieldEmpty($content, $field->fieldDefIdentifier)) {
- continue;
- }
- $variation = $this->imageVariationService->getVariation(
- $field,
- $content->getVersionInfo(),
- 'original'
- );
- $imageContainer = $sitemap->createElement('image:image');
- $imageLoc = $sitemap->createElement('image:loc', $variation->uri);
- $imageContainer->appendChild($imageLoc);
- $urlElt->appendChild($imageContainer);
- }
- }
+ // Inject the image tags if config is enabled
+ $this->injectImageTag($location, $sitemap, $urlElt);
$urlElt->appendChild($loc);
$urlElt->appendChild($lastmod);
@@ -199,9 +239,7 @@ protected function fillSitemapIndex(DOMDocument $sitemap, int $numberOfResults,
UrlGeneratorInterface::ABSOLUTE_URL
);
} catch (\Exception $exception) {
- if ($this->has('logger')) {
- $this->get('logger')->error('NovaeZSEO: '.$exception->getMessage());
- }
+ $this->siteMapHelper->logException($exception);
continue;
}
@@ -214,4 +252,161 @@ protected function fillSitemapIndex(DOMDocument $sitemap, int $numberOfResults,
$root->appendChild($sitemapElt);
}
}
+ protected function fillSitemapMultiSiteIndex(
+ DOMDocument $sitemap,
+ DOMElement $root,
+ QueryFactory $queryFactory
+ ): void {
+ $siteMapHelper = $this->siteMapHelper;
+ $this->repository->sudo(static function (Repository $repository) use (
+ $sitemap,
+ $root,
+ $queryFactory,
+ $siteMapHelper
+ ) {
+ $siteAccesses = $siteMapHelper->getSiteAccessesLocationIdLanguages();
+ foreach ($siteAccesses as $siteAccess => $rootLocationLanguages) {
+ $rootLocationId = $rootLocationLanguages['rootLocationId'];
+ $query = $queryFactory(
+ $rootLocationId,
+ [$rootLocationLanguages['mainLanguage']],
+ false
+ );
+ $query->limit = 0;
+ $numberOfResults = $repository->getSearchService()->findLocations($query)->totalCount;
+ $numberOfPage = (int) ceil($numberOfResults / static::PACKET_MAX);
+ for ($sitemapNumber = 1; $sitemapNumber <= $numberOfPage; ++$sitemapNumber) {
+ $sitemapElt = $sitemap->createElement('sitemap');
+ $locUrl = $siteMapHelper->generateRouteUrl(
+ '_novaseo_sitemap_page',
+ $siteAccess,
+ ['page' => $sitemapNumber]
+ );
+ $loc = $sitemap->createElement('loc', $locUrl);
+ $date = new DateTime();
+ $modificationDate = $date->format('c');
+ $mod = $sitemap->createElement('lastmod', $modificationDate);
+ $sitemapElt->appendChild($loc);
+ $sitemapElt->appendChild($mod);
+ $root->appendChild($sitemapElt);
+ }
+ }
+ });
+ }
+
+ /**
+ * Fill a sitemap.
+ */
+ protected function fillMultiLanguagesSitemap(
+ DOMDocument $sitemap,
+ DOMElement $root,
+ SearchResult $results
+ ): void {
+ $currentSiteAccess = $this->siteMapHelper->getCurrentSiteAccess();
+ foreach ($results->searchHits as $searchHit) {
+ /**
+ * @var Location $location
+ */
+ $location = $searchHit->valueObject;
+
+ $mainLanguageUrl = $this->siteMapHelper->generateLocationUrl($location->id, $currentSiteAccess);
+ if (null === $mainLanguageUrl || 0 != strpos($mainLanguageUrl, 'view/content/')) {
+ continue;
+ }
+
+ $modified = $location->contentInfo->modificationDate->format('c');
+ $loc = $sitemap->createElement('loc', $mainLanguageUrl);
+ $lastmod = $sitemap->createElement('lastmod', $modified);
+ $urlElt = $sitemap->createElement('url');
+
+ $urlElt->appendChild($loc);
+ // Inject the image tags if config is enabled
+ $this->injectImageTag($location, $sitemap, $urlElt);
+ // Inject the alternate lang tags if config is enabled
+ $this->injectAlternateLangTag($location, $sitemap, $urlElt);
+
+ $urlElt->appendChild($lastmod);
+ $root->appendChild($urlElt);
+ }
+ }
+
+ public function injectAlternateLangTag($location, DOMDocument $sitemap, DOMElement $root): void
+ {
+ $isMultiLanguages = $this->getConfigResolver()->getParameter('multi_languages_sitemap', 'nova_ezseo');
+ if ($isMultiLanguages) {
+ try {
+ $siteAccesses = $this->getConfigResolver()->getParameter('translation_siteaccesses');
+ $languagesCodes = [];
+ $contentInfo = $location->contentInfo;
+ $contentLanguages = $this->repository->getContentService()
+ ->loadVersionInfo($contentInfo)->getLanguages();
+ foreach ($contentLanguages as $language) {
+ $languagesCodes[] = $language->languageCode;
+ }
+ foreach ($siteAccesses as $siteAccess) {
+ $siteAccessMainLanguage = $this->siteMapHelper->getSiteAccessMainLanguage($siteAccess);
+ if (!in_array($siteAccessMainLanguage, $languagesCodes)) {
+ continue;
+ }
+
+ $url = $this->siteMapHelper->generateLocationUrl($location->id, $siteAccess);
+ $hreflang = $this->siteMapHelper->getHrefLang($siteAccessMainLanguage);
+ if (null === $url || 0 != strpos($url, 'view/content/')) {
+ continue;
+ }
+
+ $xhtml = $sitemap->createElement('xhtml:link');
+ $xhtml->setAttribute('rel', 'alternate');
+ $xhtml->setAttribute('hreflang', $hreflang);
+ $xhtml->setAttribute('href', $url);
+ $root->appendChild($xhtml);
+ }
+ } catch (Throwable $e) {
+ $this->siteMapHelper->logException($e);
+ }
+ }
+ }
+
+ public function injectImageTag($location, DOMDocument $sitemap, DOMElement $root): void
+ {
+ $displayImage = $this->getConfigResolver()->getParameter('display_images_in_sitemap', 'nova_ezseo');
+
+ if (true === $displayImage) {
+ try {
+ $content = $this->getRepository()->getContentService()->loadContentByContentInfo(
+ $location->contentInfo
+ );
+ } catch (Throwable $exception) {
+ return;
+ }
+ foreach ($content->getFields() as $field) {
+ $fieldTypeIdentifier = $content->getContentType()->getFieldDefinition(
+ $field->fieldDefIdentifier
+ )->fieldTypeIdentifier;
+
+ if ('ezimage' !== $fieldTypeIdentifier && 'ezimageasset' !== $fieldTypeIdentifier) {
+ continue;
+ }
+
+ if ($this->fieldHelper->isFieldEmpty($content, $field->fieldDefIdentifier)) {
+ continue;
+ }
+ try {
+ $variation = $this->imageVariationService->getVariation(
+ $field,
+ $content->getVersionInfo(),
+ 'original'
+ );
+
+ $imageContainer = $sitemap->createElement('image:image');
+ $imageLoc = $sitemap->createElement('image:loc', $variation->uri);
+ $imageContainer->appendChild($imageLoc);
+ $root->appendChild($imageContainer);
+ } catch (Throwable $exception) {
+ $this->siteMapHelper->logException($exception);
+ continue;
+ }
+ }
+ }
+ }
}
diff --git a/components/SEOBundle/bundle/Core/FieldType/Metas/FormMapper.php b/components/SEOBundle/bundle/Core/FieldType/Metas/FormMapper.php
index a29420dfb..090e4af67 100644
--- a/components/SEOBundle/bundle/Core/FieldType/Metas/FormMapper.php
+++ b/components/SEOBundle/bundle/Core/FieldType/Metas/FormMapper.php
@@ -89,7 +89,7 @@ public function mapFieldValueForm(FormInterface $fieldForm, FieldData $data)
$metasData = $data->value->metas;
foreach ($metasConfig as $key => $meta) {
$content = isset($metasData[$key]) ? $metasData[$key]->getContent() : null;
- $fieldType = $meta['type'];
+ $fieldType = $meta['type'] ?? 'text';
if (isset($metasData[$key]) && '' != $metasData[$key]->getFieldType()) {
$fieldType = $metasData[$key]->getFieldType();
}
diff --git a/components/SEOBundle/bundle/Core/FieldType/Metas/Type.php b/components/SEOBundle/bundle/Core/FieldType/Metas/Type.php
index ba334ff69..c0d938d77 100644
--- a/components/SEOBundle/bundle/Core/FieldType/Metas/Type.php
+++ b/components/SEOBundle/bundle/Core/FieldType/Metas/Type.php
@@ -144,7 +144,7 @@ public function getName(SPIValue $value, FieldDefinition $fieldDefinition, strin
/**
* Returns information for FieldValue->$sortKey relevant to the field type.
*/
- protected function getSortInfo(CoreValue $value): bool
+ protected function getSortInfo(SPIValue $value): bool
{
return false;
}
diff --git a/components/SEOBundle/bundle/Core/Helper/SiteMapHelper.php b/components/SEOBundle/bundle/Core/Helper/SiteMapHelper.php
new file mode 100644
index 000000000..89443f0d8
--- /dev/null
+++ b/components/SEOBundle/bundle/Core/Helper/SiteMapHelper.php
@@ -0,0 +1,164 @@
+configResolver = $configResolver;
+ $this->siteAccessService = $siteAccessService;
+ $this->routeReferenceGenerator = $routeReferenceGenerator;
+ $this->router = $router;
+ $this->localeConverter = $localeConverter;
+ $this->logger = $logger ?? new NullLogger();
+ }
+
+ public function generateLocationUrl(
+ int $locationId,
+ string $siteAccess = null
+ ): ?string {
+ try {
+ $routeParams['locationId'] = $locationId;
+ $routeReference = $this->routeReferenceGenerator->generate(
+ UrlAliasRouter::URL_ALIAS_ROUTE_NAME,
+ $routeParams
+ );
+ if ($siteAccess) {
+ $routeReference->set('siteaccess', $siteAccess);
+ }
+ $url = $this->router->generate(
+ UrlAliasRouter::URL_ALIAS_ROUTE_NAME,
+ $routeReference->getParams(),
+ UrlGeneratorInterface::ABSOLUTE_URL
+ );
+ } catch (Throwable $exception) {
+ $this->logger->error('NovaeZSEO: ' . $exception->getMessage());
+ $url = null;
+ }
+
+ return $url;
+ }
+ public function generateRouteUrl(
+ string $routeName,
+ string $siteAccess = null,
+ array $parameters = []
+ ): ?string {
+ try {
+ $url = $this->router->generate(
+ $routeName,
+ [...['siteaccess' => $siteAccess], ...$parameters],
+ UrlGeneratorInterface::ABSOLUTE_URL
+ );
+ } catch (Throwable $exception) {
+ $this->logger->error('NovaeZSEO: ' . $exception->getMessage());
+ $url = null;
+ }
+
+ return $url;
+ }
+ public function getSiteAccessesLocationIdLanguages(): array
+ {
+ $rootLocationLanguages = [];
+ $siteAccesses = $this->configResolver->getParameter('translation_siteaccesses');
+ foreach ($siteAccesses as $siteAccess) {
+ $rootLocationLanguages[$siteAccess] = [
+ 'rootLocationId' => $this->getSiteAccessRootLocationId($siteAccess),
+ 'mainLanguage' => $this->getSiteAccessMainLanguage($siteAccess),
+ 'languages' => $this->getSiteAccessLanguages($siteAccess)
+ ];
+ }
+
+ return $rootLocationLanguages;
+ }
+
+ public function getCurrentSiteAccess(): ?string
+ {
+ return $this->siteAccessService->getCurrent()?->name;
+ }
+
+ public function getCurrentSiteAccessRootLocationId(): ?int
+ {
+ return $this->getSiteAccessRootLocationId($this->getCurrentSiteAccess());
+ }
+ public function getCurrentSiteAccessMainLanguage(): ?string
+ {
+ return $this->getSiteAccessMainLanguage($this->getCurrentSiteAccess());
+ }
+
+ public function getSiteAccessRootLocationId(string $siteAccess): ?int
+ {
+ return $this->configResolver->getParameter('content.tree_root.location_id', null, $siteAccess);
+ }
+
+ public function getSiteAccessLanguages(string $siteAccess): array
+ {
+ return (array) $this->configResolver->getParameter('languages', null, $siteAccess);
+ }
+
+ public function getSiteAccessMainLanguage(string $siteAccess): string
+ {
+ $languages = $this->configResolver->getParameter('languages', null, $siteAccess);
+ return array_shift($languages);
+ }
+
+ public function getHrefLang(string $languageCode): string
+ {
+ return str_replace(
+ '_',
+ '-',
+ ($this->localeConverter->convertToPOSIX($languageCode) ?? '')
+ );
+ }
+
+ public function logException(Exception $exception): void
+ {
+ $this->logger?->error('NovaeZSEO: ' . $exception->getMessage());
+ }
+}
diff --git a/components/SEOBundle/bundle/Core/MetaNameSchema.php b/components/SEOBundle/bundle/Core/MetaNameSchema.php
index 5d07844f2..eaf44af1e 100644
--- a/components/SEOBundle/bundle/Core/MetaNameSchema.php
+++ b/components/SEOBundle/bundle/Core/MetaNameSchema.php
@@ -36,6 +36,7 @@
use Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper;
use Ibexa\Core\Repository\Values\Content\VersionInfo;
use Ibexa\FieldTypeRichText\FieldType\RichText\Value as RichTextValue;
+use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class MetaNameSchema extends NameSchemaService
{
@@ -77,20 +78,22 @@ class MetaNameSchema extends NameSchemaService
public function __construct(
ContentTypeHandler $contentTypeHandler,
FieldTypeRegistry $fieldTypeRegistry,
+ EventDispatcherInterface $eventDispatcher,
ContentLanguageHandler $languageHandler,
RepositoryInterface $repository,
TranslationHelper $translationHelper,
ConfigResolverInterface $configurationResolver,
array $settings = []
) {
+ $this->fieldTypeRegistry = $fieldTypeRegistry;
$settings['limit'] = $this->fieldContentMaxLength;
$handler = new ContentTypeDomainMapper(
$contentTypeHandler,
$languageHandler,
- $this->fieldTypeRegistry
+ $fieldTypeRegistry
);
- parent::__construct($contentTypeHandler, $handler, $fieldTypeRegistry, $settings);
+ parent::__construct($contentTypeHandler, $handler, $fieldTypeRegistry, $eventDispatcher, $settings);
$this->repository = $repository;
$this->translationHelper = $translationHelper;
@@ -113,7 +116,7 @@ public function resolveMeta(Meta $meta, Content $content, ContentType $contentTy
{
$languages = $this->configurationResolver->getParameter('languages');
- $resolveMultilingue = $this->resolve(
+ $resolveMultilingue = $this->resolveNameSchema(
$meta->getContent(),
$content->getContentType(),
$content->fields,
@@ -329,4 +332,15 @@ protected function handleImageAssetValue(ImageAssetValue $value, $fieldDefinitio
return '';
}
+
+ /**
+ * Override native function as this prevent usage of `()` inside metas in Ibexa 4.6
+ * {@inheritDoc}
+ */
+ protected function filterNameSchema(string $nameSchema): array
+ {
+ $groupLookupTable = [];
+
+ return [$nameSchema, $groupLookupTable];
+ }
}
diff --git a/components/SEOBundle/bundle/Core/Sitemap/QueryFactory.php b/components/SEOBundle/bundle/Core/Sitemap/QueryFactory.php
index b2bc4e338..d0d7405ca 100644
--- a/components/SEOBundle/bundle/Core/Sitemap/QueryFactory.php
+++ b/components/SEOBundle/bundle/Core/Sitemap/QueryFactory.php
@@ -6,6 +6,7 @@
use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException;
+use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException;
use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery as Query;
@@ -52,8 +53,11 @@ private function getRootLocation(): Location
);
}
- public function __invoke(): Query
- {
+ public function __invoke(
+ int $rootLocationId = null,
+ array $languages = [],
+ bool $matchAlwaysAvailable = true
+ ): Query {
$query = new Query();
// always here, we want visible Contents
@@ -61,7 +65,16 @@ public function __invoke(): Query
// do we want to limit per Root Location, but default we don't
$limitToRootLocation = $this->configResolver->getParameter('limit_to_rootlocation', 'nova_ezseo');
- if (true === $limitToRootLocation) {
+ if ((int) $rootLocationId) {
+ try {
+ $rootLocation = $this->repository->getLocationService()->loadLocation($rootLocationId);
+ } catch (NotFoundException|UnauthorizedException $e) {
+ $rootLocation = null;
+ }
+ if ($rootLocation) {
+ $criterions[] = new Criterion\Subtree($rootLocation->pathString);
+ }
+ } elseif (true === $limitToRootLocation) {
$criterions[] = new Criterion\Subtree($this->getRootLocation()->pathString);
}
@@ -88,7 +101,8 @@ public function __invoke(): Query
)
);
- $criterions[] = new Criterion\LanguageCode($this->configResolver->getParameter('languages'), true);
+ $languages = empty($languages) ? $this->configResolver->getParameter('languages') : $languages;
+ $criterions[] = new Criterion\LanguageCode($languages, $matchAlwaysAvailable);
$query->query = new Criterion\LogicalAnd($criterions);
$query->sortClauses = [new SortClause\DatePublished(Query::SORT_DESC)];
diff --git a/components/SEOBundle/bundle/Core/UrlWildcardRouter.php b/components/SEOBundle/bundle/Core/UrlWildcardRouter.php
index cd1ab4668..491dfd240 100644
--- a/components/SEOBundle/bundle/Core/UrlWildcardRouter.php
+++ b/components/SEOBundle/bundle/Core/UrlWildcardRouter.php
@@ -33,7 +33,7 @@ public function matchRequest(Request $request): array
{
try {
// Manage full url : http://host.com/uri
- $requestedPath = $request->attributes->get('semanticPathinfo', $request->getPathInfo());
+ $requestedPath = $request->getPathInfo();
$requestUriFull = $request->getSchemeAndHttpHost().$requestedPath;
$urlWildcard = $this->wildcardService->translate($requestUriFull);
} catch (Exception $e) {
@@ -51,6 +51,7 @@ public function matchRequest(Request $request): array
if (0 === strpos($urlWildcard->uri, 'http://') || 'https://' === substr($urlWildcard->uri, 0, 8)) {
$params += ['semanticPathinfo' => trim($urlWildcard->uri, '/')];
+ $params += ['prependSiteaccessOnRedirect' => false];
} else {
$params += ['semanticPathinfo' => '/'.trim($urlWildcard->uri, '/')];
}
diff --git a/components/SEOBundle/bundle/DependencyInjection/Configuration.php b/components/SEOBundle/bundle/DependencyInjection/Configuration.php
index 7ff4c9cc5..e87da46ea 100644
--- a/components/SEOBundle/bundle/DependencyInjection/Configuration.php
+++ b/components/SEOBundle/bundle/DependencyInjection/Configuration.php
@@ -28,8 +28,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('google_gatracker')->defaultValue('~')->end()
->scalarNode('google_anonymizeIp')->defaultValue('~')->end()
->scalarNode('bing_verification')->defaultValue('~')->end()
- ->booleanNode('limit_to_rootlocation')->defaultValue('~')->end()
- ->booleanNode('display_images_in_sitemap')->defaultValue('~')->end()
+ ->booleanNode('limit_to_rootlocation')->defaultFalse()->end()
+ ->booleanNode('multi_siteaccess_sitemap')->defaultFalse()->end()
+ ->booleanNode('multi_languages_sitemap')->defaultFalse()->end()
+ ->booleanNode('display_images_in_sitemap')->defaultFalse()->end()
->scalarNode('fieldtype_metas_identifier')->defaultValue('metas')->end()
->arrayNode('fieldtype_metas')
->isRequired()
diff --git a/components/SEOBundle/bundle/DependencyInjection/NovaeZSEOExtension.php b/components/SEOBundle/bundle/DependencyInjection/NovaeZSEOExtension.php
index a91191aa8..5c1bd66bf 100644
--- a/components/SEOBundle/bundle/DependencyInjection/NovaeZSEOExtension.php
+++ b/components/SEOBundle/bundle/DependencyInjection/NovaeZSEOExtension.php
@@ -37,7 +37,9 @@ public function prepend(ContainerBuilder $container): void
'wildcard_routing.yml' => 'ibexa',
'ez_field_templates.yml' => 'ibexa',
'variations.yml' => 'ibexa',
+ 'ibexa.yaml' => 'ibexa',
'admin_ui/ez_field_templates.yml' => 'ibexa',
+ 'ibexa_locale_conversion.yml' => 'ibexa',
];
foreach ($configs as $fileName => $extensionName) {
@@ -68,6 +70,8 @@ public function load(array $configs, ContainerBuilder $container): void
$processor->mapSetting('bing_verification', $config);
$processor->mapSetting('limit_to_rootlocation', $config);
$processor->mapSetting('display_images_in_sitemap', $config);
+ $processor->mapSetting('multi_siteaccess_sitemap', $config);
+ $processor->mapSetting('multi_languages_sitemap', $config);
$processor->mapSetting('robots', $config);
$processor->mapConfigArray('fieldtype_metas', $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL);
$processor->mapConfigArray('default_metas', $config);
diff --git a/components/SEOBundle/bundle/Form/Type/MetaType.php b/components/SEOBundle/bundle/Form/Type/MetaType.php
index 4766a6df0..6d3e3f428 100644
--- a/components/SEOBundle/bundle/Form/Type/MetaType.php
+++ b/components/SEOBundle/bundle/Form/Type/MetaType.php
@@ -28,7 +28,7 @@
class MetaType extends AbstractType
{
protected SeoMetadataFieldTypeRegistry $metadataFieldTypeRegistry;
- private ConfigResolverInterface $configResolver;
+ protected ConfigResolverInterface $configResolver;
/**
* Constructor.
@@ -63,8 +63,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
$novaEzseo = $this->configResolver->getParameter('fieldtype_metas', 'nova_ezseo');
if (isset($novaEzseo[$builder->getName()])) {
$config = $novaEzseo[$builder->getName()];
- $type = $config['type'];
- $options = array_merge($options, $config['params']);
+ $type = $config['type'] ?? $type;
+ if ('select' === $type) {
+ $options = array_merge($options, $config['params']);
+ }
}
$constraints = $this->getConstraints($config);
diff --git a/components/SEOBundle/bundle/Resources/config/default_settings.yml b/components/SEOBundle/bundle/Resources/config/default_settings.yml
index 9fed94a0a..92dfcd1b6 100644
--- a/components/SEOBundle/bundle/Resources/config/default_settings.yml
+++ b/components/SEOBundle/bundle/Resources/config/default_settings.yml
@@ -11,8 +11,10 @@ parameters:
nova_ezseo.default.google_anonymizeIp: ~
nova_ezseo.default.bing_verification: ~
nova_ezseo.default.custom_fallback_service: ~
- nova_ezseo.default.limit_to_rootlocation: ~
- nova_ezseo.default.display_images_in_sitemap: ~
+ nova_ezseo.default.limit_to_rootlocation: false
+ nova_ezseo.default.display_images_in_sitemap: false
+ nova_ezseo.default.multi_siteaccess_sitemap: false
+ nova_ezseo.default.multi_languages_sitemap: false
nova_ezseo.default.sitemap_excludes:
locations: []
subtrees: []
diff --git a/components/SEOBundle/bundle/Resources/config/ibexa.yaml b/components/SEOBundle/bundle/Resources/config/ibexa.yaml
new file mode 100644
index 000000000..efa81ce57
--- /dev/null
+++ b/components/SEOBundle/bundle/Resources/config/ibexa.yaml
@@ -0,0 +1,7 @@
+orm:
+ entity_mappings:
+ NovaeZSEOBundle:
+ is_bundle: true
+ type: annotation
+ dir: Entity
+ prefix: Novactive\Bundle\eZSEOBundle\Entity
\ No newline at end of file
diff --git a/components/SEOBundle/bundle/Resources/config/ibexa_locale_conversion.yml b/components/SEOBundle/bundle/Resources/config/ibexa_locale_conversion.yml
new file mode 100644
index 000000000..82877472d
--- /dev/null
+++ b/components/SEOBundle/bundle/Resources/config/ibexa_locale_conversion.yml
@@ -0,0 +1,15 @@
+#list from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+locale_conversion:
+ bre-BT: br_BT
+ rum-RO: ro_RO
+ mlt-MT: mt_MT
+ gle-IE: ga_IE
+ est-EE: et_EE
+ slv-SI: sl_SI
+ sla-MK: sl_MK
+ lav-LV: lv_LV
+ lit-LT: lt_LT
+ geo-GE: ka_GE
+ per-IR: fa_IR
+ bul-BG: bg_BG
+ bel-BY: be_BY
diff --git a/components/SEOBundle/bundle/Resources/config/services.yml b/components/SEOBundle/bundle/Resources/config/services.yml
index 5ccfcb341..79fc4caca 100644
--- a/components/SEOBundle/bundle/Resources/config/services.yml
+++ b/components/SEOBundle/bundle/Resources/config/services.yml
@@ -54,9 +54,9 @@ services:
Novactive\Bundle\eZSEOBundle\Core\MetaNameSchema:
lazy: true
arguments:
- $contentTypeHandler: "@Ibexa\\Contracts\\Core\\Persistence\\Content\\Type\\Handler"
- $languageHandler: "@Ibexa\\Core\\Persistence\\Cache\\ContentLanguageHandler"
- $translationHelper: "@Ibexa\\Core\\Helper\\TranslationHelper"
+ $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler'
+ $languageHandler: '@Ibexa\Core\Persistence\Cache\ContentLanguageHandler'
+ $translationHelper: '@Ibexa\Core\Helper\TranslationHelper'
calls:
- [setRichTextConverter, ["@Ibexa\\FieldTypeRichText\\RichText\\Converter\\Html5"]]
# Note: injecting lower layer Variation Handler (AliasGenerator) as a workaround for missing Public API objects context
@@ -76,6 +76,8 @@ services:
$ioService: '@ezseo_importurls.ibexa.core.io_service'
$cacheDirectory: '%kernel.cache_dir%'
+ Novactive\Bundle\eZSEOBundle\Core\Helper\SiteMapHelper:
+
Novactive\Bundle\eZSEOBundle\Core\SiteAccessAwareEntityManagerFactory:
arguments:
$repositoryConfigurationProvider: "@Ibexa\\Bundle\\Core\\ApiLoader\\RepositoryConfigurationProvider"
diff --git a/components/SEOBundle/ci-config.yaml b/components/SEOBundle/ci-config.yaml
index 51d98c43c..0f380b380 100644
--- a/components/SEOBundle/ci-config.yaml
+++ b/components/SEOBundle/ci-config.yaml
@@ -1,2 +1,3 @@
install: true
test: true
+required_ibexa_version: ^4.6
diff --git a/components/SEOBundle/composer.json b/components/SEOBundle/composer.json
index 8fde65e8d..1af78bafd 100644
--- a/components/SEOBundle/composer.json
+++ b/components/SEOBundle/composer.json
@@ -27,10 +27,11 @@
"MIT"
],
"require": {
- "php": "^7.3 || ^8.0",
+ "php": "^7.4 || ^8.0",
"ext-dom": "*",
"ext-pdo": "*",
- "ext-json": "*"
+ "ext-json": "*",
+ "ibexa/core": "^4.6"
},
"autoload": {
"psr-4": {
diff --git a/components/SEOBundle/documentation/USAGE.md b/components/SEOBundle/documentation/USAGE.md
index 06214de91..fa2bcbef3 100644
--- a/components/SEOBundle/documentation/USAGE.md
+++ b/components/SEOBundle/documentation/USAGE.md
@@ -141,6 +141,48 @@ nova_ezseo:
contentTypeIdentifiers: ['footer','something']
```
+
+Set `multi_siteaccess_sitemap` to true to automatically generate the index page sitemap.xml per site access.
+
+```yml
+nova_ezseo:
+ system:
+ default:
+ multi_siteaccess_sitemap: true
+```
+
+```html
+
+
+ https://www.example.com/fr/sitemap-1.xml
+ 2024-05-31T01:04:52+00:00
+
+
+ https://www.example.com/en/sitemap-1.xml
+ 2024-05-31T01:04:52+00:00
+
+
+```
+
+Set `multi_languages_sitemap` to inject the multilingual and multinational site annotations tag
+Notice: this doesn't work with `multi_siteaccess_sitemap: false`.
+```html
+
+ https://www.example.com/pageFr
+
+
+ 2024-05-03T08:48:52+00:00
+
+```
+
+```yml
+nova_ezseo:
+ system:
+ default:
+ multi_siteaccess_sitemap: true
+ multi_languages_sitemap: true
+```
+
Set "display_images_in_sitemap" to true to inject the image tags.
Notice: this doesn't work with `limit_to_rootlocation: true`.
diff --git a/components/SamlBundle/README.md b/components/SamlBundle/README.md
index bbc86170f..7555aebd2 100644
--- a/components/SamlBundle/README.md
+++ b/components/SamlBundle/README.md
@@ -47,6 +47,32 @@ env(SAML_IDENTITY_PROVIDER_EMAIL_ATTRIBUTE): ~
env(SAML_IDENTITY_PROVIDER_LOGIN_ATTRIBUTE): ~
```
+These variables are used to define the following global configuration :
+```
+idp:
+ entityId: '%env(resolve:SAML_IDENTITY_PROVIDER_ENTITYID)%'
+ singleSignOnService:
+ url: '%env(resolve:SAML_IDENTITY_PROVIDER_LOGIN_URL)%'
+ binding: '%env(resolve:SAML_IDENTITY_PROVIDER_LOGIN_BINDING)%'
+ singleLogoutService:
+ url: '%env(resolve:SAML_IDENTITY_PROVIDER_LOGOUT_URL)%'
+ binding: '%env(resolve:SAML_IDENTITY_PROVIDER_LOGOUT_BINDING)%'
+ x509cert: '%env(resolve:SAML_IDENTITY_PROVIDER_X509_CERT)%'
+sp:
+ entityId: '%env(resolve:SAML_SERVICE_PROVIDER_URL)%/saml/metadata'
+ assertionConsumerService:
+ url: '%env(resolve:SAML_SERVICE_PROVIDER_URL)%/saml/acs'
+ binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
+ singleLogoutService:
+ url: '%env(resolve:SAML_SERVICE_PROVIDER_URL)%/saml/logout'
+ binding: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
+ NameIDFormat: '%env(resolve:SAML_SERVICE_PROVIDER_NAMEID_FORMAT)%'
+baseurl: '%env(resolve:SAML_SERVICE_PROVIDER_URL)%/saml'
+debug: '%kernel.debug%'
+```
+
+To change the configuration based on siteaccess, it's possible to defined it under the folowing siteaccess aware parameter : `almaviacx.saml..auth_settings`
+
The following parameters are also available to tweak the behavior
```yaml
# Attribute used to get the email address from
@@ -78,6 +104,4 @@ almaviacx.saml.identity.provider.login.attribute:
# Change the user load method
almaviacx.saml.config.default.user_load_method: !php/const AlmaviaCX\Bundle\IbexaSaml\Security\Saml\SamlUserProvider::LOAD_METHOD_EMAIL
-
-
```
diff --git a/components/SamlBundle/src/bundle/AlmaviaCXIbexaSamlBundle.php b/components/SamlBundle/src/bundle/AlmaviaCXIbexaSamlBundle.php
index a5b2e557f..b7854195d 100644
--- a/components/SamlBundle/src/bundle/AlmaviaCXIbexaSamlBundle.php
+++ b/components/SamlBundle/src/bundle/AlmaviaCXIbexaSamlBundle.php
@@ -4,8 +4,15 @@
namespace AlmaviaCX\Bundle\IbexaSamlBundle;
+use AlmaviaCX\Bundle\IbexaSamlBundle\DependencyInjection\Compiler\LazySaml2Auth;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AlmaviaCXIbexaSamlBundle extends Bundle
{
+ public function build(ContainerBuilder $container)
+ {
+ parent::build($container);
+ $container->addCompilerPass(new LazySaml2Auth());
+ }
}
diff --git a/components/SamlBundle/src/bundle/DependencyInjection/Compiler/LazySaml2Auth.php b/components/SamlBundle/src/bundle/DependencyInjection/Compiler/LazySaml2Auth.php
new file mode 100644
index 000000000..e5bc8ae66
--- /dev/null
+++ b/components/SamlBundle/src/bundle/DependencyInjection/Compiler/LazySaml2Auth.php
@@ -0,0 +1,25 @@
+hasDefinition(Auth::class)) {
+ return;
+ }
+
+ $serviceDefinition = $container->getDefinition(Auth::class);
+ $serviceDefinition->setFactory(new Reference(SamlAuthFactory::class));
+ $serviceDefinition->setLazy(true);
+ }
+}
diff --git a/components/SamlBundle/src/bundle/Resources/config/services.yaml b/components/SamlBundle/src/bundle/Resources/config/services.yaml
index ea2da864a..080e10f08 100644
--- a/components/SamlBundle/src/bundle/Resources/config/services.yaml
+++ b/components/SamlBundle/src/bundle/Resources/config/services.yaml
@@ -1,5 +1,4 @@
services:
-
AlmaviaCX\Bundle\IbexaSaml\Security\Saml\SamlExceptionLogger:
arguments:
- '@monolog.logger.saml'
@@ -31,7 +30,7 @@ services:
$userService: '@ibexa.api.service.user'
$configResolver: '@ibexa.config.resolver'
- # override to make it lazy
- OneLogin\Saml2\Auth:
- arguments: [ '%hslavich_onelogin_saml.settings%' ]
- lazy: true
+ AlmaviaCX\Bundle\IbexaSaml\Security\Saml\SamlAuthFactory:
+ arguments:
+ $configResolver: '@Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface'
+ $defaultSettings: '%hslavich_onelogin_saml.settings%'
diff --git a/components/SamlBundle/src/lib/Security/Saml/SamlAuthFactory.php b/components/SamlBundle/src/lib/Security/Saml/SamlAuthFactory.php
new file mode 100644
index 000000000..eb9ca8d3d
--- /dev/null
+++ b/components/SamlBundle/src/lib/Security/Saml/SamlAuthFactory.php
@@ -0,0 +1,51 @@
+defaultSettings = $defaultSettings;
+ $this->configResolver = $configResolver;
+ }
+
+ public function __invoke(): Auth
+ {
+ $settings = $this->defaultSettings;
+ try {
+ $saSettings = $this->configResolver->getParameter('auth_settings', 'almaviacx.saml');
+ } catch (ParameterNotFoundException $exception) {
+ $saSettings = [];
+ }
+
+ return new Auth($this->mergeSettings($settings, $saSettings));
+ }
+
+ protected function mergeSettings(array $defaultSettings, array $settings): array
+ {
+ foreach ($defaultSettings as $key => $setting) {
+ if (!isset($settings[$key])) {
+ continue;
+ }
+ if (is_array($setting)) {
+ $defaultSettings[$key] = $this->mergeSettings($defaultSettings[$key], $settings[$key]);
+ } else {
+ $defaultSettings[$key] = $settings[$key];
+ }
+ }
+
+ return $defaultSettings;
+ }
+}
diff --git a/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.en.yml b/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
new file mode 100644
index 000000000..7b03f566d
--- /dev/null
+++ b/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
@@ -0,0 +1,2 @@
+solr_admin: Solr
+solr_admin.resources: Manage Synonyms/Stopwords
\ No newline at end of file
diff --git a/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml b/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
new file mode 100644
index 000000000..93b64e1da
--- /dev/null
+++ b/components/SolrSearchExtraBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
@@ -0,0 +1,2 @@
+solr_admin: Solr
+solr_admin.resources: Gestion des synonymes/mots vides
diff --git a/components/SolrSearchExtraBundle/src/lib/Query/Content/CriterionVisitor/FilterTag.php b/components/SolrSearchExtraBundle/src/lib/Query/Content/CriterionVisitor/FilterTag.php
index aac8442fb..b0ec2bc7d 100644
--- a/components/SolrSearchExtraBundle/src/lib/Query/Content/CriterionVisitor/FilterTag.php
+++ b/components/SolrSearchExtraBundle/src/lib/Query/Content/CriterionVisitor/FilterTag.php
@@ -26,7 +26,8 @@ public function canVisit(Criterion $criterion): bool
*/
public function visit(Criterion $criterion, CriterionVisitor $subVisitor = null): string
{
- $stringQuery = strtr($subVisitor->visit($criterion->criterion), ['(' => '', ')' => '']);
+ $stringQuery = $subVisitor->visit($criterion->criterion);
+ $stringQuery = trim($stringQuery, '()');
return '{!tag='.$criterion->tag.'}('.$stringQuery.')';
}
diff --git a/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.en.yml b/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
new file mode 100644
index 000000000..60326cea0
--- /dev/null
+++ b/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.en.yml
@@ -0,0 +1 @@
+ibexa.translation.ui.label: Translations UI
\ No newline at end of file
diff --git a/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml b/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
new file mode 100644
index 000000000..2e107d998
--- /dev/null
+++ b/components/TranslationUiBundle/src/bundle/Resources/translations/ibexa_menu.fr.yml
@@ -0,0 +1 @@
+ibexa.translation.ui.label: Gestion des traductions
diff --git a/composer.json b/composer.json
index 33bbc869b..a790ed51e 100644
--- a/composer.json
+++ b/composer.json
@@ -63,6 +63,7 @@
"knplabs/knp-menu": "^3.1",
"behat/behat": "^3.8",
"friends-of-behat/mink-extension": "^2.5",
- "ext-pdo": "*"
+ "ext-pdo": "*",
+ "ext-dom": "*"
}
}