first commit
This commit is contained in:
138
Classes/System/Cache/TwoLevelCache.php
Normal file
138
Classes/System/Cache/TwoLevelCache.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Cache;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2016 Timo Schmidt <timo.schmidt@dkd.de>
|
||||
* 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 3 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.
|
||||
* A copy is found in the textfile GPL.txt and important notices to the license
|
||||
* from the author is found in LICENSE.txt distributed with these scripts.
|
||||
*
|
||||
*
|
||||
* 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Provides a two level cache that uses an in memory cache as the first level cache and
|
||||
* the TYPO3 caching framework cache as the second level cache.
|
||||
*
|
||||
* @author Timo Schmidt <timo.schmidt@dkd.de>
|
||||
*/
|
||||
class TwoLevelCache
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $cacheName = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $firstLevelCache = [];
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
|
||||
*/
|
||||
protected $secondLevelCache = null;
|
||||
|
||||
/**
|
||||
* @param string $cacheName
|
||||
* @param \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $secondaryCacheFrontend
|
||||
*/
|
||||
public function __construct($cacheName, $secondaryCacheFrontend = null)
|
||||
{
|
||||
$this->cacheName = $cacheName;
|
||||
if ($secondaryCacheFrontend == null) {
|
||||
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
|
||||
$this->secondLevelCache = $cacheManager->getCache($cacheName);
|
||||
} else {
|
||||
$this->secondLevelCache = $secondaryCacheFrontend;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value from the first level cache.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @return mixed|null
|
||||
*/
|
||||
protected function getFromFirstLevelCache($cacheId)
|
||||
{
|
||||
if (!empty(self::$firstLevelCache[$this->cacheName][$cacheId])) {
|
||||
return self::$firstLevelCache[$this->cacheName][$cacheId];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to the first level cache.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function setToFirstLevelCache($cacheId, $value)
|
||||
{
|
||||
self::$firstLevelCache[$this->cacheName][$cacheId] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a value from the first level cache if present and
|
||||
* from the second level if not.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($cacheId)
|
||||
{
|
||||
$firstLevelResult = $this->getFromFirstLevelCache($cacheId);
|
||||
if ($firstLevelResult !== null) {
|
||||
return $firstLevelResult;
|
||||
}
|
||||
|
||||
$secondLevelResult = $this->secondLevelCache->get($cacheId);
|
||||
$this->setToFirstLevelCache($cacheId, $secondLevelResult);
|
||||
|
||||
return $secondLevelResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to the first and second level cache.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($cacheId, $value)
|
||||
{
|
||||
$this->setToFirstLevelCache($cacheId, $value);
|
||||
$this->secondLevelCache->set($cacheId, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
self::$firstLevelCache[$this->cacheName] = [];
|
||||
$this->secondLevelCache->flush();
|
||||
}
|
||||
}
|
114
Classes/System/Configuration/ConfigurationManager.php
Normal file
114
Classes/System/Configuration/ConfigurationManager.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Configuration;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Schmidt <timo.schmidt@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Configuration manager old the configuration instance.
|
||||
* Singleton
|
||||
*
|
||||
* @author Timo Schmidt <timo.schmidt@dkd.de>
|
||||
*/
|
||||
class ConfigurationManager implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* TypoScript Configurations
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $typoScriptConfigurations = [];
|
||||
|
||||
/**
|
||||
* Resets the state of the configuration manager.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->typoScriptConfigurations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the TypoScriptConfiguration object from an configuration array, pageId, languageId and TypoScript
|
||||
* path that is used in in the current context.
|
||||
*
|
||||
* @param array $configurationArray
|
||||
* @param int $contextPageId
|
||||
* @param int $contextLanguageId
|
||||
* @param string $contextTypoScriptPath
|
||||
* @return TypoScriptConfiguration
|
||||
*/
|
||||
public function getTypoScriptConfiguration(array $configurationArray = null, $contextPageId = null, $contextLanguageId = 0, $contextTypoScriptPath = '')
|
||||
{
|
||||
if ($configurationArray == null) {
|
||||
if (isset($this->typoScriptConfigurations['default'])) {
|
||||
$configurationArray = $this->typoScriptConfigurations['default'];
|
||||
} else {
|
||||
if (!empty($GLOBALS['TSFE']->tmpl->setup) && is_array($GLOBALS['TSFE']->tmpl->setup)) {
|
||||
$configurationArray = $GLOBALS['TSFE']->tmpl->setup;
|
||||
$this->typoScriptConfigurations['default'] = $configurationArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($configurationArray)) {
|
||||
$configurationArray = [];
|
||||
}
|
||||
|
||||
if (!isset($configurationArray['plugin.']['tx_meilisearch.'])) {
|
||||
$configurationArray['plugin.']['tx_meilisearch.'] = [];
|
||||
}
|
||||
|
||||
if ($contextPageId === null && !empty($GLOBALS['TSFE']->id)) {
|
||||
$contextPageId = $GLOBALS['TSFE']->id;
|
||||
}
|
||||
|
||||
$hash = md5(serialize($configurationArray)) . '-' . $contextPageId . '-' . $contextLanguageId . '-' . $contextTypoScriptPath;
|
||||
if (isset($this->typoScriptConfigurations[$hash])) {
|
||||
return $this->typoScriptConfigurations[$hash];
|
||||
}
|
||||
|
||||
$this->typoScriptConfigurations[$hash] = $this->getTypoScriptConfigurationInstance($configurationArray, $contextPageId);
|
||||
return $this->typoScriptConfigurations[$hash];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to build the TypoScriptConfiguration.
|
||||
*
|
||||
* @param array $configurationArray
|
||||
* @param int|null $contextPageId
|
||||
* @return object
|
||||
*/
|
||||
protected function getTypoScriptConfigurationInstance(array $configurationArray = null, $contextPageId = null)
|
||||
{
|
||||
return GeneralUtility::makeInstance(
|
||||
TypoScriptConfiguration::class,
|
||||
/** @scrutinizer ignore-type */ $configurationArray,
|
||||
/** @scrutinizer ignore-type */ $contextPageId
|
||||
);
|
||||
}
|
||||
}
|
117
Classes/System/Configuration/ConfigurationPageResolver.php
Normal file
117
Classes/System/Configuration/ConfigurationPageResolver.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Configuration;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Schmidt <timo.schmidt@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Cache\TwoLevelCache;
|
||||
use WapplerSystems\Meilisearch\System\Records\SystemTemplate\SystemTemplateRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\RootlineUtility;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
|
||||
/**
|
||||
* This class is responsible to find the closest page id from the rootline where
|
||||
* a typoscript template is stored on.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class ConfigurationPageResolver
|
||||
{
|
||||
|
||||
/**
|
||||
* @var SystemTemplateRepository
|
||||
*/
|
||||
protected $systemTemplateRepository;
|
||||
|
||||
/**
|
||||
* @var TwoLevelCache
|
||||
*/
|
||||
protected $twoLevelCache;
|
||||
|
||||
/**
|
||||
* @var TwoLevelCache
|
||||
*/
|
||||
protected $runtimeCache;
|
||||
|
||||
/**
|
||||
* ConfigurationPageResolver constructor.
|
||||
* @param PageRepository|null $pageRepository
|
||||
* @param TwoLevelCache|null $twoLevelCache
|
||||
* @param SystemTemplateRepository $systemTemplateRepository
|
||||
*/
|
||||
public function __construct(PageRepository $pageRepository = null, TwoLevelCache $twoLevelCache = null, SystemTemplateRepository $systemTemplateRepository = null)
|
||||
{
|
||||
$this->runtimeCache = $twoLevelCache ?? GeneralUtility::makeInstance(TwoLevelCache::class, /** @scrutinizer ignore-type */ 'cache_runtime');
|
||||
$this->systemTemplateRepository = $systemTemplateRepository ?? GeneralUtility::makeInstance(SystemTemplateRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fetches the rootLine and calculates the id of the closest template in the rootLine.
|
||||
* The result is stored in the runtime cache.
|
||||
*
|
||||
* @param integer $startPageId
|
||||
* @return integer
|
||||
*/
|
||||
public function getClosestPageIdWithActiveTemplate($startPageId)
|
||||
{
|
||||
if ($startPageId === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$cacheId = 'ConfigurationPageResolver' . '_' . 'getClosestPageIdWithActiveTemplate' . '_' . $startPageId;
|
||||
$methodResult = $this->runtimeCache->get($cacheId);
|
||||
if (!empty($methodResult)) {
|
||||
return $methodResult;
|
||||
}
|
||||
|
||||
$methodResult = $this->calculateClosestPageIdWithActiveTemplate($startPageId);
|
||||
$this->runtimeCache->set($cacheId, $methodResult);
|
||||
|
||||
return $methodResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method fetches the rootLine and calculates the id of the closest template in the rootLine.
|
||||
*
|
||||
* @param integer $startPageId
|
||||
* @return int
|
||||
*/
|
||||
protected function calculateClosestPageIdWithActiveTemplate($startPageId)
|
||||
{
|
||||
|
||||
$rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $startPageId);
|
||||
try {
|
||||
$rootline = $rootlineUtility->get();
|
||||
} catch (\RuntimeException $e) {
|
||||
return $startPageId;
|
||||
}
|
||||
|
||||
$closestPageIdWithTemplate = $this->systemTemplateRepository->findOneClosestPageIdWithActiveTemplateByRootLine($rootline);
|
||||
if ($closestPageIdWithTemplate === 0) {
|
||||
return $startPageId;
|
||||
}
|
||||
|
||||
return (int)$closestPageIdWithTemplate;
|
||||
}
|
||||
}
|
130
Classes/System/Configuration/ExtensionConfiguration.php
Normal file
130
Classes/System/Configuration/ExtensionConfiguration.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Configuration;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017- Timo Schmidt <timo.schmidt@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This class encapsulates the access to the extension configuration.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class ExtensionConfiguration
|
||||
{
|
||||
/**
|
||||
* Extension Configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $configuration = [];
|
||||
|
||||
/**
|
||||
* ExtensionConfiguration constructor.
|
||||
* @param array $configurationToUse
|
||||
*/
|
||||
public function __construct($configurationToUse = [])
|
||||
{
|
||||
if (empty($configurationToUse)) {
|
||||
$this->configuration = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\ExtensionConfiguration::class)->get('meilisearch');
|
||||
} else {
|
||||
$this->configuration = $configurationToUse;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for useConfigurationFromClosestTemplate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsUseConfigurationFromClosestTemplateEnabled()
|
||||
{
|
||||
return (bool)$this->getConfigurationOrDefaultValue('useConfigurationFromClosestTemplate', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for useConfigurationTrackRecordsOutsideSiteroot
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsUseConfigurationTrackRecordsOutsideSiteroot()
|
||||
{
|
||||
return (bool)$this->getConfigurationOrDefaultValue('useConfigurationTrackRecordsOutsideSiteroot', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for allowSelfSignedCertificates
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsSelfSignedCertificatesEnabled()
|
||||
{
|
||||
return (bool)$this->getConfigurationOrDefaultValue('allowSelfSignedCertificates', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for useConfigurationMonitorTables
|
||||
*
|
||||
* @return array of tableName
|
||||
*/
|
||||
public function getIsUseConfigurationMonitorTables()
|
||||
{
|
||||
$monitorTables = [];
|
||||
$monitorTablesList = $this->getConfigurationOrDefaultValue('useConfigurationMonitorTables', '');
|
||||
|
||||
if (empty($monitorTablesList)) {
|
||||
return $monitorTables;
|
||||
}
|
||||
|
||||
return GeneralUtility::trimExplode(',', $monitorTablesList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for allowLegacySiteMode
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsAllowLegacySiteModeEnabled(): bool
|
||||
{
|
||||
trigger_error('solr:deprecation: Method getIsAllowLegacySiteModeEnabled is deprecated since EXT:meilisearch 11 and will be removed in 12. Since EXT:meilisearch 10 legacy site handling is deprecated and was removed in EXT:meilisearch 11.', E_USER_DEPRECATED);
|
||||
|
||||
//@todo throw exception if set to true and log deprecation
|
||||
$legacyModeIsActive = $this->getConfigurationOrDefaultValue('allowLegacySiteMode', false);
|
||||
if($legacyModeIsActive === true) {
|
||||
throw new \InvalidArgumentException("Legacy mode is not supported anymore, please migrate your system to use sitehandling now!");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getConfigurationOrDefaultValue($key, $defaultValue)
|
||||
{
|
||||
return isset($this->configuration[$key]) ? $this->configuration[$key] : $defaultValue;
|
||||
}
|
||||
}
|
2251
Classes/System/Configuration/TypoScriptConfiguration.php
Normal file
2251
Classes/System/Configuration/TypoScriptConfiguration.php
Normal file
File diff suppressed because it is too large
Load Diff
84
Classes/System/ContentObject/ContentObjectService.php
Normal file
84
Classes/System/ContentObject/ContentObjectService.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\ContentObject;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* StdWrap Service that can be used to apply the ContentObjectRenderer stdWrap functionality on data.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class ContentObjectService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var ContentObjectRenderer
|
||||
*/
|
||||
protected $contentObjectRenderer;
|
||||
|
||||
/**
|
||||
* StdWrapService constructor.
|
||||
* @param ContentObjectRenderer|null $contentObject
|
||||
*/
|
||||
public function __construct(ContentObjectRenderer $contentObject = null)
|
||||
{
|
||||
$this->contentObjectRenderer = $contentObject ?? GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method use $name and $conf and passes it directly to cObjGetSingle.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $conf
|
||||
* @return string
|
||||
*/
|
||||
public function renderSingleContentObject($name = '', $conf = [])
|
||||
{
|
||||
return $this->contentObjectRenderer->cObjGetSingle($name, $conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Very often cObjGetSingle is used with 'field' as $name and 'field.' as $conf with this
|
||||
* method you can pass the array and the $key that is used to access $conant and $conf from $array.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function renderSingleContentObjectByArrayAndKey($array = [], $key = '')
|
||||
{
|
||||
$name = isset($array[$key]) ? $array[$key] : [];
|
||||
$conf = isset($array[$key . '.']) ? $array[$key . '.'] : '';
|
||||
|
||||
if (!is_array($conf)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $this->renderSingleContentObject($name, $conf);
|
||||
}
|
||||
}
|
169
Classes/System/Data/AbstractCollection.php
Normal file
169
Classes/System/Data/AbstractCollection.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Data;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class AbstractCollection
|
||||
*/
|
||||
abstract class AbstractCollection implements \IteratorAggregate, \Countable, \ArrayAccess
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function __construct(array $data = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function clean()
|
||||
{
|
||||
$this->data = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to pass a closure to created a filtered copy.
|
||||
* The closure get an collection item passed and needs to return true when the item should
|
||||
* be kept or false when it can be skipped.
|
||||
*
|
||||
* @param callable $filter
|
||||
* @return AbstractCollection
|
||||
*/
|
||||
public function getFilteredCopy(\Closure $filter)
|
||||
{
|
||||
$copy = clone $this;
|
||||
$filteredData = [];
|
||||
foreach ($this->data as $key => $item) {
|
||||
if ($filter($item)) {
|
||||
$filteredData[$key] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$copy->data = $filteredData;
|
||||
return $copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ArrayIterator|\Traversable
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getArrayCopy()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $position
|
||||
* @return Object
|
||||
*/
|
||||
public function getByPosition($position)
|
||||
{
|
||||
$keys = array_keys($this->data);
|
||||
return isset($this->data[$keys[$position]]) ? $this->data[$keys[$position]] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.1.0)<br/>
|
||||
* Count elements of an object
|
||||
* @link http://php.net/manual/en/countable.count.php
|
||||
* @return int The custom count as an integer.
|
||||
* </p>
|
||||
* <p>
|
||||
* The return value is cast to an integer.
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a offset exists
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return bool true on success or false on failure
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return array_key_exists($offset, $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to retrieve
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset)) {
|
||||
return $this->data[$offset];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to set
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ($offset === null) {
|
||||
$this->data[] = $value;
|
||||
return;
|
||||
}
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset to unset
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset)) {
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
}
|
||||
}
|
41
Classes/System/Data/DateTime.php
Normal file
41
Classes/System/Data/DateTime.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Data;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class DateTime
|
||||
*/
|
||||
class DateTime extends \DateTime
|
||||
{
|
||||
/**
|
||||
* @param string $time
|
||||
* @param \DateTimeZone $timezone
|
||||
*/
|
||||
public function __construct($time = 'now', \DateTimeZone $timezone = null)
|
||||
{
|
||||
parent::__construct($time, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date formatted as ISO.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->format(\DateTime::ISO8601);
|
||||
}
|
||||
}
|
128
Classes/System/DateTime/FormatService.php
Normal file
128
Classes/System/DateTime/FormatService.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\DateTime;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2011-2016 Timo Schmidt <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
/**
|
||||
* Testcase to check if the configuration object can be used as expected
|
||||
*
|
||||
* @author Hendrik Putzek <hendrik.putzek@dkd.de>
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class FormatService
|
||||
{
|
||||
const SOLR_ISO_DATETIME_FORMAT = 'Y-m-d\TH:i:s\Z';
|
||||
|
||||
/**
|
||||
* @see http://php.net/manual/de/function.date.php for formatting options
|
||||
* @param string $input the passed date string
|
||||
* @param string $inputFormat the input format that should be used for parsing
|
||||
* @param string $outputFormat The output format, when nothing is passed
|
||||
* $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] will be used or Y-m-d when nothing is configured
|
||||
* @param DateTimeZone $timezone
|
||||
* @return \DateTime|string
|
||||
*/
|
||||
public function format($input = '', $inputFormat = 'Y-m-d\TH:i:s\Z', $outputFormat = '', $timezone = null)
|
||||
{
|
||||
if ($outputFormat === '') {
|
||||
// when no value was passed we us the TYPO3 configured or fallback to Y-m-d
|
||||
$outputFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] ?: 'Y-m-d';
|
||||
}
|
||||
|
||||
// try to create DateTime object
|
||||
$timezone = $timezone ?? new DateTimeZone(date_default_timezone_get());
|
||||
return $this->getFormattedDate($input, $inputFormat, $outputFormat, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date from unix timestamp to ISO 8601 format.
|
||||
*
|
||||
* @param int $timestamp unix timestamp
|
||||
* @return string the date in ISO 8601 format
|
||||
*/
|
||||
public function timestampToIso($timestamp)
|
||||
{
|
||||
return date(self::SOLR_ISO_DATETIME_FORMAT, $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date from ISO 8601 format to unix timestamp.
|
||||
*
|
||||
* @param string $isoTime date in ISO 8601 format
|
||||
* @return int unix timestamp
|
||||
*/
|
||||
public function isoToTimestamp($isoTime)
|
||||
{
|
||||
$dateTime = \DateTime::createFromFormat(self::SOLR_ISO_DATETIME_FORMAT,
|
||||
$isoTime);
|
||||
return $dateTime ? (int)$dateTime->format('U') : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date from unix timestamp to ISO 8601 format in UTC timezone.
|
||||
*
|
||||
* @param int $timestamp unix timestamp
|
||||
* @return string the date in ISO 8601 format
|
||||
*/
|
||||
public function timestampToUtcIso($timestamp)
|
||||
{
|
||||
return gmdate(self::SOLR_ISO_DATETIME_FORMAT, $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a date from ISO 8601 format in UTC timezone to unix timestamp.
|
||||
*
|
||||
* @param string $isoTime date in ISO 8601 format
|
||||
* @return int unix timestamp
|
||||
*/
|
||||
public function utcIsoToTimestamp($isoTime)
|
||||
{
|
||||
$utcTimeZone = new \DateTimeZone('UTC');
|
||||
$dateTime = \DateTime::createFromFormat(self::SOLR_ISO_DATETIME_FORMAT,
|
||||
$isoTime, $utcTimeZone);
|
||||
return $dateTime ? (int)$dateTime->format('U') : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the formatting using DateTime.
|
||||
*
|
||||
* @param string $input
|
||||
* @param string $inputFormat
|
||||
* @param string $outputFormat
|
||||
* @param DateTimeZone $timezone
|
||||
* @return \DateTime|string
|
||||
*/
|
||||
protected function getFormattedDate($input, $inputFormat, $outputFormat, DateTimeZone $timezone)
|
||||
{
|
||||
$formattedDate = DateTime::createFromFormat($inputFormat, $input, $timezone);
|
||||
if ($formattedDate) {
|
||||
return $formattedDate->format($outputFormat);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
106
Classes/System/Environment/CliEnvironment.php
Normal file
106
Classes/System/Environment/CliEnvironment.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Environment;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2016 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
|
||||
/**
|
||||
* Helper class for the cli environment helps to define the variables and constants
|
||||
* that are required in the cli context to allow frontend related operations in the cli context.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class CliEnvironment implements SingletonInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $backupServerVariables = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $isInitialized = false;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function backup()
|
||||
{
|
||||
$this->backupServerVariables = $_SERVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the frontend related server variables for the cli context.
|
||||
*
|
||||
* @param string $webRoot
|
||||
* @param string $scriptFileName
|
||||
* @param string $phpSelf
|
||||
* @param string $scriptName
|
||||
* @throws WebRootAllReadyDefinedException
|
||||
* @return bool
|
||||
*/
|
||||
public function initialize($webRoot, $scriptFileName = '', $phpSelf = '/index.php', $scriptName = '/index.php')
|
||||
{
|
||||
// if the environment has be initialized once, we do not need to initialize it twice.
|
||||
if ($this->isInitialized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('TYPO3_PATH_WEB')) {
|
||||
throw new WebRootAllReadyDefinedException('TYPO3_PATH_WEB is already defined');
|
||||
}
|
||||
|
||||
if ($scriptFileName === '') {
|
||||
$scriptFileName = Environment::getPublicPath() . '/';
|
||||
}
|
||||
|
||||
define('TYPO3_PATH_WEB', $webRoot);
|
||||
$_SERVER['SCRIPT_FILENAME'] = $scriptFileName;
|
||||
$_SERVER['PHP_SELF'] = $phpSelf;
|
||||
$_SERVER['SCRIPT_NAME'] = $scriptName;
|
||||
|
||||
$this->isInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsInitialized()
|
||||
{
|
||||
return $this->isInitialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function restore()
|
||||
{
|
||||
$_SERVER = $this->backupServerVariables;
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Environment;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2008-2016 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Exception that is thrown when a language file is needed, but not available.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class WebRootAllReadyDefinedException extends \Exception
|
||||
{
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Hooks\Backend\Toolbar;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2018 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Toolbar\ClearCacheActionsHookInterface;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class ClearCacheActionsHook
|
||||
*/
|
||||
class ClearCacheActionsHook implements ClearCacheActionsHookInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var UriBuilder
|
||||
*/
|
||||
protected $uriBuilder;
|
||||
|
||||
/**
|
||||
* @var BackendUserAuthentication
|
||||
*/
|
||||
protected $backendUser;
|
||||
|
||||
/**
|
||||
* ClearCacheActionsHook constructor.
|
||||
* @param UriBuilder|null $uriBuilder
|
||||
* @param BackendUserAuthentication|null $backendUser
|
||||
*/
|
||||
public function __construct(UriBuilder $uriBuilder = null, BackendUserAuthentication $backendUser = null)
|
||||
{
|
||||
$this->uriBuilder = $uriBuilder ?? GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$this->backendUser = $backendUser ?? $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a menu entry to the clear cache menu to detect Solr connections.
|
||||
*
|
||||
* @param array $cacheActions Array of CacheMenuItems
|
||||
* @param array $optionValues Array of AccessConfigurations-identifiers (typically used by userTS with options.clearCache.identifier)
|
||||
*/
|
||||
public function manipulateCacheActions(&$cacheActions, &$optionValues)
|
||||
{
|
||||
if (!$this->backendUser->isAdmin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$href = $this->uriBuilder->buildUriFromRoute('ajax_solr_updateConnections');
|
||||
$optionValues[] = 'clearSolrConnectionCache';
|
||||
$cacheActions[] = [
|
||||
'id' => 'clearSolrConnectionCache',
|
||||
'title' => 'LLL:EXT:meilisearch/Resources/Private/Language/locallang.xlf:cache_initialize_solr_connections',
|
||||
'href' => $href,
|
||||
'iconIdentifier' => 'extensions-solr-module-initsolrconnections'
|
||||
];
|
||||
}
|
||||
}
|
144
Classes/System/Language/FrontendOverlayService.php
Normal file
144
Classes/System/Language/FrontendOverlayService.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Language;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2018 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\TCA\TCAService;
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
|
||||
|
||||
/**
|
||||
* Class FrontendOverlayService
|
||||
*/
|
||||
class FrontendOverlayService {
|
||||
|
||||
/**
|
||||
* @var TCAService
|
||||
*/
|
||||
protected $tcaService = null;
|
||||
|
||||
/**
|
||||
* @var TypoScriptFrontendController
|
||||
*/
|
||||
protected $tsfe = null;
|
||||
|
||||
/**
|
||||
* Relation constructor.
|
||||
* @param TCAService|null $tcaService
|
||||
* @param TypoScriptFrontendController|null $tsfe
|
||||
*/
|
||||
public function __construct(TCAService $tcaService = null, TypoScriptFrontendController $tsfe = null)
|
||||
{
|
||||
$this->tcaService = $tcaService ?? GeneralUtility::makeInstance(TCAService::class);
|
||||
$this->tsfe = $tsfe ?? $GLOBALS['TSFE'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the translated record
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param array $record
|
||||
* @return array
|
||||
*/
|
||||
public function getOverlay($tableName, $record)
|
||||
{
|
||||
if ($tableName === 'pages') {
|
||||
// @extensionScannerIgnoreLine
|
||||
return $this->tsfe->sys_page->getPageOverlay($record, Util::getLanguageUid());
|
||||
}
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
return $this->tsfe->sys_page->getRecordOverlay($tableName, $record, Util::getLanguageUid());
|
||||
}
|
||||
|
||||
/**
|
||||
* When the record has an overlay we retrieve the uid of the translated record,
|
||||
* to resolve the relations from the translation.
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param int $uid
|
||||
* @return int
|
||||
*/
|
||||
public function getUidOfOverlay($table, $field, $uid)
|
||||
{
|
||||
// when no language is set at all we do not need to overlay
|
||||
if (Util::getLanguageUid() === null) {
|
||||
return $uid;
|
||||
}
|
||||
// when no language is set we can return the passed recordUid
|
||||
if (!(Util::getLanguageUid() > 0)) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
$record = $this->getRecord($table, $uid);
|
||||
|
||||
// when the overlay is not an array, we return the localRecordUid
|
||||
if (!is_array($record)) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
$overlayUid = $this->getLocalRecordUidFromOverlay($table, $record);
|
||||
$uid = ($overlayUid !== 0) ? $overlayUid : $uid;
|
||||
return $uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves the _PAGES_OVERLAY_UID or _LOCALIZED_UID from the localized record.
|
||||
*
|
||||
* @param string $localTableName
|
||||
* @param array $originalRecord
|
||||
* @return int
|
||||
*/
|
||||
protected function getLocalRecordUidFromOverlay($localTableName, $originalRecord)
|
||||
{
|
||||
$overlayRecord = $this->getOverlay($localTableName, $originalRecord);
|
||||
|
||||
// when there is a _PAGES_OVERLAY_UID | _LOCALIZED_UID in the overlay, we return it
|
||||
if ($localTableName === 'pages' && isset($overlayRecord['_PAGES_OVERLAY_UID'])) {
|
||||
return (int)$overlayRecord['_PAGES_OVERLAY_UID'];
|
||||
} elseif (isset($overlayRecord['_LOCALIZED_UID'])) {
|
||||
return (int)$overlayRecord['_LOCALIZED_UID'];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $localTableName
|
||||
* @param $localRecordUid
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getRecord($localTableName, $localRecordUid)
|
||||
{
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($localTableName);
|
||||
|
||||
$record = $queryBuilder->select('*')->from($localTableName)->where($queryBuilder->expr()->eq('uid', $localRecordUid))->execute()->fetch();
|
||||
return $record;
|
||||
}
|
||||
}
|
99
Classes/System/Logging/DebugWriter.php
Normal file
99
Classes/System/Logging/DebugWriter.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Logging;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use TYPO3\CMS\Core\Utility\DebugUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
|
||||
|
||||
/**
|
||||
* The DebugWriter is used to write the devLog messages to the output of the page, or to the TYPO3 console in the
|
||||
* backend to provide a simple and lightweigt debugging possibility.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class DebugWriter
|
||||
{
|
||||
|
||||
/**
|
||||
* When the feature is enabled with: plugin.tx_meilisearch.logging.debugOutput the log writer uses the extbase
|
||||
* debug functionality in the frontend, or the console in the backend to display the devlog messages.
|
||||
*
|
||||
* @param int|string $level Log level. Value according to \TYPO3\CMS\Core\Log\LogLevel. Alternatively accepts a string.
|
||||
* @param string $message Log message.
|
||||
* @param array $data Additional data to log
|
||||
*/
|
||||
public function write($level, $message, $data = [])
|
||||
{
|
||||
$debugAllowedForIp = $this->getIsAllowedByDevIPMask();
|
||||
if (!$debugAllowedForIp) {
|
||||
return;
|
||||
}
|
||||
|
||||
$isDebugOutputEnabled = $this->getIsDebugOutputEnabled();
|
||||
if (!$isDebugOutputEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->writeDebugMessage($level, $message, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function getIsAllowedByDevIPMask()
|
||||
{
|
||||
return GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Logging via debugOutput has been configured
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function getIsDebugOutputEnabled()
|
||||
{
|
||||
return Util::getSolrConfiguration()->getLoggingDebugOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $level Log level. Value according to \TYPO3\CMS\Core\Log\LogLevel. Alternatively accepts a string.
|
||||
* @param string $message Log message.
|
||||
* @param array $data Additional data to log
|
||||
*/
|
||||
protected function writeDebugMessage($level, $message, $data)
|
||||
{
|
||||
$parameters = ['extKey' => 'meilisearch', 'msg' => $message, 'level' => $level, 'data' => $data];
|
||||
$message = isset($parameters['msg']) ? $parameters['msg'] : '';
|
||||
if (TYPO3_MODE === 'BE') {
|
||||
DebugUtility::debug($parameters, $parameters['extKey'], 'DevLog ext:solr: ' . $message);
|
||||
} else {
|
||||
echo $message . ':<br/>';
|
||||
DebuggerUtility::var_dump($parameters);
|
||||
}
|
||||
}
|
||||
}
|
98
Classes/System/Logging/SolrLogManager.php
Normal file
98
Classes/System/Logging/SolrLogManager.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Logging;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 - Thomas Hohn <tho@systime.dk>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use TYPO3\CMS\Core\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\Log\LogManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Wrapper to for the TYPO3 Logging Framework
|
||||
*
|
||||
* @author Thomas Hohn <tho@systime.dk>
|
||||
*/
|
||||
class SolrLogManager
|
||||
{
|
||||
const WARNING = LogLevel::WARNING;
|
||||
const ERROR = LogLevel::ERROR;
|
||||
const INFO = LogLevel::INFO;
|
||||
const NOTICE = LogLevel::NOTICE;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected $logger = null;
|
||||
|
||||
/**
|
||||
* @var DebugWriter
|
||||
*/
|
||||
protected $debugWriter = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $className = '';
|
||||
|
||||
/**
|
||||
* SolrLogManager constructor.
|
||||
*
|
||||
* @param string $className
|
||||
* @param DebugWriter $debugWriter
|
||||
*/
|
||||
public function __construct($className, DebugWriter $debugWriter = null)
|
||||
{
|
||||
$this->className = $className;
|
||||
$this->debugWriter = $debugWriter ?? GeneralUtility::makeInstance(DebugWriter::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Core\Log\Logger
|
||||
*/
|
||||
protected function getLogger()
|
||||
{
|
||||
if ($this->logger === null) {
|
||||
$this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger($this->className);
|
||||
}
|
||||
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to the LogManager
|
||||
*
|
||||
* @param int|string $level Log level. Value according to \TYPO3\CMS\Core\Log\LogLevel. Alternatively accepts a string.
|
||||
* @param string $message Log message.
|
||||
* @param array $data Additional data to log
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function log($level, $message, array $data = [])
|
||||
{
|
||||
$this->getLogger()->log($level, $message, $data);
|
||||
$this->debugWriter->write($level, $message, $data);
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Mvc\Backend\Component\Exception;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-support@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use \TYPO3\CMS\Extbase\Mvc\Exception;
|
||||
|
||||
class InvalidViewObjectNameException extends Exception
|
||||
{
|
||||
}
|
84
Classes/System/Mvc/Backend/ModuleData.php
Normal file
84
Classes/System/Mvc/Backend/ModuleData.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Mvc\Backend;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-support@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Site\Site;
|
||||
|
||||
/**
|
||||
* Represents the state of needed for backend module components e.g. selected option from select menu, enabled or disabled button, etc..
|
||||
*/
|
||||
class ModuleData
|
||||
{
|
||||
/**
|
||||
* @var Site
|
||||
*/
|
||||
protected $site = null;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $core = '';
|
||||
|
||||
/**
|
||||
* Gets the site to work with.
|
||||
*
|
||||
* @return Site
|
||||
*/
|
||||
public function getSite()
|
||||
{
|
||||
return $this->site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the site to work with.
|
||||
*
|
||||
* @param Site $site
|
||||
* @return void
|
||||
*/
|
||||
public function setSite(Site $site)
|
||||
{
|
||||
$this->site = $site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the currently selected core
|
||||
*
|
||||
* @return string Selected core name
|
||||
*/
|
||||
public function getCore()
|
||||
{
|
||||
return $this->core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the currently selected core
|
||||
*
|
||||
* @param string $core Selected core name
|
||||
*/
|
||||
public function setCore($core)
|
||||
{
|
||||
$this->core = $core;
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Mvc\Backend\Service;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2013-2015 Ingo Renner <ingo@typo3.org>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Mvc\Backend\ModuleData;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Module data storage service. Used to store and retrieve module state (eg.
|
||||
* checkboxes, selections).
|
||||
*/
|
||||
class ModuleDataStorageService implements SingletonInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const KEY = 'tx_meilisearch';
|
||||
|
||||
/**
|
||||
* Loads module data for user settings or returns a fresh object initially
|
||||
*
|
||||
* @return ModuleData
|
||||
*/
|
||||
public function loadModuleData()
|
||||
{
|
||||
$moduleData = $GLOBALS['BE_USER']->getModuleData(self::KEY);
|
||||
|
||||
$this->unsetModuleDataIfCanNotBeSerialized($moduleData);
|
||||
if (empty($moduleData) || !$moduleData) {
|
||||
$moduleData = GeneralUtility::makeInstance(ModuleData::class);
|
||||
} else {
|
||||
$moduleData = unserialize($moduleData);
|
||||
}
|
||||
|
||||
return $moduleData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists serialized module data to user settings
|
||||
*
|
||||
* @param ModuleData $moduleData
|
||||
* @return void
|
||||
*/
|
||||
public function persistModuleData(ModuleData $moduleData)
|
||||
{
|
||||
$GLOBALS['BE_USER']->pushModuleData(self::KEY, serialize($moduleData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets not serializable module data.
|
||||
*
|
||||
* @param string|null $serializedModuleData
|
||||
*/
|
||||
private function unsetModuleDataIfCanNotBeSerialized(string &$serializedModuleData = null)
|
||||
{
|
||||
if (!isset($serializedModuleData)) {
|
||||
$serializedModuleData = '';
|
||||
return;
|
||||
}
|
||||
if (false !== strpos($serializedModuleData, 'ApacheSolrForTypo3\\Solr\\Domain\\Model\\ModuleData')
|
||||
|| false !== strpos($serializedModuleData, 'Tx_Solr_Site')) {
|
||||
$serializedModuleData = '';
|
||||
}
|
||||
}
|
||||
}
|
113
Classes/System/Object/AbstractClassRegistry.php
Normal file
113
Classes/System/Object/AbstractClassRegistry.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Object;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
|
||||
/**
|
||||
* Abstract class to hold the logic to register and retrieve different classes
|
||||
* for a specific key.
|
||||
*
|
||||
* Can be used to retrieve different "strategies" for the same thing.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class AbstractClassRegistry implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* Holds the mapping key => className
|
||||
* @var array
|
||||
*/
|
||||
protected $classMap = [];
|
||||
|
||||
/**
|
||||
* Name for the default implementation
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultClass = \stdClass::class;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @param ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an instance for an registered type.
|
||||
*
|
||||
* @param string $type
|
||||
* @return object
|
||||
*/
|
||||
public function getInstance($type)
|
||||
{
|
||||
$className = $this->resolveClassName($type);
|
||||
return $this->createInstance($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveClassName($type)
|
||||
{
|
||||
$className = $this->defaultClass;
|
||||
if (isset($this->classMap[$type])) {
|
||||
$className = $this->classMap[$type];
|
||||
return $className;
|
||||
}
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a certain class
|
||||
*
|
||||
* @param string $className
|
||||
* @return object
|
||||
*/
|
||||
protected function createInstance($className)
|
||||
{
|
||||
return $this->objectManager->get($className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to register an implementation in the classMap.
|
||||
*
|
||||
* @param string $className
|
||||
* @param string $type
|
||||
* @param string $requiredBaseClass
|
||||
*/
|
||||
protected function register($className, $type, $requiredBaseClass) {
|
||||
// check if the class is available for TYPO3 before registering the driver
|
||||
if (!class_exists($className)) {
|
||||
throw new \InvalidArgumentException('Class ' . $className . ' does not exist.', 1462883324);
|
||||
}
|
||||
|
||||
if (!is_subclass_of($className, $requiredBaseClass)) {
|
||||
throw new \InvalidArgumentException('Parser ' . $className . ' needs to extend the ' . $requiredBaseClass . '.', 1462883325);
|
||||
}
|
||||
|
||||
$this->classMap[$type] = $className;
|
||||
}
|
||||
}
|
||||
|
125
Classes/System/Page/Rootline.php
Normal file
125
Classes/System/Page/Rootline.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Page;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Site\Site;
|
||||
|
||||
/**
|
||||
* Rootline class. This class is used to perform operations on a rootline array.
|
||||
* The constructor requires an rootline array as an arguments (as you get it from
|
||||
* PageRepository::getRootline or TSFE->rootline.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Rootline
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $rootLineArray = [];
|
||||
|
||||
/**
|
||||
* Rootline constructor.
|
||||
* @param array $rootLineArray
|
||||
*/
|
||||
public function __construct(array $rootLineArray = [])
|
||||
{
|
||||
$this->rootLineArray = $rootLineArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRootLineArray()
|
||||
{
|
||||
return $this->rootLineArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $rootLineArray
|
||||
*/
|
||||
public function setRootLineArray($rootLineArray)
|
||||
{
|
||||
$this->rootLineArray = $rootLineArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the rooline contains a root page.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function getHasRootPage()
|
||||
{
|
||||
return $this->getRootPageId() !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rootPageId as integer if a rootpage is given,
|
||||
* if non is given 0 will be returned
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getRootPageId()
|
||||
{
|
||||
$rootPageId = 0;
|
||||
|
||||
if (empty($this->rootLineArray)) {
|
||||
return $rootPageId;
|
||||
}
|
||||
|
||||
foreach ($this->rootLineArray as $page) {
|
||||
if (Site::isRootPage($page)) {
|
||||
$rootPageId = $page['uid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $rootPageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the pageUids in the rootline.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getParentPageIds()
|
||||
{
|
||||
$rootLineParentPageIds = [];
|
||||
if (empty($this->rootLineArray)) {
|
||||
// no rootline given
|
||||
return $rootLineParentPageIds;
|
||||
}
|
||||
|
||||
foreach ($this->rootLineArray as $pageRecord) {
|
||||
$rootLineParentPageIds[] = $pageRecord['uid'];
|
||||
if (Site::isRootPage($pageRecord)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $rootLineParentPageIds;
|
||||
}
|
||||
}
|
130
Classes/System/Records/AbstractRepository.php
Normal file
130
Classes/System/Records/AbstractRepository.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Records;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Repository class to encapsulate the database access for records used in solr.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
abstract class AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '';
|
||||
|
||||
/**
|
||||
* Retrieves a single row from the database by a given uid
|
||||
*
|
||||
* @param string $fields
|
||||
* @param string $uid
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getOneRowByUid($fields, $uid)
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
return $queryBuilder
|
||||
->select($fields)
|
||||
->from($this->table)
|
||||
->where($queryBuilder->expr()->eq('uid', intval($uid)))
|
||||
->execute()->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns QueryBuilder for Doctrine DBAL
|
||||
*
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
protected function getQueryBuilder()
|
||||
{
|
||||
/** @var QueryBuilder $queryBuilder */
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current count of last searches
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() : int
|
||||
{
|
||||
return (int)$this->getQueryBuilder()
|
||||
->count('*')
|
||||
->from($this->table)
|
||||
->execute()->fetchColumn(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns connection for all in transaction involved tables.
|
||||
*
|
||||
* Note: Rollback will not work in case of different connections.
|
||||
*
|
||||
* @param string[] ...$tableNames
|
||||
* @return Connection
|
||||
*/
|
||||
public function getConnectionForAllInTransactionInvolvedTables(string ...$tableNames) : Connection
|
||||
{
|
||||
if (empty($tableNames) || count($tableNames) < 2) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . ' requires at least 2 table names.', 1504801512);
|
||||
}
|
||||
|
||||
if (!$this->isConnectionForAllTablesTheSame(...$tableNames)) {
|
||||
throw new \RuntimeException(
|
||||
vsprintf('The tables "%s" using different database connections. Transaction needs same database connection ' .
|
||||
'for all tables, please reconfigure the database settings for involved tables properly.', [implode('", "', $tableNames)]
|
||||
), 1504866142
|
||||
);
|
||||
}
|
||||
return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable(array_shift($tableNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all table involved in transaction using same connection.
|
||||
*
|
||||
* @param string[] ...$tableNames
|
||||
* @return bool
|
||||
*/
|
||||
protected function isConnectionForAllTablesTheSame(string ...$tableNames) : bool
|
||||
{
|
||||
/** @var ConnectionPool $connectionPool */
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$connection = $connectionPool->getConnectionForTable(array_shift($tableNames));
|
||||
foreach ($tableNames as $tableName) {
|
||||
$connectionForTable = $connectionPool->getConnectionForTable($tableName);
|
||||
if ($connection !== $connectionForTable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
296
Classes/System/Records/Pages/PagesRepository.php
Normal file
296
Classes/System/Records/Pages/PagesRepository.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Records\Pages;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-eb-support@dkd.de>
|
||||
*
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Cache\TwoLevelCache;
|
||||
use WapplerSystems\Meilisearch\System\Records\AbstractRepository;
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* PagesRepository to encapsulate the database access.
|
||||
*/
|
||||
class PagesRepository extends AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'pages';
|
||||
|
||||
/**
|
||||
* @var TwoLevelCache
|
||||
*/
|
||||
protected $transientVariableCache;
|
||||
|
||||
/**
|
||||
* PagesRepository constructor.
|
||||
*
|
||||
* @param TwoLevelCache|null $transientVariableCache
|
||||
*/
|
||||
public function __construct(TwoLevelCache $transientVariableCache = null)
|
||||
{
|
||||
$this->transientVariableCache = $transientVariableCache ?? GeneralUtility::makeInstance(TwoLevelCache::class, /** @scrutinizer ignore-type */ 'cache_runtime');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the site's root pages. The "Is root of website" flag must be set,
|
||||
* which usually is the case for pages with pid = 0.
|
||||
*
|
||||
* @return array An array of (partial) root page records, containing the uid and title fields
|
||||
*/
|
||||
public function findAllRootPages()
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
|
||||
$queryBuilder
|
||||
->select('uid', 'title')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->neq('pid', -1),
|
||||
$queryBuilder->expr()->eq('is_siteroot', 1)
|
||||
);
|
||||
|
||||
|
||||
$this->addDefaultLanguageUidConstraint($queryBuilder);
|
||||
|
||||
return $queryBuilder->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the MountPointProperties array for mount points(destinations) by mounted page UID(source) or by the rootline array of mounted page.
|
||||
*
|
||||
* @param int $mountedPageUid
|
||||
* @param array $rootLineParentPageIds
|
||||
* @return array
|
||||
*/
|
||||
public function findMountPointPropertiesByPageIdOrByRootLineParentPageIds(int $mountedPageUid, array $rootLineParentPageIds = []) : array
|
||||
{
|
||||
if (array_filter($rootLineParentPageIds, 'is_int') !== $rootLineParentPageIds) {
|
||||
throw new \InvalidArgumentException('Given $rootLineParentPageIds array is not valid. Allowed only the arrays with the root line page UIDs as integers.', 1502459711);
|
||||
}
|
||||
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->select('uid', 'uid AS mountPageDestination', 'mount_pid AS mountPageSource', 'mount_pid_ol AS mountPageOverlayed')->from($this->table);
|
||||
$queryBuilder = $this->addWhereClauseForMountpointDestinationProperties($queryBuilder, $mountedPageUid, $rootLineParentPageIds);
|
||||
$result = $queryBuilder->execute()->fetchAll();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods builds the where clause for the mountpoint destinations. It retrieves all records where the mount_pid = $mountedPageUid or the mount_pid is
|
||||
* in the rootLineParentPageIds.
|
||||
*
|
||||
* @param QueryBuilder $queryBuilder
|
||||
* @param int $mountedPageUid
|
||||
* @param array $rootLineParentPageIds
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
protected function addWhereClauseForMountpointDestinationProperties(QueryBuilder $queryBuilder, $mountedPageUid, array $rootLineParentPageIds) : QueryBuilder
|
||||
{
|
||||
if (empty($rootLineParentPageIds)) {
|
||||
$queryBuilder->andWhere(
|
||||
$queryBuilder->expr()->eq('doktype', 7),
|
||||
$queryBuilder->expr()->eq('no_search', 0),
|
||||
$queryBuilder->expr()->eq('mount_pid', $mountedPageUid),
|
||||
$queryBuilder->expr()->eq('mount_pid_ol', 1)
|
||||
);
|
||||
} else {
|
||||
$queryBuilder->andWhere(
|
||||
$queryBuilder->expr()->eq('doktype', 7),
|
||||
$queryBuilder->expr()->eq('no_search', 0),
|
||||
$queryBuilder->expr()->orX(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('mount_pid', $mountedPageUid),
|
||||
$queryBuilder->expr()->eq('mount_pid_ol', 1)
|
||||
),
|
||||
$queryBuilder->expr()->in('mount_pid', $rootLineParentPageIds)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->addDefaultLanguageUidConstraint($queryBuilder);
|
||||
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of page IDs in this site.
|
||||
* Attention: Includes all page types except Deleted pages!
|
||||
*
|
||||
* @param int $rootPageId Page ID from where to start collection sub pages
|
||||
* @param int $maxDepth Maximum depth to descend into the site tree
|
||||
* @param string $initialPagesAdditionalWhereClause
|
||||
* @return array Array of pages (IDs) in this site
|
||||
*/
|
||||
public function findAllSubPageIdsByRootPage(int $rootPageId, int $maxDepth = 999, string $initialPagesAdditionalWhereClause = '') : array
|
||||
{
|
||||
$pageIds = [];
|
||||
|
||||
$recursionRootPageId = $rootPageId;
|
||||
|
||||
// when we have a cached value, we can return it.
|
||||
$cacheIdentifier = sha1('getPages' . (string)$rootPageId);
|
||||
if ($this->transientVariableCache->get($cacheIdentifier) !== false) {
|
||||
return $this->transientVariableCache->get($cacheIdentifier);
|
||||
}
|
||||
|
||||
if ($maxDepth <= 0) {
|
||||
// exiting the recursion loop, may write to cache now
|
||||
$this->transientVariableCache->set($cacheIdentifier, $pageIds);
|
||||
return $pageIds;
|
||||
}
|
||||
|
||||
// get the page ids of the current level and if needed call getPages recursive
|
||||
$pageIds = $this->getPageIdsFromCurrentDepthAndCallRecursive($maxDepth, $recursionRootPageId, $pageIds, $initialPagesAdditionalWhereClause);
|
||||
|
||||
// exiting the recursion loop, may write to cache now
|
||||
$this->transientVariableCache->set($cacheIdentifier, $pageIds);
|
||||
return $pageIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves the pages ids from the current tree level an calls getPages recursive,
|
||||
* when the maxDepth has not been reached.
|
||||
*
|
||||
* @param int $maxDepth
|
||||
* @param int $recursionRootPageId
|
||||
* @param array $pageIds
|
||||
* @param string $initialPagesAdditionalWhereClause
|
||||
* @return array
|
||||
*/
|
||||
protected function getPageIdsFromCurrentDepthAndCallRecursive(int $maxDepth, int $recursionRootPageId, array $pageIds, string $initialPagesAdditionalWhereClause = '')
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
|
||||
$queryBuilder
|
||||
->select('uid')
|
||||
->from($this->table)
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($recursionRootPageId, \PDO::PARAM_INT))
|
||||
);
|
||||
|
||||
$this->addDefaultLanguageUidConstraint($queryBuilder);
|
||||
|
||||
if (!empty($initialPagesAdditionalWhereClause)) {
|
||||
$queryBuilder->andWhere($initialPagesAdditionalWhereClause);
|
||||
}
|
||||
|
||||
$resultSet = $queryBuilder->execute();
|
||||
while ($page = $resultSet->fetch()) {
|
||||
$pageIds[] = $page['uid'];
|
||||
|
||||
if ($maxDepth > 1) {
|
||||
$pageIds = array_merge($pageIds, $this->findAllSubPageIdsByRootPage($page['uid'], $maxDepth - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return $pageIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds translation overlays by given page Id.
|
||||
*
|
||||
* @param int $pageId
|
||||
* @return array
|
||||
*/
|
||||
public function findTranslationOverlaysByPageId(int $pageId) : array
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
|
||||
return $queryBuilder
|
||||
->select('pid', 'l10n_parent', 'sys_language_uid')
|
||||
->from('pages')
|
||||
->add('where',
|
||||
$queryBuilder->expr()->eq('l10n_parent', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT))
|
||||
. BackendUtility::BEenableFields('pages')
|
||||
)->execute()->fetchAll();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds Pages, which are showing content from the page currently being updated.
|
||||
*
|
||||
* @param int $pageId UID of the page currently being updated
|
||||
* @return array with page Uids from pages, which are showing contents from given Page Id
|
||||
*/
|
||||
public function findPageUidsWithContentsFromPid(int $pageId) : array
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
|
||||
|
||||
$queryBuilder
|
||||
->select('uid')
|
||||
->from($this->table)
|
||||
->add('where',
|
||||
$queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT))
|
||||
);
|
||||
|
||||
$this->addDefaultLanguageUidConstraint($queryBuilder);
|
||||
|
||||
return $queryBuilder->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all pages by given where clause
|
||||
*
|
||||
* @param string $whereClause
|
||||
* @return array
|
||||
*/
|
||||
public function findAllMountPagesByWhereClause(string $whereClause) : array
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$queryBuilder
|
||||
->select(
|
||||
'uid',
|
||||
'mount_pid AS mountPageSource',
|
||||
'uid AS mountPageDestination',
|
||||
'mount_pid_ol AS mountPageOverlayed')
|
||||
->from($this->table)
|
||||
->add('where', $whereClause);
|
||||
|
||||
$this->addDefaultLanguageUidConstraint($queryBuilder);
|
||||
|
||||
return $queryBuilder->execute()->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the pages to the sys_language_uid = 0 (default language)
|
||||
*
|
||||
* @param $queryBuilder
|
||||
*/
|
||||
protected function addDefaultLanguageUidConstraint($queryBuilder)
|
||||
{
|
||||
$queryBuilder->andWhere($queryBuilder->expr()->eq('sys_language_uid', 0));
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Records\SystemCategory;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Records\AbstractRepository;
|
||||
|
||||
/**
|
||||
* Repository class for sys_category items of the TYPO3 system.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class SystemCategoryRepository extends AbstractRepository
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'sys_category';
|
||||
|
||||
/**
|
||||
* @param int $uid
|
||||
* @param string $limitFields
|
||||
* @return array
|
||||
*/
|
||||
public function findOneByUid($uid = 0, $limitFields = '*')
|
||||
{
|
||||
return $this->getOneRowByUid($limitFields, $uid);
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Records\SystemLanguage;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-eb-support@dkd.de>
|
||||
*
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Records\AbstractRepository;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* SystemLanguageRepository to encapsulate the database access for records used in solr.
|
||||
*
|
||||
*/
|
||||
class SystemLanguageRepository extends AbstractRepository implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'sys_language';
|
||||
|
||||
/**
|
||||
* Finds the language name for a given language ID.
|
||||
*
|
||||
* @param int $languageId language ID
|
||||
* @return string Language name
|
||||
*/
|
||||
public function findOneLanguageTitleByLanguageId(int $languageId) : string
|
||||
{
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$result = $queryBuilder->select('title')
|
||||
->from($this->table)
|
||||
->where($queryBuilder->expr()->eq('uid', $languageId))
|
||||
->execute()->fetch();
|
||||
|
||||
if ($result == false && $languageId == 0) {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
return isset($result['title']) ? $result['title'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the system's configured languages.
|
||||
*
|
||||
* @return array An array of language UIDs
|
||||
*/
|
||||
public function findSystemLanguages()
|
||||
{
|
||||
$languages = [0];
|
||||
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
$queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
|
||||
$languageRecords = $queryBuilder->select('uid')
|
||||
->from($this->table)
|
||||
->execute()->fetchAll();
|
||||
|
||||
if ($languageRecords == false) {
|
||||
return $languages;
|
||||
}
|
||||
|
||||
foreach ($languageRecords as $languageRecord) {
|
||||
$languages[] = $languageRecord['uid'];
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Records\SystemTemplate;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-eb-support@dkd.de>
|
||||
*
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Records\AbstractRepository;
|
||||
|
||||
/**
|
||||
* SystemTemplateRepository to encapsulate the database access for records used in solr.
|
||||
*
|
||||
*/
|
||||
class SystemTemplateRepository extends AbstractRepository
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'sys_template';
|
||||
|
||||
/**
|
||||
* Finds a first closest page id with active template.
|
||||
*
|
||||
* This method expects one startPageId, which must be inside the root line and does not check if it is one in the root line.
|
||||
*
|
||||
* @param array $rootLine
|
||||
* @return int
|
||||
*/
|
||||
public function findOneClosestPageIdWithActiveTemplateByRootLine(array $rootLine)
|
||||
{
|
||||
$rootLinePageIds = [0];
|
||||
foreach ($rootLine as $rootLineItem) {
|
||||
$rootLinePageIds[] = (int)$rootLineItem['uid'];
|
||||
}
|
||||
|
||||
$queryBuilder = $this->getQueryBuilder();
|
||||
|
||||
$result = $queryBuilder
|
||||
->select('uid', 'pid')
|
||||
->from($this->table)
|
||||
->where($queryBuilder->expr()->in('pid', $rootLinePageIds))
|
||||
->execute()->fetch();
|
||||
|
||||
return isset($result['pid']) ? $result['pid'] : 0;
|
||||
}
|
||||
}
|
142
Classes/System/Service/ConfigurationService.php
Normal file
142
Classes/System/Service/ConfigurationService.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Service;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
|
||||
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||
|
||||
/**
|
||||
* Service to ease work with configurations.
|
||||
*
|
||||
* @author Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*/
|
||||
class ConfigurationService
|
||||
{
|
||||
/**
|
||||
* @var FlexFormService
|
||||
*/
|
||||
protected $flexFormService;
|
||||
|
||||
/**
|
||||
* @var TypoScriptService
|
||||
*/
|
||||
protected $typoScriptService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
||||
$this->typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FlexFormService $flexFormService
|
||||
*/
|
||||
public function setFlexFormService($flexFormService)
|
||||
{
|
||||
$this->flexFormService = $flexFormService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\TypoScript\TypoScriptService $typoScriptService
|
||||
*/
|
||||
public function setTypoScriptService($typoScriptService)
|
||||
{
|
||||
$this->typoScriptService = $typoScriptService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the given solrConfiguration with flex form configuration.
|
||||
*
|
||||
* @param string $flexFormData The raw data from database.
|
||||
* @param TypoScriptConfiguration $solrTypoScriptConfiguration
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function overrideConfigurationWithFlexFormSettings($flexFormData, TypoScriptConfiguration $solrTypoScriptConfiguration)
|
||||
{
|
||||
if (empty($flexFormData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flexFormConfiguration = $this->flexFormService->convertFlexFormContentToArray($flexFormData);
|
||||
$flexFormConfiguration = $this->overrideFilter($flexFormConfiguration);
|
||||
$flexFormConfiguration = $this->typoScriptService->convertPlainArrayToTypoScriptArray($flexFormConfiguration);
|
||||
|
||||
$solrTypoScriptConfiguration->mergeSolrConfiguration($flexFormConfiguration, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override filter in configuration.
|
||||
*
|
||||
* Will parse the filter from flex form structure and rewrite it as typoscript structure.
|
||||
*
|
||||
* @param array $flexFormConfiguration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function overrideFilter(array $flexFormConfiguration)
|
||||
{
|
||||
$filter = $this->getFilterFromFlexForm($flexFormConfiguration);
|
||||
unset($flexFormConfiguration['search']['query']['filter']);
|
||||
if (empty($filter)) {
|
||||
return $flexFormConfiguration;
|
||||
}
|
||||
|
||||
return array_merge_recursive(
|
||||
$flexFormConfiguration,
|
||||
[
|
||||
'search' => [
|
||||
'query' => [
|
||||
'filter' => $filter,
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns filter in typoscript form from flex form.
|
||||
*
|
||||
* @param array $flexFormConfiguration
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFilterFromFlexForm(array $flexFormConfiguration)
|
||||
{
|
||||
$filterConfiguration = [];
|
||||
$filters = ObjectAccess::getPropertyPath($flexFormConfiguration, 'search.query.filter');
|
||||
|
||||
if (empty($filters)) {
|
||||
return $filterConfiguration;
|
||||
}
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
$filter = $filter['field'];
|
||||
|
||||
$fieldName = $filter['field'];
|
||||
$fieldValue = $filter['value'];
|
||||
|
||||
if (!is_numeric($fieldValue) && strpos($fieldValue, '?') === false && strpos($fieldValue, '*') === false) {
|
||||
$fieldValue = '"' . str_replace('"', '\"', $fieldValue) . '"';
|
||||
}
|
||||
|
||||
$filterConfiguration[] = $fieldName . ':' . $fieldValue;
|
||||
}
|
||||
return $filterConfiguration;
|
||||
}
|
||||
}
|
93
Classes/System/Session/FrontendUserSession.php
Normal file
93
Classes/System/Session/FrontendUserSession.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Session;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 dkd Internet Service GmbH <solr-eb-support@dkd.de>
|
||||
*
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
|
||||
|
||||
/**
|
||||
* Encapsulates the access to the session of the frontend user.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class FrontendUserSession
|
||||
{
|
||||
|
||||
/**
|
||||
* @var FrontendUserAuthentication
|
||||
*/
|
||||
protected $feUser;
|
||||
|
||||
/**
|
||||
* FrontendUserSession constructor.
|
||||
* @param FrontendUserAuthentication $feUser
|
||||
*/
|
||||
public function __construct(FrontendUserAuthentication $feUser = null)
|
||||
{
|
||||
$this->feUser = $feUser ?? $GLOBALS['TSFE']->fe_user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $requestedPerPage
|
||||
*/
|
||||
public function setPerPage(int $requestedPerPage)
|
||||
{
|
||||
$this->feUser->setKey('ses', 'tx_meilisearch_resultsPerPage', intval($requestedPerPage));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPerPage() : int
|
||||
{
|
||||
return (int)$this->feUser->getKey('ses', 'tx_meilisearch_resultsPerPage');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function getHasPerPage()
|
||||
{
|
||||
return $this->feUser->getKey('ses', 'tx_meilisearch_resultsPerPage') !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLastSearches() : array
|
||||
{
|
||||
$result = $this->feUser->getKey('ses', 'tx_meilisearch_lastSearches');
|
||||
return is_array($result) ? $result : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $lastSearches
|
||||
*/
|
||||
public function setLastSearches(array $lastSearches)
|
||||
{
|
||||
return $this->feUser->setKey('ses', 'tx_meilisearch_lastSearches', $lastSearches);
|
||||
}
|
||||
}
|
52
Classes/System/Solr/Document/Document.php
Normal file
52
Classes/System/Solr/Document/Document.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Document;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use RuntimeException;
|
||||
use Solarium\QueryType\Update\Query\Document as SolariumDocument;
|
||||
|
||||
/**
|
||||
* Document representing the update query document
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Document extends SolariumDocument
|
||||
{
|
||||
/**
|
||||
* Magic call method used to emulate getters as used by the template engine.
|
||||
*
|
||||
* @param string $name method name
|
||||
* @param array $arguments method arguments
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (substr($name, 0, 3) == 'get') {
|
||||
$field = substr($name, 3);
|
||||
$field = strtolower($field[0]) . substr($field, 1);
|
||||
return $this->fields[$field] ?? null;
|
||||
} else {
|
||||
throw new RuntimeException('Call to undefined method. Supports magic getters only.', 1311006605);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFieldNames()
|
||||
{
|
||||
return array_keys($this->fields);
|
||||
}
|
||||
}
|
166
Classes/System/Solr/Node.php
Normal file
166
Classes/System/Solr/Node.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use Solarium\Core\Client\Endpoint;
|
||||
|
||||
/**
|
||||
* Represent a server node of solr, in the most setups you would only have one, but sometimes
|
||||
* multiple for reading and writing.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
* @copyright Copyright (c) 2009-2020 Timo Hund <timo.hund@dkd.de>
|
||||
*
|
||||
* @deprecated Class will removed with Ext:solr 12.x. Use class \Solarium\Core\Client\Endpoint instead.
|
||||
*/
|
||||
class Node extends Endpoint
|
||||
{
|
||||
/**
|
||||
* Node constructor.
|
||||
* @param string $scheme
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param string $path
|
||||
* @param ?string $username
|
||||
* @param ?string $password
|
||||
*/
|
||||
public function __construct(
|
||||
string $scheme = 'http',
|
||||
string $host = 'localhost',
|
||||
int $port = 8983,
|
||||
string $path = '/solr/core_en/',
|
||||
?string $username = null,
|
||||
?string $password = null
|
||||
) {
|
||||
$path = (string)$path;
|
||||
$elements = explode('/', trim($path, '/'));
|
||||
$coreName = (string)array_pop($elements);
|
||||
// Remove API version
|
||||
array_pop($elements);
|
||||
|
||||
// The path should always have the same format!
|
||||
$path = trim(implode('/', $elements), '/');
|
||||
|
||||
$options = [
|
||||
'scheme' => $scheme,
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'path' => '/' . $path,
|
||||
'collection' => null,
|
||||
'core' => $coreName,
|
||||
'leader' => false,
|
||||
];
|
||||
|
||||
parent::__construct($options);
|
||||
$this->setAuthentication($username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $configuration
|
||||
* @return Node
|
||||
*/
|
||||
public static function fromArray(array $configuration): Node
|
||||
{
|
||||
static::checkIfRequiredKeyIsSet($configuration, 'scheme');
|
||||
static::checkIfRequiredKeyIsSet($configuration, 'host');
|
||||
static::checkIfRequiredKeyIsSet($configuration, 'port');
|
||||
static::checkIfRequiredKeyIsSet($configuration, 'path');
|
||||
|
||||
$scheme = $configuration['scheme'];
|
||||
$host = $configuration['host'];
|
||||
$port = $configuration['port'];
|
||||
$path = $configuration['path'];
|
||||
|
||||
$username = $configuration['username'] ?? '';
|
||||
$password = $configuration['password'] ?? '';
|
||||
return new Node($scheme, $host, $port, $path, $username, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the required configuration option is set.
|
||||
*
|
||||
* @param array $configuration
|
||||
* @param string $name
|
||||
* @throws |UnexpectedValueException
|
||||
*/
|
||||
protected static function checkIfRequiredKeyIsSet(array $configuration, string $name)
|
||||
{
|
||||
if (empty($configuration[$name])) {
|
||||
throw new \UnexpectedValueException('Required solr connection property ' . $name. ' is missing.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername(): string
|
||||
{
|
||||
return (string)$this->getOption('username');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword(): string
|
||||
{
|
||||
return (string)$this->getOption('password');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path including api path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCoreBasePath(): string
|
||||
{
|
||||
$pathWithoutLeadingAndTrailingSlashes = trim(trim($this->getPath()), "/");
|
||||
$pathWithoutLastSegment = substr($pathWithoutLeadingAndTrailingSlashes, 0, strrpos($pathWithoutLeadingAndTrailingSlashes, "/"));
|
||||
return ($pathWithoutLastSegment === '') ? '/' : '/' . $pathWithoutLastSegment . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the core name from the configured path.
|
||||
*
|
||||
* @return string
|
||||
* @deprecated Will be remove with Ext:solr 12.x. Use method getCore() instead.
|
||||
*/
|
||||
public function getCoreName(): string
|
||||
{
|
||||
return $this->getCore();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSolariumClientOptions(): array
|
||||
{
|
||||
return [
|
||||
'host' => $this->getHost(),
|
||||
'port' => $this->getPort(),
|
||||
'scheme' => $this->getScheme(),
|
||||
'path' => $this->getPath(),
|
||||
'core' => $this->getCore()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated Will be removed with Ext:solr 12.x. Use methods getCoreBaseUri() for API version 1 instead
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getCoreBaseUri();
|
||||
}
|
||||
}
|
103
Classes/System/Solr/Parser/SchemaParser.php
Normal file
103
Classes/System/Solr/Parser/SchemaParser.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Parser;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Solr\Schema\Schema;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class to parse the schema from a solr response.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class SchemaParser
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse the solr stopwords response from an json string to an array.
|
||||
*
|
||||
* @param string $jsonString
|
||||
* @return Schema
|
||||
*/
|
||||
public function parseJson($jsonString)
|
||||
{
|
||||
$decodedResponse = json_decode($jsonString);
|
||||
$schemaResponse = $decodedResponse->schema;
|
||||
|
||||
$schema = GeneralUtility::makeInstance(Schema::class);
|
||||
|
||||
if ($schemaResponse === null) {
|
||||
return $schema;
|
||||
}
|
||||
|
||||
$language = $this->parseLanguage($schemaResponse);
|
||||
$schema->setLanguage($language);
|
||||
|
||||
$name = $this->parseName($schemaResponse);
|
||||
$schema->setName($name);
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the language from a solr schema response.
|
||||
*
|
||||
* @param \stdClass $schema
|
||||
* @return string
|
||||
*/
|
||||
protected function parseLanguage(\stdClass $schema)
|
||||
{
|
||||
$language = 'english';
|
||||
if (!is_object($schema) || !isset($schema->fieldTypes)) {
|
||||
return $language;
|
||||
}
|
||||
|
||||
foreach ($schema->fieldTypes as $fieldType) {
|
||||
if ($fieldType->name !== 'text') {
|
||||
continue;
|
||||
}
|
||||
// we have a text field
|
||||
foreach ($fieldType->queryAnalyzer->filters as $filter) {
|
||||
if ($filter->class === 'solr.ManagedSynonymGraphFilterFactory') {
|
||||
$language = $filter->managed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the schema name from the response.
|
||||
*
|
||||
* @param \stdClass $schemaResponse
|
||||
* @return string
|
||||
*/
|
||||
protected function parseName(\stdClass $schemaResponse)
|
||||
{
|
||||
return isset($schemaResponse->name) ? $schemaResponse->name : '';
|
||||
}
|
||||
}
|
74
Classes/System/Solr/Parser/StopWordParser.php
Normal file
74
Classes/System/Solr/Parser/StopWordParser.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Parser;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Class to parse the stopwords from a solr response.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class StopWordParser
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse the solr stopwords response from an json string to an array.
|
||||
*
|
||||
* @param string $jsonString
|
||||
* @return array
|
||||
*/
|
||||
public function parseJson($jsonString)
|
||||
{
|
||||
$stopWords = [];
|
||||
|
||||
$decodedResponse = json_decode($jsonString);
|
||||
|
||||
if (isset($decodedResponse->wordSet->managedList)) {
|
||||
$stopWords = (array)$decodedResponse->wordSet->managedList;
|
||||
}
|
||||
|
||||
return $stopWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $stopWords
|
||||
* @return string
|
||||
* @throws \Apache_Solr_InvalidArgumentException
|
||||
*/
|
||||
public function toJson($stopWords)
|
||||
{
|
||||
if (empty($stopWords)) {
|
||||
throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
|
||||
}
|
||||
|
||||
if (is_string($stopWords)) {
|
||||
$stopWords = [$stopWords];
|
||||
}
|
||||
|
||||
$stopWords = array_values($stopWords);
|
||||
return json_encode($stopWords);
|
||||
}
|
||||
}
|
75
Classes/System/Solr/Parser/SynonymParser.php
Normal file
75
Classes/System/Solr/Parser/SynonymParser.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Parser;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Class to parse the synonyms from a solr response.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class SynonymParser
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse the solr synonyms response from an json string to an array.
|
||||
*
|
||||
* @param string $baseWord
|
||||
* @param string $jsonString
|
||||
* @return array
|
||||
*/
|
||||
public function parseJson($baseWord, $jsonString)
|
||||
{
|
||||
$decodedResponse = json_decode($jsonString);
|
||||
$synonyms = [];
|
||||
if (!empty($baseWord)) {
|
||||
if (is_array($decodedResponse->{$baseWord})) {
|
||||
$synonyms = $decodedResponse->{$baseWord};
|
||||
}
|
||||
} else {
|
||||
if (isset($decodedResponse->synonymMappings->managedMap)) {
|
||||
$synonyms = (array)$decodedResponse->synonymMappings->managedMap;
|
||||
}
|
||||
}
|
||||
|
||||
return $synonyms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $baseWord
|
||||
* @param array $synonyms
|
||||
* @return string
|
||||
* @throws \Apache_Solr_InvalidArgumentException
|
||||
*/
|
||||
public function toJson($baseWord, $synonyms)
|
||||
{
|
||||
if (empty($baseWord) || empty($synonyms)) {
|
||||
throw new \Apache_Solr_InvalidArgumentException('Must provide base word and synonyms.');
|
||||
}
|
||||
|
||||
return json_encode([$baseWord => $synonyms]);
|
||||
}
|
||||
}
|
34
Classes/System/Solr/ParsingUtil.php
Normal file
34
Classes/System/Solr/ParsingUtil.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/**
|
||||
* This class provides static helper functions that are helpful during the result parsing for solr.
|
||||
*/
|
||||
class ParsingUtil
|
||||
{
|
||||
/**
|
||||
* This method is used to covert a array structure with json.nl=flat to have it as return with json.nl=map.
|
||||
*
|
||||
* @param $options
|
||||
* @return array
|
||||
*/
|
||||
public static function getMapArrayFromFlatArray(array $options): array
|
||||
{
|
||||
$keyValueMap = [];
|
||||
$valueFromKeyNode = -1;
|
||||
foreach($options as $key => $value) {
|
||||
$isKeyNode = (($key % 2) == 0);
|
||||
if ($isKeyNode) {
|
||||
$valueFromKeyNode = $value;
|
||||
} else {
|
||||
if($valueFromKeyNode == -1) {
|
||||
throw new \UnexpectedValueException('No optionValue before count value');
|
||||
}
|
||||
//we have a countNode
|
||||
$keyValueMap[$valueFromKeyNode] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $keyValueMap;
|
||||
}
|
||||
}
|
44
Classes/System/Solr/RequestFactory.php
Normal file
44
Classes/System/Solr/RequestFactory.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Core\Http\RequestFactory as CoreRequestFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
class RequestFactory extends CoreRequestFactory
|
||||
{
|
||||
protected $clientOptions = [];
|
||||
|
||||
/**
|
||||
* RequestFactory constructor.
|
||||
* @param array $clientOptions
|
||||
*/
|
||||
public function __construct(array $clientOptions)
|
||||
{
|
||||
$this->clientOptions = $clientOptions;
|
||||
}
|
||||
|
||||
public function request(string $uri, string $method = 'GET', array $options = []): ResponseInterface
|
||||
{
|
||||
/* @var GuzzleClient $client */
|
||||
$client = GeneralUtility::makeInstance(GuzzleClient::class, $this->clientOptions);
|
||||
return $client->request($method, $uri, $options);
|
||||
}
|
||||
}
|
150
Classes/System/Solr/ResponseAdapter.php
Normal file
150
Classes/System/Solr/ResponseAdapter.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Document\Document;
|
||||
use Countable;
|
||||
|
||||
/**
|
||||
* In EXT:meilisearch 9 we have switched from the SolrPhpClient to the solarium api.
|
||||
*
|
||||
* In many places of the code the class Apache_Solr_Response and the property Apache_Solr_Response::reponse is used.
|
||||
* To be able to refactor this we need to have a replacement for Apache_Solr_Response that behaves like the original class,
|
||||
* to keep the old code working. This allows us to drop the old code of SolrPhpClient and refactore the other parts step by step.
|
||||
*
|
||||
* Class ResponseAdapter
|
||||
*
|
||||
* Search response
|
||||
*
|
||||
* @property \stdClass facet_counts
|
||||
* @property \stdClass facets
|
||||
* @property \stdClass spellcheck
|
||||
* @property \stdClass response
|
||||
* @property \stdClass responseHeader
|
||||
* @property \stdClass highlighting
|
||||
* @property \stdClass debug
|
||||
* @property \stdClass lucene
|
||||
* @property string file
|
||||
* @property array file_metadata
|
||||
*
|
||||
* Luke response
|
||||
*
|
||||
* @property \stdClass index
|
||||
* @property \stdClass fields
|
||||
*/
|
||||
class ResponseAdapter implements Countable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $responseBody;
|
||||
|
||||
/**
|
||||
* @var \stdClass
|
||||
*/
|
||||
protected $data = null;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $httpStatus = 200;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $httpStatusMessage = '';
|
||||
|
||||
/**
|
||||
* ResponseAdapter constructor.
|
||||
*
|
||||
* @param string $responseBody
|
||||
* @param int $httpStatus
|
||||
* @param string $httpStatusMessage
|
||||
*/
|
||||
public function __construct($responseBody, $httpStatus = 500, $httpStatusMessage = '')
|
||||
{
|
||||
$this->data = json_decode($responseBody);
|
||||
$this->responseBody = $responseBody;
|
||||
$this->httpStatus = $httpStatus;
|
||||
$this->httpStatusMessage = $httpStatusMessage;
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
if (isset($this->data->response) && is_array($this->data->response->docs)) {
|
||||
$documents = array();
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
foreach ($this->data->response->docs as $originalDocument) {
|
||||
$fields = get_object_vars($originalDocument);
|
||||
$document = new Document($fields);
|
||||
$documents[] = $document;
|
||||
}
|
||||
|
||||
// @extensionScannerIgnoreLine
|
||||
$this->data->response->docs = $documents;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get to expose the parsed data and to lazily load it
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
if (isset($this->data->$key)) {
|
||||
return $this->data->$key;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic function for isset function on parsed data
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->data->$key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParsedData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRawResponse()
|
||||
{
|
||||
return $this->responseBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getHttpStatus(): int
|
||||
{
|
||||
return $this->httpStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getHttpStatusMessage(): string
|
||||
{
|
||||
return $this->httpStatusMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the elements of
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return count(get_object_vars($this->data));
|
||||
}
|
||||
}
|
77
Classes/System/Solr/Schema/Schema.php
Normal file
77
Classes/System/Solr/Schema/Schema.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Schema;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Object representation of the solr schema.
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class Schema
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $language = 'english';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLanguage()
|
||||
{
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $language
|
||||
*/
|
||||
public function setLanguage($language)
|
||||
{
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
451
Classes/System/Solr/Service/AbstractSolrService.php
Normal file
451
Classes/System/Solr/Service/AbstractSolrService.php
Normal file
@@ -0,0 +1,451 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Service;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2017 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\PingFailedException;
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
use WapplerSystems\Meilisearch\System\Logging\SolrLogManager;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use Solarium\Client;
|
||||
use Solarium\Core\Client\Endpoint;
|
||||
use Solarium\Core\Client\Request;
|
||||
use Solarium\Core\Query\QueryInterface;
|
||||
use Solarium\Exception\HttpException;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
abstract class AbstractSolrService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $pingCache = [];
|
||||
|
||||
/**
|
||||
* @var TypoScriptConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var \WapplerSystems\Meilisearch\System\Logging\SolrLogManager
|
||||
*/
|
||||
protected $logger = null;
|
||||
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
protected $client = null;
|
||||
|
||||
/**
|
||||
* SolrReadService constructor.
|
||||
*/
|
||||
public function __construct(Client $client, $typoScriptConfiguration = null, $logManager = null)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->configuration = $typoScriptConfiguration ?? Util::getSolrConfiguration();
|
||||
$this->logger = $logManager ?? GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the core solr path + core path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCorePath()
|
||||
{
|
||||
$endpoint = $this->getPrimaryEndpoint();
|
||||
return is_null($endpoint) ? '' : $endpoint->getPath() .'/'. $endpoint->getCore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Solarium client
|
||||
*
|
||||
* @return ?Client
|
||||
*/
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a valid http URL given this server's host, port and path and a provided servlet name
|
||||
*
|
||||
* @param string $servlet
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
protected function _constructUrl($servlet, $params = [])
|
||||
{
|
||||
$queryString = count($params) ? '?' . http_build_query($params, null, '&') : '';
|
||||
return $this->__toString() . $servlet . $queryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of the Solr connection. Specifically
|
||||
* will return the Solr URL.
|
||||
*
|
||||
* @return string The Solr URL.
|
||||
* @TODO: Add support for API version 2
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$endpoint = $this->getPrimaryEndpoint();
|
||||
if (!$endpoint instanceof Endpoint) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return $endpoint->getCoreBaseUri();
|
||||
} catch (\Exception $exception) {
|
||||
}
|
||||
return $endpoint->getScheme(). '://' . $endpoint->getHost() . ':' . $endpoint->getPort() . $endpoint->getPath() . '/' . $endpoint->getCore() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Endpoint|null
|
||||
*/
|
||||
public function getPrimaryEndpoint()
|
||||
{
|
||||
return is_array($this->client->getEndpoints()) ? reset($this->client->getEndpoints()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Central method for making a get operation against this Solr Server
|
||||
*
|
||||
* @param string $url
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function _sendRawGet($url)
|
||||
{
|
||||
return $this->_sendRawRequest($url, Request::METHOD_GET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Central method for making a HTTP DELETE operation against the Solr server
|
||||
*
|
||||
* @param string $url
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function _sendRawDelete($url)
|
||||
{
|
||||
return $this->_sendRawRequest($url, Request::METHOD_DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Central method for making a post operation against this Solr Server
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $rawPost
|
||||
* @param string $contentType
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function _sendRawPost($url, $rawPost, $contentType = 'text/xml; charset=UTF-8')
|
||||
{
|
||||
$initializeRequest = function(Request $request) use ($rawPost, $contentType) {
|
||||
$request->setRawData($rawPost);
|
||||
$request->addHeader('Content-Type: ' . $contentType);
|
||||
return $request;
|
||||
};
|
||||
|
||||
return $this->_sendRawRequest($url, Request::METHOD_POST, $rawPost, $initializeRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that performs an http request with the solarium client.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @param string $body
|
||||
* @param ?\Closure $initializeRequest
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function _sendRawRequest(
|
||||
string $url,
|
||||
$method = Request::METHOD_GET,
|
||||
$body = '',
|
||||
\Closure $initializeRequest = null
|
||||
) {
|
||||
$logSeverity = SolrLogManager::INFO;
|
||||
$exception = null;
|
||||
$url = $this->reviseUrl($url);
|
||||
try {
|
||||
$request = $this->buildSolariumRequestFromUrl($url, $method);
|
||||
if($initializeRequest !== null) {
|
||||
$request = $initializeRequest($request);
|
||||
}
|
||||
$response = $this->executeRequest($request);
|
||||
} catch (HttpException $exception) {
|
||||
$logSeverity = SolrLogManager::ERROR;
|
||||
$response = new ResponseAdapter($exception->getBody(), $exception->getCode(), $exception->getMessage());
|
||||
}
|
||||
|
||||
if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) {
|
||||
$message = 'Querying Solr using '.$method;
|
||||
$this->writeLog($logSeverity, $message, $url, $response, $exception, $body);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revise url
|
||||
* - Resolve relative paths
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function reviseUrl(string $url): string
|
||||
{
|
||||
/* @var Uri $uri */
|
||||
$uri = GeneralUtility::makeInstance(Uri::class, $url);
|
||||
|
||||
if ((string)$uri->getPath() === '') {
|
||||
return $url;
|
||||
}
|
||||
|
||||
$path = trim($uri->getPath(), '/');
|
||||
$pathsCurrent = explode('/', $path);
|
||||
$pathNew = [];
|
||||
foreach ($pathsCurrent as $pathCurrent) {
|
||||
if ($pathCurrent === '..') {
|
||||
array_pop($pathNew);
|
||||
continue;
|
||||
}
|
||||
if ($pathCurrent === '.') {
|
||||
continue;
|
||||
}
|
||||
$pathNew[] = $pathCurrent;
|
||||
}
|
||||
|
||||
$uri = $uri->withPath(implode('/', $pathNew));
|
||||
return (string)$uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the log data and writes the message to the log
|
||||
*
|
||||
* @param integer $logSeverity
|
||||
* @param string $message
|
||||
* @param string $url
|
||||
* @param ResponseAdapter $solrResponse
|
||||
* @param ?\Exception $exception
|
||||
* @param string $contentSend
|
||||
*/
|
||||
protected function writeLog($logSeverity, $message, $url, $solrResponse, $exception = null, $contentSend = '')
|
||||
{
|
||||
$logData = $this->buildLogDataFromResponse($solrResponse, $exception, $url, $contentSend);
|
||||
$this->logger->log($logSeverity, $message, $logData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the solr information to build data for the logger.
|
||||
*
|
||||
* @param ResponseAdapter $solrResponse
|
||||
* @param ?\Exception $e
|
||||
* @param string $url
|
||||
* @param string $contentSend
|
||||
* @return array
|
||||
*/
|
||||
protected function buildLogDataFromResponse(ResponseAdapter $solrResponse, \Exception $e = null, $url = '', $contentSend = '')
|
||||
{
|
||||
$logData = ['query url' => $url, 'response' => (array)$solrResponse];
|
||||
|
||||
if ($contentSend !== '') {
|
||||
$logData['content'] = $contentSend;
|
||||
}
|
||||
|
||||
if (!empty($e)) {
|
||||
$logData['exception'] = $e->__toString();
|
||||
return $logData;
|
||||
} else {
|
||||
// trigger data parsing
|
||||
// @extensionScannerIgnoreLine
|
||||
$solrResponse->response;
|
||||
$logData['response data'] = print_r($solrResponse, true);
|
||||
return $logData;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the /admin/ping servlet, can be used to quickly tell if a connection to the
|
||||
* server is available.
|
||||
*
|
||||
* Simply overrides the SolrPhpClient implementation, changing ping from a
|
||||
* HEAD to a GET request, see http://forge.typo3.org/issues/44167
|
||||
*
|
||||
* Also does not report the time, see https://forge.typo3.org/issues/64551
|
||||
*
|
||||
* @param boolean $useCache indicates if the ping result should be cached in the instance or not
|
||||
* @return bool TRUE if Solr can be reached, FALSE if not
|
||||
*/
|
||||
public function ping($useCache = true)
|
||||
{
|
||||
try {
|
||||
$httpResponse = $this->performPingRequest($useCache);
|
||||
} catch (HttpException $exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ($httpResponse->getHttpStatus() === 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the /admin/ping servlet, can be used to get the runtime of a ping request.
|
||||
*
|
||||
* @param boolean $useCache indicates if the ping result should be cached in the instance or not
|
||||
* @return double runtime in milliseconds
|
||||
* @throws \WapplerSystems\Meilisearch\PingFailedException
|
||||
*/
|
||||
public function getPingRoundTripRuntime($useCache = true)
|
||||
{
|
||||
try {
|
||||
$start = $this->getMilliseconds();
|
||||
$httpResponse = $this->performPingRequest($useCache);
|
||||
$end = $this->getMilliseconds();
|
||||
} catch (HttpException $e) {
|
||||
$message = 'Solr ping failed with unexpected response code: ' . $e->getCode();
|
||||
/** @var $exception \WapplerSystems\Meilisearch\PingFailedException */
|
||||
$exception = GeneralUtility::makeInstance(PingFailedException::class, /** @scrutinizer ignore-type */ $message);
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if ($httpResponse->getHttpStatus() !== 200) {
|
||||
$message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getHttpStatus();
|
||||
/** @var $exception \WapplerSystems\Meilisearch\PingFailedException */
|
||||
$exception = GeneralUtility::makeInstance(PingFailedException::class, /** @scrutinizer ignore-type */ $message);
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $end - $start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a ping request and returns the result.
|
||||
*
|
||||
* @param boolean $useCache indicates if the ping result should be cached in the instance or not
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function performPingRequest($useCache = true)
|
||||
{
|
||||
$cacheKey = (string)($this);
|
||||
if ($useCache && isset(static::$pingCache[$cacheKey])) {
|
||||
return static::$pingCache[$cacheKey];
|
||||
}
|
||||
|
||||
$pingQuery = $this->client->createPing();
|
||||
$pingResult = $this->createAndExecuteRequest($pingQuery);
|
||||
|
||||
if ($useCache) {
|
||||
static::$pingCache[$cacheKey] = $pingResult;
|
||||
}
|
||||
|
||||
return $pingResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time in milliseconds.
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
protected function getMilliseconds()
|
||||
{
|
||||
return GeneralUtility::milliseconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryInterface $query
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function createAndExecuteRequest(QueryInterface $query): ResponseAdapter
|
||||
{
|
||||
$request = $this->client->createRequest($query);
|
||||
return $this->executeRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $request
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
protected function executeRequest($request): ResponseAdapter
|
||||
{
|
||||
$result = $this->client->executeRequest($request);
|
||||
return new ResponseAdapter($result->getBody(), $result->getStatusCode(), $result->getStatusMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the request for Solarium.
|
||||
*
|
||||
* Important: The endpoint already contains the API information.
|
||||
* The internal Solarium will append the information including the core if set.
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $httpMethod
|
||||
* @return Request
|
||||
*/
|
||||
protected function buildSolariumRequestFromUrl(string $url, $httpMethod = Request::METHOD_GET): Request
|
||||
{
|
||||
$params = [];
|
||||
parse_str(parse_url($url, PHP_URL_QUERY), $params);
|
||||
$request = new Request();
|
||||
$path = parse_url($url, PHP_URL_PATH);
|
||||
$endpoint = $this->getPrimaryEndpoint();
|
||||
$api = $request->getApi() === Request::API_V1 ? 'solr' : 'api';
|
||||
$coreBasePath = $endpoint->getPath() . '/' . $api . '/' . $endpoint->getCore() . '/';
|
||||
|
||||
$handler = $this->buildRelativePath($coreBasePath, $path);
|
||||
$request->setMethod($httpMethod);
|
||||
$request->setParams($params);
|
||||
$request->setHandler($handler);
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a relative path from base path to target path.
|
||||
* Required since Solarium contains the core information
|
||||
*
|
||||
* @param string $basePath
|
||||
* @param string $targetPath
|
||||
* @return string
|
||||
*/
|
||||
protected function buildRelativePath(string $basePath, string $targetPath): string
|
||||
{
|
||||
$basePath = trim($basePath, '/');
|
||||
$targetPath = trim($targetPath, '/');
|
||||
$baseElements = explode('/', $basePath);
|
||||
$targetElements = explode('/', $targetPath);
|
||||
$targetSegment = array_pop($targetElements);
|
||||
foreach ($baseElements as $i => $segment) {
|
||||
if (isset($targetElements[$i]) && $segment === $targetElements[$i]) {
|
||||
unset($baseElements[$i], $targetElements[$i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$targetElements[] = $targetSegment;
|
||||
return str_repeat('../', count($baseElements)) . implode('/', $targetElements);
|
||||
}
|
||||
}
|
390
Classes/System/Solr/Service/SolrAdminService.php
Normal file
390
Classes/System/Solr/Service/SolrAdminService.php
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Service;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2017 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
use WapplerSystems\Meilisearch\System\Logging\SolrLogManager;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\SchemaParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\StopWordParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\SynonymParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Schema\Schema;
|
||||
use Solarium\Client;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class SolrAdminService
|
||||
*/
|
||||
class SolrAdminService extends AbstractSolrService
|
||||
{
|
||||
const PLUGINS_SERVLET = 'admin/plugins';
|
||||
const LUKE_SERVLET = 'admin/luke';
|
||||
const SYSTEM_SERVLET = 'admin/system';
|
||||
const CORES_SERVLET = '../admin/cores';
|
||||
const FILE_SERVLET = 'admin/file';
|
||||
const SCHEMA_SERVLET = 'schema';
|
||||
const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
|
||||
const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $lukeData = [];
|
||||
|
||||
protected $systemData = null;
|
||||
|
||||
protected $pluginsData = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $solrconfigName;
|
||||
|
||||
/**
|
||||
* @var SchemaParser
|
||||
*/
|
||||
protected $schemaParser = null;
|
||||
|
||||
/**
|
||||
* @var Schema
|
||||
*/
|
||||
protected $schema;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_synonymsUrl;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $_stopWordsUrl;
|
||||
|
||||
/**
|
||||
* @var SynonymParser
|
||||
*/
|
||||
protected $synonymParser = null;
|
||||
|
||||
/**
|
||||
* @var StopWordParser
|
||||
*/
|
||||
protected $stopWordParser = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param TypoScriptConfiguration $typoScriptConfiguration
|
||||
* @param SynonymParser $synonymParser
|
||||
* @param StopWordParser $stopWordParser
|
||||
* @param SchemaParser $schemaParser
|
||||
* @param SolrLogManager $logManager
|
||||
*/
|
||||
public function __construct(
|
||||
Client $client,
|
||||
TypoScriptConfiguration $typoScriptConfiguration = null,
|
||||
SolrLogManager $logManager = null,
|
||||
SynonymParser $synonymParser = null,
|
||||
StopWordParser $stopWordParser = null,
|
||||
SchemaParser $schemaParser = null
|
||||
)
|
||||
{
|
||||
parent::__construct($client, $typoScriptConfiguration);
|
||||
|
||||
$this->synonymParser = $synonymParser ?? GeneralUtility::makeInstance(SynonymParser::class);
|
||||
$this->stopWordParser = $stopWordParser ?? GeneralUtility::makeInstance(StopWordParser::class);
|
||||
$this->schemaParser = $schemaParser ?? GeneralUtility::makeInstance(SchemaParser::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the /admin/system servlet and retrieve system information about Solr
|
||||
*
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function system()
|
||||
{
|
||||
return $this->_sendRawGet($this->_constructUrl(self::SYSTEM_SERVLET, ['wt' => 'json']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the plugins installed in Solr
|
||||
*
|
||||
* @return array A nested array of plugin data.
|
||||
*/
|
||||
public function getPluginsInformation()
|
||||
{
|
||||
if (count($this->pluginsData) == 0) {
|
||||
$url = $this->_constructUrl(self::PLUGINS_SERVLET, ['wt' => 'json']);
|
||||
$pluginsInformation = $this->_sendRawGet($url);
|
||||
|
||||
// access a random property to trigger response parsing
|
||||
$pluginsInformation->responseHeader;
|
||||
$this->pluginsData = $pluginsInformation;
|
||||
}
|
||||
|
||||
return $this->pluginsData;
|
||||
}
|
||||
|
||||
/**
|
||||
* get field meta data for the index
|
||||
*
|
||||
* @param int $numberOfTerms Number of top terms to fetch for each field
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function getFieldsMetaData($numberOfTerms = 0)
|
||||
{
|
||||
return $this->getLukeMetaData($numberOfTerms)->fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves meta data about the index from the luke request handler
|
||||
*
|
||||
* @param int $numberOfTerms Number of top terms to fetch for each field
|
||||
* @return ResponseAdapter Index meta data
|
||||
*/
|
||||
public function getLukeMetaData($numberOfTerms = 0)
|
||||
{
|
||||
if (!isset($this->lukeData[$numberOfTerms])) {
|
||||
$lukeUrl = $this->_constructUrl(
|
||||
self::LUKE_SERVLET, ['numTerms' => $numberOfTerms, 'wt' => 'json', 'fl' => '*']
|
||||
);
|
||||
|
||||
$this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
|
||||
}
|
||||
|
||||
return $this->lukeData[$numberOfTerms];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets information about the Solr server
|
||||
*
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function getSystemInformation()
|
||||
{
|
||||
if (empty($this->systemData)) {
|
||||
$systemInformation = $this->system();
|
||||
|
||||
// access a random property to trigger response parsing
|
||||
$systemInformation->responseHeader;
|
||||
$this->systemData = $systemInformation;
|
||||
}
|
||||
|
||||
return $this->systemData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the solrconfig.xml file installed and in use on the Solr
|
||||
* server.
|
||||
*
|
||||
* @return string Name of the active solrconfig.xml
|
||||
*/
|
||||
public function getSolrconfigName()
|
||||
{
|
||||
if (is_null($this->solrconfigName)) {
|
||||
$solrconfigXmlUrl = $this->_constructUrl(self::FILE_SERVLET, ['file' => 'solrconfig.xml']);
|
||||
$response = $this->_sendRawGet($solrconfigXmlUrl);
|
||||
$solrconfigXml = simplexml_load_string($response->getRawResponse());
|
||||
if ($solrconfigXml === false) {
|
||||
throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl);
|
||||
}
|
||||
$this->solrconfigName = (string)$solrconfigXml->attributes()->name;
|
||||
}
|
||||
|
||||
return $this->solrconfigName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Solr server's version number.
|
||||
*
|
||||
* @return string Solr version number
|
||||
*/
|
||||
public function getSolrServerVersion()
|
||||
{
|
||||
$systemInformation = $this->getSystemInformation();
|
||||
// don't know why $systemInformation->lucene->solr-spec-version won't work
|
||||
$luceneInformation = (array)$systemInformation->lucene;
|
||||
return $luceneInformation['solr-spec-version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the current core
|
||||
*
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function reloadCore()
|
||||
{
|
||||
$response = $this->reloadCoreByName($this->getPrimaryEndpoint()->getCore());
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads a core of the connection by a given corename.
|
||||
*
|
||||
* @param string $coreName
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function reloadCoreByName($coreName)
|
||||
{
|
||||
$coreAdminReloadUrl = $this->_constructUrl(self::CORES_SERVLET) . '?action=reload&core=' . $coreName;
|
||||
$response = $this->_sendRawGet($coreAdminReloadUrl);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured schema for the current core.
|
||||
*
|
||||
* @return Schema
|
||||
*/
|
||||
public function getSchema()
|
||||
{
|
||||
if ($this->schema !== null) {
|
||||
return $this->schema;
|
||||
}
|
||||
$response = $this->_sendRawGet($this->_constructUrl(self::SCHEMA_SERVLET));
|
||||
|
||||
$this->schema = $this->schemaParser->parseJson($response->getRawResponse());
|
||||
return $this->schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currently configured synonyms
|
||||
*
|
||||
* @param string $baseWord If given a base word, retrieves the synonyms for that word only
|
||||
* @return array
|
||||
*/
|
||||
public function getSynonyms($baseWord = '')
|
||||
{
|
||||
$this->initializeSynonymsUrl();
|
||||
$synonymsUrl = $this->_synonymsUrl;
|
||||
if (!empty($baseWord)) {
|
||||
$synonymsUrl .= '/' . $baseWord;
|
||||
}
|
||||
|
||||
$response = $this->_sendRawGet($synonymsUrl);
|
||||
return $this->synonymParser->parseJson($baseWord, $response->getRawResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add list of synonyms for base word to managed synonyms map
|
||||
*
|
||||
* @param string $baseWord
|
||||
* @param array $synonyms
|
||||
*
|
||||
* @return ResponseAdapter
|
||||
*
|
||||
* @throws \InvalidArgumentException If $baseWord or $synonyms are empty
|
||||
*/
|
||||
public function addSynonym($baseWord, array $synonyms)
|
||||
{
|
||||
$this->initializeSynonymsUrl();
|
||||
$json = $this->synonymParser->toJson($baseWord, $synonyms);
|
||||
$response = $this->_sendRawPost($this->_synonymsUrl, $json, 'application/json');
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a synonym from the synonyms map
|
||||
*
|
||||
* @param string $baseWord
|
||||
* @return ResponseAdapter
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function deleteSynonym($baseWord)
|
||||
{
|
||||
$this->initializeSynonymsUrl();
|
||||
if (empty($baseWord)) {
|
||||
throw new \InvalidArgumentException('Must provide base word.');
|
||||
}
|
||||
|
||||
$response = $this->_sendRawDelete($this->_synonymsUrl . '/' . urlencode($baseWord));
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get currently configured stop words
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStopWords()
|
||||
{
|
||||
$this->initializeStopWordsUrl();
|
||||
$response = $this->_sendRawGet($this->_stopWordsUrl);
|
||||
return $this->stopWordParser->parseJson($response->getRawResponse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds stop words to the managed stop word list
|
||||
*
|
||||
* @param array|string $stopWords string for a single word, array for multiple words
|
||||
* @return ResponseAdapter
|
||||
* @throws \InvalidArgumentException If $stopWords is empty
|
||||
*/
|
||||
public function addStopWords($stopWords)
|
||||
{
|
||||
$this->initializeStopWordsUrl();
|
||||
$json = $this->stopWordParser->toJson($stopWords);
|
||||
return $this->_sendRawPost($this->_stopWordsUrl, $json, 'application/json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a words from the managed stop word list
|
||||
*
|
||||
* @param string $stopWord stop word to delete
|
||||
* @return ResponseAdapter
|
||||
* @throws \InvalidArgumentException If $stopWords is empty
|
||||
*/
|
||||
public function deleteStopWord($stopWord)
|
||||
{
|
||||
$this->initializeStopWordsUrl();
|
||||
if (empty($stopWord)) {
|
||||
throw new \InvalidArgumentException('Must provide stop word.');
|
||||
}
|
||||
|
||||
return $this->_sendRawDelete($this->_stopWordsUrl . '/' . urlencode($stopWord));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function initializeSynonymsUrl()
|
||||
{
|
||||
if (trim($this->_synonymsUrl) !== '') {
|
||||
return;
|
||||
}
|
||||
$this->_synonymsUrl = $this->_constructUrl(self::SYNONYMS_SERVLET) . $this->getSchema()->getLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
protected function initializeStopWordsUrl()
|
||||
{
|
||||
if (trim($this->_stopWordsUrl) !== '') {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_stopWordsUrl = $this->_constructUrl(self::STOPWORDS_SERVLET) . $this->getSchema()->getLanguage();
|
||||
}
|
||||
}
|
120
Classes/System/Solr/Service/SolrReadService.php
Normal file
120
Classes/System/Solr/Service/SolrReadService.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Service;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2017 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\Domain\Search\Query\Query;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
use WapplerSystems\Meilisearch\System\Solr\SolrCommunicationException;
|
||||
use WapplerSystems\Meilisearch\System\Solr\SolrInternalServerErrorException;
|
||||
use WapplerSystems\Meilisearch\System\Solr\SolrUnavailableException;
|
||||
use Solarium\Exception\HttpException;
|
||||
|
||||
/**
|
||||
* Class SolrReadService
|
||||
*/
|
||||
class SolrReadService extends AbstractSolrService
|
||||
{
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $hasSearched = false;
|
||||
|
||||
/**
|
||||
* @var ResponseAdapter
|
||||
*/
|
||||
protected $responseCache = null;
|
||||
|
||||
/**
|
||||
* Performs a search.
|
||||
*
|
||||
* @param Query $query
|
||||
* @return ResponseAdapter Solr response
|
||||
* @throws \RuntimeException if Solr returns a HTTP status code other than 200
|
||||
*/
|
||||
public function search($query)
|
||||
{
|
||||
try {
|
||||
$request = $this->client->createRequest($query);
|
||||
$response = $this->executeRequest($request);
|
||||
$this->hasSearched = true;
|
||||
$this->responseCache = $response;
|
||||
} catch (HttpException $e) {
|
||||
$this->handleErrorResponses($e);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a search has been executed or not.
|
||||
*
|
||||
* @return bool TRUE if a search has been executed, FALSE otherwise
|
||||
*/
|
||||
public function hasSearched()
|
||||
{
|
||||
return $this->hasSearched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the most recent response (if any)
|
||||
*
|
||||
* @return ResponseAdapter Most recent response, or NULL if a search has not been executed yet.
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->responseCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method maps the failed solr requests to a meaningful exception.
|
||||
*
|
||||
* @param HttpException $exception
|
||||
* @throws SolrCommunicationException
|
||||
* @return HttpException
|
||||
*/
|
||||
protected function handleErrorResponses(HttpException $exception)
|
||||
{
|
||||
$status = $exception->getCode();
|
||||
$message = $exception->getStatusMessage();
|
||||
$solrRespone = new ResponseAdapter($exception->getBody());
|
||||
|
||||
if ($status === 0 || $status === 502) {
|
||||
$e = new SolrUnavailableException('Solr Server not available: ' . $message, 1505989391);
|
||||
$e->setSolrResponse($solrRespone);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($status === 500) {
|
||||
$e = new SolrInternalServerErrorException('Internal Server error during search: ' . $message, 1505989897);
|
||||
$e->setSolrResponse($solrRespone);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$e = new SolrCommunicationException('Invalid query. Solr returned an error: ' . $status . ' ' . $message, 1293109870);
|
||||
$e->setSolrResponse($solrRespone);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
121
Classes/System/Solr/Service/SolrWriteService.php
Normal file
121
Classes/System/Solr/Service/SolrWriteService.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr\Service;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2017 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Logging\SolrLogManager;
|
||||
use WapplerSystems\Meilisearch\System\Solr\ResponseAdapter;
|
||||
use Solarium\QueryType\Extract\Query;
|
||||
|
||||
/**
|
||||
* Class SolrWriteService
|
||||
*/
|
||||
class SolrWriteService extends AbstractSolrService
|
||||
{
|
||||
const EXTRACT_SERVLET = 'update/extract';
|
||||
|
||||
/**
|
||||
* Performs a content and meta data extraction request.
|
||||
*
|
||||
* @param Query $query An extraction query
|
||||
* @return array An array containing the extracted content [0] and meta data [1]
|
||||
*/
|
||||
public function extractByQuery(Query $query)
|
||||
{
|
||||
try {
|
||||
$response = $this->createAndExecuteRequest($query);
|
||||
return [$response->file, (array)$response->file_metadata];
|
||||
} catch (\Exception $e) {
|
||||
$param = $query->getRequestBuilder()->build($query)->getParams();
|
||||
$this->logger->log(
|
||||
SolrLogManager::ERROR,
|
||||
'Extracting text and meta data through Solr Cell over HTTP POST',
|
||||
[
|
||||
'query' => (array)$query,
|
||||
'parameters' => $param,
|
||||
'file' => $query->getFile(),
|
||||
'query url' => self::EXTRACT_SERVLET,
|
||||
'exception' => $e->getMessage()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all index documents of a certain type and does a commit
|
||||
* afterwards.
|
||||
*
|
||||
* @param string $type The type of documents to delete, usually a table name.
|
||||
* @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE
|
||||
*/
|
||||
public function deleteByType($type, $commit = true)
|
||||
{
|
||||
$this->deleteByQuery('type:' . trim($type));
|
||||
|
||||
if ($commit) {
|
||||
$this->commit(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a delete document based on a query and submit it
|
||||
*
|
||||
* @param string $rawQuery Expected to be utf-8 encoded
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function deleteByQuery($rawQuery) {
|
||||
$query = $this->client->createUpdate();
|
||||
$query->addDeleteQuery($rawQuery);
|
||||
return $this->createAndExecuteRequest($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an array of Solr Documents to the index all at once
|
||||
*
|
||||
* @param array $documents Should be an array of \WapplerSystems\Meilisearch\System\Solr\Document\Document instances
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function addDocuments($documents)
|
||||
{
|
||||
$update = $this->client->createUpdate();
|
||||
$update->addDocuments($documents);
|
||||
return $this->createAndExecuteRequest($update);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a commit command. Will be synchronous unless both wait parameters are set to false.
|
||||
*
|
||||
* @param boolean $expungeDeletes Defaults to false, merge segments with deletes away
|
||||
* @param boolean $waitSearcher Defaults to true, block until a new searcher is opened and registered as the main query searcher, making the changes visible
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function commit($expungeDeletes = false, $waitSearcher = true)
|
||||
{
|
||||
$update = $this->client->createUpdate();
|
||||
$update->addCommit(false, $waitSearcher, $expungeDeletes);
|
||||
return $this->createAndExecuteRequest($update);
|
||||
}
|
||||
}
|
52
Classes/System/Solr/SolrCommunicationException.php
Normal file
52
Classes/System/Solr/SolrCommunicationException.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* This exception or a more specific one should be thrown when the is an error in the communication with the solr server.
|
||||
*/
|
||||
class SolrCommunicationException extends \RuntimeException {
|
||||
|
||||
/**
|
||||
* @var ResponseAdapter
|
||||
*/
|
||||
protected $solrResponse;
|
||||
|
||||
/**
|
||||
* @return ResponseAdapter
|
||||
*/
|
||||
public function getSolrResponse(): ResponseAdapter
|
||||
{
|
||||
return $this->solrResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ResponseAdapter $solrResponse
|
||||
*/
|
||||
public function setSolrResponse(ResponseAdapter $solrResponse)
|
||||
{
|
||||
$this->solrResponse = $solrResponse;
|
||||
}
|
||||
}
|
311
Classes/System/Solr/SolrConnection.php
Normal file
311
Classes/System/Solr/SolrConnection.php
Normal file
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2009-2015 Ingo Renner <ingo@typo3.org>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
|
||||
use WapplerSystems\Meilisearch\System\Logging\SolrLogManager;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\SchemaParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\StopWordParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Parser\SynonymParser;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Service\SolrAdminService;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Service\SolrReadService;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Service\SolrWriteService;
|
||||
use WapplerSystems\Meilisearch\Util;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Solarium\Client;
|
||||
use Solarium\Core\Client\Adapter\Psr18Adapter;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Solr Service Access
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class SolrConnection
|
||||
{
|
||||
/**
|
||||
* @var SolrAdminService
|
||||
*/
|
||||
protected $adminService;
|
||||
|
||||
/**
|
||||
* @var SolrReadService
|
||||
*/
|
||||
protected $readService;
|
||||
|
||||
/**
|
||||
* @var SolrWriteService
|
||||
*/
|
||||
protected $writeService;
|
||||
|
||||
/**
|
||||
* @var TypoScriptConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var SynonymParser
|
||||
*/
|
||||
protected $synonymParser = null;
|
||||
|
||||
/**
|
||||
* @var StopWordParser
|
||||
*/
|
||||
protected $stopWordParser = null;
|
||||
|
||||
/**
|
||||
* @var SchemaParser
|
||||
*/
|
||||
protected $schemaParser = null;
|
||||
|
||||
/**
|
||||
* @var Node[]
|
||||
*/
|
||||
protected $nodes = [];
|
||||
|
||||
/**
|
||||
* @var SolrLogManager
|
||||
*/
|
||||
protected $logger = null;
|
||||
|
||||
/**
|
||||
* @var ClientInterface[]
|
||||
*/
|
||||
protected $clients = [];
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
protected $psr7Client;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
protected $requestFactory;
|
||||
|
||||
/**
|
||||
* @var StreamFactoryInterface
|
||||
*/
|
||||
protected $streamFactory;
|
||||
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Node $readNode
|
||||
* @param Node $writeNode
|
||||
* @param ?TypoScriptConfiguration $configuration
|
||||
* @param ?SynonymParser $synonymParser
|
||||
* @param ?StopWordParser $stopWordParser
|
||||
* @param ?SchemaParser $schemaParser
|
||||
* @param ?SolrLogManager $logManager
|
||||
* @param ?ClientInterface $psr7Client
|
||||
* @param ?RequestFactoryInterface $requestFactory
|
||||
* @param ?StreamFactoryInterface $streamFactory
|
||||
* @param ?EventDispatcherInterface $eventDispatcher
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function __construct(
|
||||
Node $readNode,
|
||||
Node $writeNode,
|
||||
TypoScriptConfiguration $configuration = null,
|
||||
SynonymParser $synonymParser = null,
|
||||
StopWordParser $stopWordParser = null,
|
||||
SchemaParser $schemaParser = null,
|
||||
SolrLogManager $logManager = null,
|
||||
ClientInterface $psr7Client = null,
|
||||
RequestFactoryInterface $requestFactory = null,
|
||||
StreamFactoryInterface $streamFactory = null,
|
||||
EventDispatcherInterface $eventDispatcher = null
|
||||
) {
|
||||
$this->nodes['read'] = $readNode;
|
||||
$this->nodes['write'] = $writeNode;
|
||||
$this->nodes['admin'] = $writeNode;
|
||||
$this->configuration = $configuration ?? Util::getSolrConfiguration();
|
||||
$this->synonymParser = $synonymParser;
|
||||
$this->stopWordParser = $stopWordParser;
|
||||
$this->schemaParser = $schemaParser;
|
||||
$this->logger = $logManager;
|
||||
$this->psr7Client = $psr7Client ?? GeneralUtility::getContainer()->get(ClientInterface::class);
|
||||
$this->requestFactory = $requestFactory ?? GeneralUtility::getContainer()->get(RequestFactoryInterface::class);
|
||||
$this->streamFactory = $streamFactory ?? GeneralUtility::getContainer()->get(StreamFactoryInterface::class);
|
||||
$this->eventDispatcher = $eventDispatcher ?? GeneralUtility::getContainer()->get(EventDispatcherInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @return Node
|
||||
*/
|
||||
public function getNode(string $key): Node
|
||||
{
|
||||
return $this->nodes[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrAdminService
|
||||
*/
|
||||
public function getAdminService(): SolrAdminService
|
||||
{
|
||||
if ($this->adminService === null) {
|
||||
$this->adminService = $this->buildAdminService();
|
||||
}
|
||||
|
||||
return $this->adminService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrAdminService
|
||||
* @noinspection PhpIncompatibleReturnTypeInspection
|
||||
*/
|
||||
protected function buildAdminService(): SolrAdminService
|
||||
{
|
||||
$endpointKey = 'admin';
|
||||
$client = $this->getClient($endpointKey);
|
||||
$this->initializeClient($client, $endpointKey);
|
||||
return GeneralUtility::makeInstance(SolrAdminService::class, $client, $this->configuration, $this->logger, $this->synonymParser, $this->stopWordParser, $this->schemaParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrReadService
|
||||
*/
|
||||
public function getReadService(): SolrReadService
|
||||
{
|
||||
if ($this->readService === null) {
|
||||
$this->readService = $this->buildReadService();
|
||||
}
|
||||
|
||||
return $this->readService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrReadService
|
||||
* @noinspection PhpIncompatibleReturnTypeInspection
|
||||
*/
|
||||
protected function buildReadService(): SolrReadService
|
||||
{
|
||||
$endpointKey = 'read';
|
||||
$client = $this->getClient($endpointKey);
|
||||
$this->initializeClient($client, $endpointKey);
|
||||
return GeneralUtility::makeInstance(SolrReadService::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrWriteService
|
||||
*/
|
||||
public function getWriteService(): SolrWriteService
|
||||
{
|
||||
if ($this->writeService === null) {
|
||||
$this->writeService = $this->buildWriteService();
|
||||
}
|
||||
|
||||
return $this->writeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SolrWriteService
|
||||
* @noinspection PhpIncompatibleReturnTypeInspection
|
||||
*/
|
||||
protected function buildWriteService(): SolrWriteService
|
||||
{
|
||||
$endpointKey = 'write';
|
||||
$client = $this->getClient($endpointKey);
|
||||
$this->initializeClient($client, $endpointKey);
|
||||
return GeneralUtility::makeInstance(SolrWriteService::class, $client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $endpointKey
|
||||
* @return Client
|
||||
*/
|
||||
protected function initializeClient(Client $client, string $endpointKey): Client
|
||||
{
|
||||
if (trim($this->getNode($endpointKey)->getUsername()) === '') {
|
||||
return $client;
|
||||
}
|
||||
|
||||
$username = $this->getNode($endpointKey)->getUsername();
|
||||
$password = $this->getNode($endpointKey)->getPassword();
|
||||
$this->setAuthenticationOnAllEndpoints($client, $username, $password);
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
protected function setAuthenticationOnAllEndpoints(Client $client, string $username, string $password)
|
||||
{
|
||||
foreach ($client->getEndpoints() as $endpoint) {
|
||||
$endpoint->setAuthentication($username, $password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $endpointKey
|
||||
* @return Client
|
||||
*/
|
||||
protected function getClient(string $endpointKey): Client
|
||||
{
|
||||
if ($this->clients[$endpointKey]) {
|
||||
return $this->clients[$endpointKey];
|
||||
}
|
||||
|
||||
$adapter = new Psr18Adapter($this->psr7Client, $this->requestFactory, $this->streamFactory);
|
||||
|
||||
$client = new Client($adapter, $this->eventDispatcher);
|
||||
$client->getPlugin('postbigrequest');
|
||||
$client->clearEndpoints();
|
||||
|
||||
$newEndpointOptions = $this->getNode($endpointKey)->getSolariumClientOptions();
|
||||
$newEndpointOptions['key'] = $endpointKey;
|
||||
$client->createEndpoint($newEndpointOptions, true);
|
||||
|
||||
$this->clients[$endpointKey] = $client;
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Client $client
|
||||
* @param ?string $endpointKey
|
||||
*/
|
||||
public function setClient(Client $client, ?string $endpointKey = 'read')
|
||||
{
|
||||
$this->clients[$endpointKey] = $client;
|
||||
}
|
||||
}
|
31
Classes/System/Solr/SolrIncompleteResponseException.php
Normal file
31
Classes/System/Solr/SolrIncompleteResponseException.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* This exception should be thrown when the response from solr was incomplete
|
||||
*/
|
||||
class SolrIncompleteResponseException extends SolrCommunicationException {}
|
31
Classes/System/Solr/SolrInternalServerErrorException.php
Normal file
31
Classes/System/Solr/SolrInternalServerErrorException.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* This exception is used when the solr an 500 internal server error is thrown by the solr server
|
||||
*/
|
||||
class SolrInternalServerErrorException extends SolrCommunicationException {}
|
31
Classes/System/Solr/SolrUnavailableException.php
Normal file
31
Classes/System/Solr/SolrUnavailableException.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Solr;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* This exception is used when the solr server is unavailable.
|
||||
*/
|
||||
class SolrUnavailableException extends SolrCommunicationException {}
|
315
Classes/System/TCA/TCAService.php
Normal file
315
Classes/System/TCA/TCAService.php
Normal file
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\TCA;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2017 Timo Hund <timo.hund@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Class to encapsulate TCA specific logic
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class TCAService
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tca = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $visibilityAffectingFields = [];
|
||||
|
||||
/**
|
||||
* TCAService constructor.
|
||||
* @param array|null $TCA
|
||||
*/
|
||||
public function __construct($TCA = null)
|
||||
{
|
||||
$this->tca = (array)($TCA ?? $GLOBALS['TCA']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return integer
|
||||
*/
|
||||
protected function getTime()
|
||||
{
|
||||
return isset($GLOBALS['EXEC_TIME']) ? $GLOBALS['EXEC_TIME'] : time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a record is "enabled"
|
||||
*
|
||||
* A record is considered "enabled" if
|
||||
* - it is not hidden
|
||||
* - it is not deleted
|
||||
* - as a page it is not set to be excluded from search
|
||||
*
|
||||
* @param string $table The record's table name
|
||||
* @param array $record The record to check
|
||||
* @return bool TRUE if the record is enabled, FALSE otherwise
|
||||
*/
|
||||
public function isEnabledRecord($table, $record)
|
||||
{
|
||||
if (
|
||||
(empty($record))
|
||||
||
|
||||
(isset($this->tca[$table]['ctrl']['enablecolumns']['disabled']) && !empty($record[$this->tca[$table]['ctrl']['enablecolumns']['disabled']]))
|
||||
||
|
||||
(isset($this->tca[$table]['ctrl']['delete']) && !empty($record[$this->tca[$table]['ctrl']['delete']]))
|
||||
||
|
||||
($table === 'pages' && !empty($record['no_search']))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a end time field exists for the record's table and if so
|
||||
* determines if a time is set and whether that time is in the past,
|
||||
* making the record invisible on the website.
|
||||
*
|
||||
* @param string $table The table name.
|
||||
* @param array $record An array with record fields that may affect visibility.
|
||||
* @return bool True if the record's end time is in the past, FALSE otherwise.
|
||||
*/
|
||||
public function isEndTimeInPast($table, $record)
|
||||
{
|
||||
$endTimeInPast = false;
|
||||
|
||||
if (isset($this->tca[$table]['ctrl']['enablecolumns']['endtime'])) {
|
||||
$endTimeField = $this->tca[$table]['ctrl']['enablecolumns']['endtime'];
|
||||
if ($record[$endTimeField] > 0) {
|
||||
$endTimeInPast = $record[$endTimeField] < $this->getTime();
|
||||
}
|
||||
}
|
||||
|
||||
return $endTimeInPast;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to check if there is a configured key in
|
||||
*
|
||||
* $GLOBALS['TCA']['mytable']['ctrl']['enablecolumns']
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $GLOBALS['TCA']['mytable']]['ctrl']['enablecolumns']['fe_group'] = 'mygroupfield'
|
||||
*
|
||||
* ->isEnableColumn('mytable', 'fe_group') will return true, because 'mygroupfield' is
|
||||
* configured as column.
|
||||
*
|
||||
* @params string $table
|
||||
* @param string $columnName
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnableColumn($table, $columnName)
|
||||
{
|
||||
return (
|
||||
isset($GLOBALS['TCA'][$table]['ctrl']['enablecolumns']) &&
|
||||
array_key_exists($columnName, $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a start time field exists for the record's table and if so
|
||||
* determines if a time is set and whether that time is in the future,
|
||||
* making the record invisible on the website.
|
||||
*
|
||||
* @param string $table The table name.
|
||||
* @param array $record An array with record fields that may affect visibility.
|
||||
* @return bool True if the record's start time is in the future, FALSE otherwise.
|
||||
*/
|
||||
public function isStartTimeInFuture($table, $record)
|
||||
{
|
||||
$startTimeInFuture = false;
|
||||
|
||||
if (isset($this->tca[$table]['ctrl']['enablecolumns']['starttime'])) {
|
||||
$startTimeField = $this->tca[$table]['ctrl']['enablecolumns']['starttime'];
|
||||
$startTimeInFuture = $record[$startTimeField] > $this->getTime();
|
||||
}
|
||||
|
||||
return $startTimeInFuture;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a hidden field exists for the current table and if so
|
||||
* determines whether it is set on the current record.
|
||||
*
|
||||
* @param string $table The table name.
|
||||
* @param array $record An array with record fields that may affect visibility.
|
||||
* @return bool True if the record is hidden, FALSE otherwise.
|
||||
*/
|
||||
public function isHidden($table, $record)
|
||||
{
|
||||
$hidden = false;
|
||||
|
||||
if (isset($this->tca[$table]['ctrl']['enablecolumns']['disabled'])) {
|
||||
$hiddenField = $this->tca[$table]['ctrl']['enablecolumns']['disabled'];
|
||||
$hidden = (boolean)$record[$hiddenField];
|
||||
}
|
||||
|
||||
return $hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that "empty" frontend group fields are always the same value.
|
||||
*
|
||||
* @param string $table The record's table name.
|
||||
* @param array $record the record array.
|
||||
* @return array The cleaned record
|
||||
*/
|
||||
public function normalizeFrontendGroupField($table, $record)
|
||||
{
|
||||
if (isset($this->tca[$table]['ctrl']['enablecolumns']['fe_group'])) {
|
||||
$frontendGroupsField = $this->tca[$table]['ctrl']['enablecolumns']['fe_group'];
|
||||
|
||||
if ($record[$frontendGroupsField] == '') {
|
||||
$record[$frontendGroupsField] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $table
|
||||
* @param array $record
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTranslationOriginalUid($table, array $record)
|
||||
{
|
||||
return $record[$this->tca[$table]['ctrl']['transOrigPointerField']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the uid that as marked as original if the record is a translation if not it returns the
|
||||
* originalUid.
|
||||
*
|
||||
* @param $table
|
||||
* @param array $record
|
||||
* @param $originalUid
|
||||
* @return integer
|
||||
*/
|
||||
public function getTranslationOriginalUidIfTranslated($table, array $record, $originalUid)
|
||||
{
|
||||
if (!$this->isLocalizedRecord($table, $record)) {
|
||||
return $originalUid;
|
||||
}
|
||||
|
||||
return $this->getTranslationOriginalUid($table, $record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a record is a localization overlay.
|
||||
*
|
||||
* @param string $tableName The record's table name
|
||||
* @param array $record The record to check
|
||||
* @return bool TRUE if the record is a language overlay, FALSE otherwise
|
||||
*/
|
||||
public function isLocalizedRecord($tableName, array $record)
|
||||
{
|
||||
$translationUid = $this->getTranslationOriginalUid($tableName, $record);
|
||||
if (is_null($translationUid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasTranslationReference = $translationUid > 0;
|
||||
if (!$hasTranslationReference) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a list of visibility affecting fields of a table so that it can
|
||||
* be used in SQL queries.
|
||||
*
|
||||
* @param string $table Table name to retrieve visibility affecting fields for
|
||||
* @return string Comma separated list of field names that affect the visibility of a record on the website
|
||||
*/
|
||||
public function getVisibilityAffectingFieldsByTable($table)
|
||||
{
|
||||
if (isset($this->visibilityAffectingFields[$table])) {
|
||||
return $this->visibilityAffectingFields[$table];
|
||||
}
|
||||
|
||||
// we always want to get the uid and pid although they do not affect visibility
|
||||
$fields = ['uid', 'pid'];
|
||||
if (isset($this->tca[$table]['ctrl']['enablecolumns'])) {
|
||||
$fields = array_merge($fields, $this->tca[$table]['ctrl']['enablecolumns']);
|
||||
}
|
||||
|
||||
if (isset($this->tca[$table]['ctrl']['delete'])) {
|
||||
$fields[] = $this->tca[$table]['ctrl']['delete'];
|
||||
}
|
||||
|
||||
if ($table === 'pages') {
|
||||
$fields[] = 'no_search';
|
||||
$fields[] = 'doktype';
|
||||
}
|
||||
|
||||
$this->visibilityAffectingFields[$table] = implode(', ', $fields);
|
||||
|
||||
return $this->visibilityAffectingFields[$table];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if TCA is available for column by table
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $fieldName
|
||||
* @return bool
|
||||
*/
|
||||
public function getHasConfigurationForField(string $tableName, string $fieldName) : bool
|
||||
{
|
||||
return isset($this->tca[$tableName]['columns'][$fieldName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tca configuration for a certains field
|
||||
*
|
||||
* @param string $tableName
|
||||
* @param string $fieldName
|
||||
* @return array
|
||||
*/
|
||||
public function getConfigurationForField(string $tableName, string $fieldName) : array
|
||||
{
|
||||
return $this->tca[$tableName]['columns'][$fieldName] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $tableName
|
||||
* @return array
|
||||
*/
|
||||
public function getTableConfiguration(string $tableName) : array
|
||||
{
|
||||
return $this->tca[$tableName] ?? [];
|
||||
}
|
||||
}
|
193
Classes/System/Url/UrlHelper.php
Normal file
193
Classes/System/Url/UrlHelper.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Url;
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
|
||||
/**
|
||||
* Class UrlHelper
|
||||
*
|
||||
* @author Timo Hund <timo.hund@dkd.de>
|
||||
*/
|
||||
class UrlHelper extends Uri
|
||||
{
|
||||
/**
|
||||
* @param string $host
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use withHost instead.
|
||||
* @see Uri::withHost()
|
||||
*/
|
||||
public function setHost(string $host)
|
||||
{
|
||||
$this->host = $host;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $port
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use withPort instead.
|
||||
* @see Uri::withPort()
|
||||
*/
|
||||
public function setPort(string $port)
|
||||
{
|
||||
if ($port !== '') {
|
||||
if (MathUtility::canBeInterpretedAsInteger($port) === false) {
|
||||
$argumentType = is_object($port) ? get_class($port) : gettype($port);
|
||||
throw new \InvalidArgumentException('Invalid port "' . $argumentType . '" specified, must be an integer.', 1436717324);
|
||||
}
|
||||
|
||||
$port = (int)$port;
|
||||
if ($port < 1 || $port > 65535) {
|
||||
throw new \InvalidArgumentException('Invalid port "' . $port . '" specified, must be a valid TCP/UDP port.', 1436717326);
|
||||
}
|
||||
}
|
||||
|
||||
$this->port = $port;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $scheme
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use Uri::withScheme instead.
|
||||
* @see Uri::withScheme()
|
||||
*/
|
||||
public function setScheme(string $scheme)
|
||||
{
|
||||
$this->scheme = $this->sanitizeScheme($scheme);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use withPath instead.
|
||||
* @see Uri::withPath()
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
if (!is_string($path)) {
|
||||
throw new \InvalidArgumentException('Invalid path provided. Must be of type string.', 1436717328);
|
||||
}
|
||||
|
||||
if (strpos($path, '?') !== false) {
|
||||
throw new \InvalidArgumentException('Invalid path provided. Must not contain a query string.', 1436717330);
|
||||
}
|
||||
|
||||
if (strpos($path, '#') !== false) {
|
||||
throw new \InvalidArgumentException('Invalid path provided; must not contain a URI fragment', 1436717332);
|
||||
}
|
||||
$this->path = $this->sanitizePath($path);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a given parameter from the query and create a new instance.
|
||||
*
|
||||
* @param string $parameterName
|
||||
* @return UrlHelper
|
||||
*/
|
||||
public function withoutQueryParameter(string $parameterName): UrlHelper
|
||||
{
|
||||
parse_str($this->query, $parameters);
|
||||
if (isset($parameters[$parameterName])) {
|
||||
unset($parameters[$parameterName]);
|
||||
}
|
||||
$query = '';
|
||||
if (!empty($parameters)) {
|
||||
$query = http_build_query($parameters);
|
||||
}
|
||||
$query = $this->sanitizeQuery($query);
|
||||
$clonedObject = clone $this;
|
||||
$clonedObject->query = $query;
|
||||
return $clonedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $parameterName
|
||||
* @throws \InvalidArgumentException
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use withoutQueryParameter instead.
|
||||
*/
|
||||
public function removeQueryParameter(string $parameterName): UrlHelper
|
||||
{
|
||||
parse_str($this->query, $parameters);
|
||||
if (isset($parameters[$parameterName])) {
|
||||
unset($parameters[$parameterName]);
|
||||
}
|
||||
$query = '';
|
||||
if (!empty($parameters)) {
|
||||
$query = http_build_query($parameters);
|
||||
}
|
||||
$this->query = $this->sanitizeQuery($query);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a given parameter with value to the query and create a new instance.
|
||||
*
|
||||
* @param string $parameterName
|
||||
* @param mixed $value
|
||||
* @return UrlHelper
|
||||
*/
|
||||
public function withQueryParameter(string $parameterName, $value): UrlHelper
|
||||
{
|
||||
parse_str($this->query, $parameters);
|
||||
$parameters[$parameterName] = $value;
|
||||
$query = '';
|
||||
if (!empty($parameters)) {
|
||||
$query = http_build_query($parameters);
|
||||
}
|
||||
$query = $this->sanitizeQuery($query);
|
||||
$clonedObject = clone $this;
|
||||
$clonedObject->query = $query;
|
||||
return $clonedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $parameterName
|
||||
* @param mixed $value
|
||||
* @throws \InvalidArgumentException
|
||||
* @return UrlHelper
|
||||
* @deprecated Will be removed with v12. Use withQueryParameter instead.
|
||||
*/
|
||||
public function addQueryParameter(string $parameterName, $value): UrlHelper
|
||||
{
|
||||
$parameters = $this->query;
|
||||
parse_str($this->query, $parameters);
|
||||
if (empty($parameters)) {
|
||||
$parameters = [];
|
||||
}
|
||||
$parameters[$parameterName] = $value;
|
||||
$query = '';
|
||||
if (!empty($parameters)) {
|
||||
$query = http_build_query($parameters);
|
||||
}
|
||||
$this->query = $this->sanitizeQuery($query);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated Will be removed with v12. Use __toString() instead.
|
||||
*/
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->__toString();
|
||||
}
|
||||
}
|
222
Classes/System/UserFunctions/FlexFormUserFunctions.php
Normal file
222
Classes/System/UserFunctions/FlexFormUserFunctions.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\UserFunctions;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
use WapplerSystems\Meilisearch\ConnectionManager;
|
||||
use WapplerSystems\Meilisearch\FrontendEnvironment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
/**
|
||||
* This class contains all user functions for flexforms.
|
||||
*
|
||||
* @author Daniel Siepmann <coding@daniel-siepmann.de>
|
||||
*/
|
||||
class FlexFormUserFunctions
|
||||
{
|
||||
|
||||
/**
|
||||
* @var FrontendEnvironment
|
||||
*/
|
||||
protected $frontendEnvironment = null;
|
||||
|
||||
public function __construct(FrontendEnvironment $frontendEnvironment = null)
|
||||
{
|
||||
$this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides all facet fields for a flexform select, enabling the editor to select one of them.
|
||||
*
|
||||
* @param array $parentInformation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getFacetFieldsFromSchema(array &$parentInformation)
|
||||
{
|
||||
$pageRecord = $parentInformation['flexParentDatabaseRow'];
|
||||
$configuredFacets = $this->getConfiguredFacetsForPage($pageRecord['pid']);
|
||||
|
||||
if (!is_array($pageRecord)) {
|
||||
$parentInformation['items'] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$newItems = $this->getParsedSolrFieldsFromSchema($configuredFacets, $pageRecord);
|
||||
$parentInformation['items'] = $newItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method parses the solr schema fields into the required format for the backend flexform.
|
||||
*
|
||||
* @param array $configuredFacets
|
||||
* @param array $pageRecord
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getParsedSolrFieldsFromSchema($configuredFacets, $pageRecord)
|
||||
{
|
||||
$newItems = [];
|
||||
|
||||
array_map(function($fieldName) use (&$newItems, $configuredFacets) {
|
||||
$value = $fieldName;
|
||||
$label = $fieldName;
|
||||
|
||||
$facetNameFilter = function($facet) use ($fieldName) {
|
||||
return ($facet['field'] === $fieldName);
|
||||
};
|
||||
$configuredFacets = array_filter($configuredFacets, $facetNameFilter);
|
||||
if (!empty($configuredFacets)) {
|
||||
$configuredFacet = array_values($configuredFacets);
|
||||
$label = $configuredFacet[0]['label'];
|
||||
// try to translate LLL: label or leave it unchanged
|
||||
if (GeneralUtility::isFirstPartOfStr($label, 'LLL:') && $this->getTranslation($label) != '') {
|
||||
$label = $this->getTranslation($label);
|
||||
} elseif (!GeneralUtility::isFirstPartOfStr($label, 'LLL:') && $configuredFacet[0]['label.']) {
|
||||
$label = sprintf('cObject[...faceting.facets.%slabel]', array_keys($configuredFacets)[0]);
|
||||
}
|
||||
$label = sprintf('%s (Facet Label: "%s")', $value, $label);
|
||||
}
|
||||
|
||||
$newItems[$value] = [$label, $value];
|
||||
}, $this->getFieldNamesFromSolrMetaDataForPage($pageRecord));
|
||||
|
||||
ksort($newItems, SORT_NATURAL);
|
||||
return $newItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured facets for a page.
|
||||
*
|
||||
* @param integer $pid
|
||||
* @return array
|
||||
*/
|
||||
protected function getConfiguredFacetsForPage($pid)
|
||||
{
|
||||
$typoScriptConfiguration = $this->getConfigurationFromPageId($pid);
|
||||
return $typoScriptConfiguration->getSearchFacetingFacets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the translation with the LocalizationUtility.
|
||||
*
|
||||
* @param string $label
|
||||
* @return null|string
|
||||
*/
|
||||
protected function getTranslation($label)
|
||||
{
|
||||
return LocalizationUtility::translate($label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get solr connection.
|
||||
*
|
||||
* @param array $pageRecord
|
||||
*
|
||||
* @return \WapplerSystems\Meilisearch\System\Solr\SolrConnection
|
||||
*/
|
||||
protected function getConnection(array $pageRecord)
|
||||
{
|
||||
return GeneralUtility::makeInstance(ConnectionManager::class)->getConnectionByPageId($pageRecord['pid'], $pageRecord['sys_language_uid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all fieldnames that occure in the solr schema for one page.
|
||||
*
|
||||
* @param array $pageRecord
|
||||
* @return array
|
||||
*/
|
||||
protected function getFieldNamesFromSolrMetaDataForPage(array $pageRecord)
|
||||
{
|
||||
return array_keys((array)$this->getConnection($pageRecord)->getAdminService()->getFieldsMetaData());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parentInformation
|
||||
*/
|
||||
public function getAvailableTemplates(array &$parentInformation)
|
||||
{
|
||||
$pageRecord = $parentInformation['flexParentDatabaseRow'];
|
||||
if (!is_array($pageRecord) || !isset ($pageRecord['pid'])) {
|
||||
$parentInformation['items'] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$pageId = $pageRecord['pid'];
|
||||
|
||||
$templateKey = $this->getTypoScriptTemplateKeyFromFieldName($parentInformation);
|
||||
$availableTemplate = $this->getAvailableTemplateFromTypoScriptConfiguration($pageId, $templateKey);
|
||||
$newItems = $this->buildSelectItemsFromAvailableTemplate($availableTemplate);
|
||||
|
||||
$parentInformation['items'] = $newItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parentInformation
|
||||
* @return string
|
||||
*/
|
||||
protected function getTypoScriptTemplateKeyFromFieldName(array &$parentInformation)
|
||||
{
|
||||
$field = $parentInformation['field'];
|
||||
return str_replace('view.templateFiles.', '', $field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pid
|
||||
* @return \WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration|array
|
||||
*/
|
||||
protected function getConfigurationFromPageId($pid)
|
||||
{
|
||||
$typoScriptConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($pid);
|
||||
return $typoScriptConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configured templates from TypoScript.
|
||||
*
|
||||
* @param integer $pageId
|
||||
* @param string $templateKey
|
||||
* @return array
|
||||
*/
|
||||
protected function getAvailableTemplateFromTypoScriptConfiguration($pageId, $templateKey)
|
||||
{
|
||||
$configuration = $this->getConfigurationFromPageId($pageId);
|
||||
return $configuration->getAvailableTemplatesByFileKey($templateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the available templates as needed for the flexform.
|
||||
*
|
||||
* @param array $availableTemplates
|
||||
* @return array
|
||||
*/
|
||||
protected function buildSelectItemsFromAvailableTemplate($availableTemplates)
|
||||
{
|
||||
$newItems = [];
|
||||
$newItems['Use Default'] = ['Use Default', null];
|
||||
foreach ($availableTemplates as $availableTemplate) {
|
||||
$label = isset($availableTemplate['label']) ? $availableTemplate['label'] : '';
|
||||
$value = isset($availableTemplate['file']) ? $availableTemplate['file'] : '';
|
||||
$newItems[$label] = [$label, $value];
|
||||
}
|
||||
|
||||
return $newItems;
|
||||
}
|
||||
}
|
257
Classes/System/Util/ArrayAccessor.php
Normal file
257
Classes/System/Util/ArrayAccessor.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Util;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-2016 Timo Schmidt <timo.schmidt@dkd.de
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Class ArrayAccessor
|
||||
*
|
||||
* LowLevel class to access nested associative arrays with
|
||||
* a path.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $data = [];
|
||||
* $data['foo']['bar'] = 'bla';
|
||||
*
|
||||
* $accessor = new ArrayAccesor($data);
|
||||
* $value = $accessor->get('foo.bar');
|
||||
*
|
||||
* echo $value;
|
||||
*
|
||||
* the example above will output "bla"
|
||||
*
|
||||
*/
|
||||
class ArrayAccessor
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $pathSeparator = ':';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $includePathSeparatorInKeys = false;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param string $pathSeparator
|
||||
* @param bool $includePathSeparatorInKeys
|
||||
*/
|
||||
public function __construct(array $data = [], $pathSeparator = ':', $includePathSeparatorInKeys = false)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->pathSeparator = $pathSeparator;
|
||||
$this->includePathSeparatorInKeys = $includePathSeparatorInKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param mixed $defaultIfEmpty
|
||||
* @return array|null
|
||||
*/
|
||||
public function get($path, $defaultIfEmpty = null)
|
||||
{
|
||||
$pathArray = $this->getPathAsArray($path);
|
||||
$pathSegmentCount = count($pathArray);
|
||||
|
||||
switch ($pathSegmentCount) {
|
||||
// direct access for small paths
|
||||
case 1:
|
||||
return isset($this->data[$pathArray[0]]) ?
|
||||
$this->data[$pathArray[0]] : $defaultIfEmpty;
|
||||
case 2:
|
||||
return isset($this->data[$pathArray[0]][$pathArray[1]]) ?
|
||||
$this->data[$pathArray[0]][$pathArray[1]] : $defaultIfEmpty;
|
||||
case 3:
|
||||
return isset($this->data[$pathArray[0]][$pathArray[1]][$pathArray[2]]) ?
|
||||
$this->data[$pathArray[0]][$pathArray[1]][$pathArray[2]] : $defaultIfEmpty;
|
||||
default:
|
||||
// when we have a longer path we use a loop to get the element
|
||||
$loopResult = $this->getDeepElementWithLoop($pathArray);
|
||||
return $loopResult !== null ? $loopResult : $defaultIfEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pathArray
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getDeepElementWithLoop($pathArray)
|
||||
{
|
||||
$currentElement = $this->data;
|
||||
foreach ($pathArray as $key => $pathSegment) {
|
||||
// if the current path segment was not found we can stop searching
|
||||
if (!isset($currentElement[$pathSegment])) {
|
||||
break;
|
||||
}
|
||||
$currentElement = $currentElement[$pathSegment];
|
||||
unset($pathArray[$key]);
|
||||
}
|
||||
|
||||
// if segments are left the item does not exist
|
||||
if (count($pathArray) > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if no items left, we've found the last element
|
||||
return $currentElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @return bool
|
||||
*/
|
||||
public function has($path)
|
||||
{
|
||||
return $this->get($path) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $path
|
||||
* @param $value
|
||||
*/
|
||||
public function set($path, $value)
|
||||
{
|
||||
$pathArray = $this->getPathAsArray($path);
|
||||
$pathSegmentCount = count($pathArray);
|
||||
|
||||
switch ($pathSegmentCount) {
|
||||
// direct access for small paths
|
||||
case 1:
|
||||
$this->data[$pathArray[0]] = $value;
|
||||
return;
|
||||
case 2:
|
||||
$this->data[$pathArray[0]][$pathArray[1]] = $value;
|
||||
return;
|
||||
default:
|
||||
$this->setDeepElementWithLoop($pathArray, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pathArray
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function setDeepElementWithLoop($pathArray, $value)
|
||||
{
|
||||
$currentElement = &$this->data;
|
||||
foreach ($pathArray as $key => $pathSegment) {
|
||||
if (!isset($currentElement[$pathSegment])) {
|
||||
$currentElement[$pathSegment] = [];
|
||||
}
|
||||
|
||||
unset($pathArray[$key]);
|
||||
// if segments are left the item does not exist
|
||||
if (count($pathArray) === 0) {
|
||||
$currentElement[$pathSegment] = $value;
|
||||
return;
|
||||
}
|
||||
|
||||
$currentElement = &$currentElement[$pathSegment];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*/
|
||||
public function reset($path)
|
||||
{
|
||||
$pathArray = $this->getPathAsArray($path);
|
||||
$pathSegmentCount = count($pathArray);
|
||||
|
||||
switch ($pathSegmentCount) {
|
||||
// direct access for small paths
|
||||
case 1:
|
||||
unset($this->data[$pathArray[0]]);
|
||||
return;
|
||||
case 2:
|
||||
unset($this->data[$pathArray[0]][$pathArray[1]]);
|
||||
return;
|
||||
default:
|
||||
$this->resetDeepElementWithLoop($pathArray);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $pathArray
|
||||
*/
|
||||
protected function resetDeepElementWithLoop($pathArray)
|
||||
{
|
||||
$currentElement = &$this->data;
|
||||
|
||||
foreach ($pathArray as $key => $pathSegment) {
|
||||
unset($pathArray[$key]);
|
||||
// if segments are left the item does not exist
|
||||
if (count($pathArray) === 0) {
|
||||
unset($currentElement[$pathSegment]);
|
||||
// when the element is empty after unsetting the path segment, we can remove it completely
|
||||
if (empty($currentElement)) {
|
||||
unset($currentElement);
|
||||
}
|
||||
} else {
|
||||
$currentElement = &$currentElement[$pathSegment];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
protected function getPathAsArray($path)
|
||||
{
|
||||
if (!$this->includePathSeparatorInKeys) {
|
||||
$pathArray = explode($this->pathSeparator, $path);
|
||||
return $pathArray;
|
||||
}
|
||||
|
||||
$substitutedPath = str_replace($this->pathSeparator, $this->pathSeparator . '@@@', trim($path));
|
||||
$pathArray = array_filter(explode('@@@', $substitutedPath));
|
||||
return $pathArray;
|
||||
}
|
||||
}
|
185
Classes/System/Util/SiteUtility.php
Normal file
185
Classes/System/Util/SiteUtility.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\System\Util;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2019 Timo Hund <timo.hund@dkd.de>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
|
||||
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
|
||||
use TYPO3\CMS\Core\Site\Entity\Site;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* This class contains related functions for the new site management that was introduced with TYPO3 9.
|
||||
*/
|
||||
class SiteUtility
|
||||
{
|
||||
|
||||
/**
|
||||
* Determines if the site where the page belongs to is managed with the TYPO3 site management.
|
||||
*
|
||||
* @param int $pageId
|
||||
* @return boolean
|
||||
*/
|
||||
public static function getIsSiteManagedSite(int $pageId): bool
|
||||
{
|
||||
|
||||
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
|
||||
try {
|
||||
/* @var SiteFinder $siteFinder */
|
||||
$site = $siteFinder->getSiteByPageId($pageId);
|
||||
} catch (SiteNotFoundException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $site instanceof Site;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the connection configuration from the TYPO3 site configuration.
|
||||
*
|
||||
* Note: Language context properties have precedence over global settings.
|
||||
*
|
||||
* The configuration is done in the globals configuration of a site, and be extended in the language specific configuration
|
||||
* of a site.
|
||||
*
|
||||
* Typically everything except the core name is configured on the global level and the core name differs for each language.
|
||||
*
|
||||
* In addition every property can be defined for the ```read``` and ```write``` scope.
|
||||
*
|
||||
* The convention for property keys is "solr_{propertyName}_{scope}". With the configuration "solr_host_read" you define the host
|
||||
* for the solr read connection.
|
||||
*
|
||||
* @param Site $typo3Site
|
||||
* @param string $property
|
||||
* @param int $languageId
|
||||
* @param string $scope
|
||||
* @param string $defaultValue
|
||||
* @return string
|
||||
*/
|
||||
public static function getConnectionProperty(Site $typo3Site, string $property, int $languageId, string $scope, string $defaultValue = null): string
|
||||
{
|
||||
$value = self::getConnectionPropertyOrFallback($typo3Site, $property, $languageId, $scope);
|
||||
if ($value === null) {
|
||||
return $defaultValue;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves site configuration properties.
|
||||
* Language context properties have precedence over global settings.
|
||||
*
|
||||
* @param Site $typo3Site
|
||||
* @param string $property
|
||||
* @param int $languageId
|
||||
* @param string $scope
|
||||
* @return mixed
|
||||
*/
|
||||
protected static function getConnectionPropertyOrFallback(Site $typo3Site, string $property, int $languageId, string $scope)
|
||||
{
|
||||
if ($scope === 'write' && !self::writeConnectionIsEnabled($typo3Site, $languageId)) {
|
||||
$scope = 'read';
|
||||
}
|
||||
|
||||
// convention key solr_$property_$scope
|
||||
$keyToCheck = 'solr_' . $property . '_' . $scope;
|
||||
|
||||
// convention fallback key solr_$property_read
|
||||
$fallbackKey = 'solr_' . $property . '_read';
|
||||
|
||||
// try to find language specific setting if found return it
|
||||
$languageSpecificConfiguration = $typo3Site->getLanguageById($languageId)->toArray();
|
||||
$value = self::getValueOrFallback($languageSpecificConfiguration, $keyToCheck, $fallbackKey);
|
||||
if ($value !== null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// if not found check global configuration
|
||||
$siteBaseConfiguration = $typo3Site->getConfiguration();
|
||||
return self::getValueOrFallback($siteBaseConfiguration, $keyToCheck, $fallbackKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether write connection is enabled.
|
||||
* Language context properties have precedence over global settings.
|
||||
*
|
||||
* @param Site $typo3Site
|
||||
* @param int $languageId
|
||||
* @return bool
|
||||
*/
|
||||
protected static function writeConnectionIsEnabled(Site $typo3Site, int $languageId): bool
|
||||
{
|
||||
$languageSpecificConfiguration = $typo3Site->getLanguageById($languageId)->toArray();
|
||||
$value = self::getValueOrFallback($languageSpecificConfiguration, 'solr_use_write_connection', 'solr_use_write_connection');
|
||||
if ($value !== null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$siteBaseConfiguration = $typo3Site->getConfiguration();
|
||||
$value = self::getValueOrFallback($siteBaseConfiguration, 'solr_use_write_connection', 'solr_use_write_connection');
|
||||
if ($value !== null) {
|
||||
return $value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @param string $keyToCheck
|
||||
* @param string $fallbackKey
|
||||
* @return string|bool|null
|
||||
*/
|
||||
protected static function getValueOrFallback(array $data, string $keyToCheck, string $fallbackKey)
|
||||
{
|
||||
$value = $data[$keyToCheck] ?? null;
|
||||
if ($value === '0' || $value === 0 || !empty($value)) {
|
||||
return self::evaluateConfigurationData($value);
|
||||
}
|
||||
|
||||
return self::evaluateConfigurationData($data[$fallbackKey] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate configuration data
|
||||
*
|
||||
* Setting boolean values via environment variables
|
||||
* results in strings like 'false' that may be misinterpreted
|
||||
* thus we check for boolean values in strings.
|
||||
*
|
||||
* @param string|bool|null $value
|
||||
* @return string|bool|null
|
||||
*/
|
||||
protected static function evaluateConfigurationData($value)
|
||||
{
|
||||
if ($value === 'true') {
|
||||
return true;
|
||||
} elseif ($value === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
51
Classes/System/Validator/Path.php
Normal file
51
Classes/System/Validator/Path.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\System\Validator;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2017 - Thomas Hohn <tho@systime.dk>
|
||||
* 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 3 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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Class Path is used for Solr Path related methods
|
||||
*
|
||||
* @author Thomas Hohn <tho@systime.dk>
|
||||
*/
|
||||
class Path
|
||||
{
|
||||
|
||||
/**
|
||||
* Validate that a path is a valid Solr Path
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidSolrPath($path)
|
||||
{
|
||||
$path = trim($path);
|
||||
|
||||
if ((!empty($path)) && (preg_match('/^[^*?"<>|:#]*$/', $path))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user