first commit

This commit is contained in:
Sven Wappler
2021-04-17 00:26:33 +02:00
commit 866c63cc63
813 changed files with 100696 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result\Parser;
/***************************************************************
* Copyright notice
*
* (c) 2015-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\ResultSet\Result\SearchResultBuilder;
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result\SearchResultCollection;
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* A ResultParser is responsible to create the result object structure from the \Apache_Solr_Response
* and assign it to the SearchResultSet.
*/
abstract class AbstractResultParser {
/**
* @var SearchResultBuilder
*/
protected $searchResultBuilder;
/**
* @var DocumentEscapeService
*/
protected $documentEscapeService;
/**
* AbstractResultParser constructor.
* @param SearchResultBuilder|null $resultBuilder
* @param DocumentEscapeService|null $documentEscapeService
*/
public function __construct(SearchResultBuilder $resultBuilder = null, DocumentEscapeService $documentEscapeService = null) {
$this->searchResultBuilder = $resultBuilder ?? GeneralUtility::makeInstance(SearchResultBuilder::class);
$this->documentEscapeService = $documentEscapeService ?? GeneralUtility::makeInstance(DocumentEscapeService::class);
}
/**
* @param SearchResultSet $resultSet
* @param bool $useRawDocuments
* @return SearchResultSet
*/
abstract public function parse(SearchResultSet $resultSet, bool $useRawDocuments = true);
/**
* @param SearchResultSet $resultSet
* @return mixed
*/
abstract public function canParse(SearchResultSet $resultSet);
}

View File

@@ -0,0 +1,88 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result\Parser;
/***************************************************************
* Copyright notice
*
* (c) 2015-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\ResultSet\Result\SearchResultCollection;
use WapplerSystems\Meilisearch\Domain\Search\ResultSet\SearchResultSet;
use WapplerSystems\Meilisearch\System\Configuration\TypoScriptConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* The DefaultResultParser is able to parse normal(ungroupd results)
*/
class DefaultResultParser extends AbstractResultParser {
/**
* @param SearchResultSet $resultSet
* @param bool $useRawDocuments
* @return SearchResultSet
*/
public function parse(SearchResultSet $resultSet, bool $useRawDocuments = true)
{
$searchResults = GeneralUtility::makeInstance(SearchResultCollection::class);
$parsedData = $resultSet->getResponse()->getParsedData();
// @extensionScannerIgnoreLine
$resultSet->setMaximumScore($parsedData->response->maxScore ?? 0.0);
// @extensionScannerIgnoreLine
$resultSet->setAllResultCount($parsedData->response->numFound ?? 0);
// @extensionScannerIgnoreLine
if (!is_array($parsedData->response->docs)) {
return $resultSet;
}
// @extensionScannerIgnoreLine
$documents = $parsedData->response->docs;
if (!$useRawDocuments) {
$documents = $this->documentEscapeService->applyHtmlSpecialCharsOnAllFields($documents);
}
foreach ($documents as $searchResult) {
$searchResultObject = $this->searchResultBuilder->fromApacheSolrDocument($searchResult);
$searchResults[] = $searchResultObject;
}
$resultSet->setSearchResults($searchResults);
return $resultSet;
}
/**
* @param SearchResultSet $resultSet
* @return bool
*/
public function canParse(SearchResultSet $resultSet)
{
// This parsers should not be used when grouping is enabled
$configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration();
if ($configuration instanceof TypoScriptConfiguration && $configuration->getSearchGrouping())
{
return false;
}
return true;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result\Parser;
/***************************************************************
* Copyright notice
*
* (c) 2015-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\Solr\Document\Document;
use WapplerSystems\Meilisearch\Util;
/**
* Applies htmlspecialschars on documents of a solr response.
*/
class DocumentEscapeService {
/**
* @var TypoScriptConfiguration
*/
protected $typoScriptConfiguration = null;
/**
* DocumentEscapeService constructor.
* @param TypoScriptConfiguration|null $typoScriptConfiguration
*/
public function __construct(TypoScriptConfiguration $typoScriptConfiguration = null) {
$this->typoScriptConfiguration = $typoScriptConfiguration ?? Util::getSolrConfiguration();
}
/**
* This method is used to apply htmlspecialchars on all document fields that
* are not configured to be secure. Secure mean that we know where the content is coming from.
*
* @param Document[] $documents
* @return Document[]
*/
public function applyHtmlSpecialCharsOnAllFields(array $documents)
{
$trustedSolrFields = $this->typoScriptConfiguration->getSearchTrustedFieldsArray();
foreach ($documents as $key => $document) {
$fieldNames = array_keys($document->getFields() ?? []);
foreach ($fieldNames as $fieldName) {
if (is_array($trustedSolrFields) && in_array($fieldName, $trustedSolrFields)) {
// we skip this field, since it was marked as secure
continue;
}
$value = $this->applyHtmlSpecialCharsOnSingleFieldValue($document[$fieldName]);
$document->setField($fieldName, $value);
}
$documents[$key] = $document;
}
return $documents;
}
/**
* Applies htmlspecialchars on all items of an array of a single value.
*
* @param $fieldValue
* @return array|string
*/
protected function applyHtmlSpecialCharsOnSingleFieldValue($fieldValue)
{
if (is_array($fieldValue)) {
foreach ($fieldValue as $key => $fieldValueItem) {
$fieldValue[$key] = htmlspecialchars($fieldValueItem, null, null, false);
}
} else {
$fieldValue = htmlspecialchars($fieldValue, null, null, false);
}
return $fieldValue;
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result\Parser;
/***************************************************************
* 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\Domain\Search\ResultSet\SearchResultSet;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class ResultParserRegistry
*
* @author Frans Saris <frans@beech.it>
* @author Timo Hund <timo.hund@dkd.de>
*/
class ResultParserRegistry implements SingletonInterface
{
/**
* Array of available parser classNames
*
* @var array
*/
protected $parsers = [
100 => DefaultResultParser::class,
];
/**
* @var AbstractResultParser[]
*/
protected $parserInstances;
/**
* Get registered parser classNames
*
* @return array
*/
public function getParsers()
{
return $this->parsers;
}
/**
* Can be used to register a custom parser.
*
* @param string $className classname of the parser that should be used
* @param int $priority higher priority means more important
* @throws \InvalidArgumentException
*/
public function registerParser($className, $priority)
{
// check if the class is available for TYPO3 before registering the driver
if (!class_exists($className)) {
throw new \InvalidArgumentException('Class ' . $className . ' does not exist.', 1468863997);
}
if (!is_subclass_of($className, AbstractResultParser::class)) {
throw new \InvalidArgumentException('Parser ' . $className . ' needs to implement the AbstractResultParser.', 1468863998);
}
if (array_key_exists((int)$priority, $this->parsers)) {
throw new \InvalidArgumentException('There is already a parser registerd with priority ' . (int)$priority . '.', 1468863999);
}
$this->parsers[(int)$priority] = $className;
}
/**
* Method to check if a certain parser is allready registered
*
* @param string $className
* @param int $priority
* @return boolean
*/
public function hasParser($className, $priority)
{
if (empty($this->parsers[$priority])) {
return false;
}
return $this->parsers[$priority] === $className;
}
/**
* @return AbstractResultParser[]
*/
public function getParserInstances()
{
if ($this->parserInstances === null) {
ksort($this->parsers);
$orderedParsers = array_reverse($this->parsers);
foreach ($orderedParsers as $className) {
$this->parserInstances[] = $this->createParserInstance($className);
}
}
return $this->parserInstances;
}
/**
* @param SearchResultSet $resultSet
* @return AbstractResultParser|null
*/
public function getParser(SearchResultSet $resultSet)
{
/** @var AbstractResultParser $parser */
foreach ($this->getParserInstances() as $parser) {
if ($parser->canParse($resultSet)) {
return $parser;
}
}
return null;
}
/**
* Create an instance of a certain parser class
*
* @return AbstractResultParser
*/
protected function createParserInstance($className)
{
return GeneralUtility::makeInstance($className);
}
}

View File

@@ -0,0 +1,242 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result;
/***************************************************************
* Copyright notice
*
* (c) 2015-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\Domain\Search\ResultSet\Grouping\GroupItem;
use WapplerSystems\Meilisearch\System\Solr\Document\Document;
/**
* Solr document class that should be used in the frontend in the search context.
*
* @author Timo Schmidt <timo.schmidt@dkd.de>
*/
class SearchResult extends Document
{
/**
* The variant field value
*
* Value of Solr collapse field, which is defined via
* TypoScript variable "variants.variantField"
*
* @var string
*/
protected $variantFieldValue = '';
/**
* Number of variants found
*
* May differ from documents in variants as
* returned variants are limited by expand.rows
*
* @var int
*/
protected $variantsNumFound = 0;
/**
* @var SearchResult[]
*/
protected $variants = [];
/**
* Indicates if an instance of this document is a variant (a sub document of another).
*
* @var bool
*/
protected $isVariant = false;
/**
* References the parent document of the document is a variant.
*
* @var SearchResult|null
*/
protected $variantParent = null;
/**
* @var GroupItem
*/
protected $groupItem = null;
/**
* @return GroupItem
*/
public function getGroupItem(): GroupItem
{
return $this->groupItem;
}
/**
* @return bool
*/
public function getHasGroupItem()
{
return $this->groupItem !== null;
}
/**
* @param GroupItem $group
*/
public function setGroupItem(GroupItem $group)
{
$this->groupItem = $group;
}
/**
* @return string
*/
public function getVariantFieldValue(): string
{
return $this->variantFieldValue;
}
/**
* @param string $variantFieldValue
*/
public function setVariantFieldValue(string $variantFieldValue)
{
$this->variantFieldValue = $variantFieldValue;
}
/**
* @return int
*/
public function getVariantsNumFound(): int
{
return $this->variantsNumFound;
}
/**
* @param int $numFound
*/
public function setVariantsNumFound(int $numFound)
{
$this->variantsNumFound = $numFound;
}
/**
* @return SearchResult[]
*/
public function getVariants()
{
return $this->variants;
}
/**
* @param SearchResult $expandedResult
*/
public function addVariant(SearchResult $expandedResult)
{
$this->variants[] = $expandedResult;
}
/**
* @return bool
*/
public function getIsVariant()
{
return $this->isVariant;
}
/**
* @param bool $isVariant
*/
public function setIsVariant($isVariant)
{
$this->isVariant = $isVariant;
}
/**
* @return SearchResult
*/
public function getVariantParent()
{
return $this->variantParent;
}
/**
* @param SearchResult $variantParent
*/
public function setVariantParent(SearchResult $variantParent)
{
$this->variantParent = $variantParent;
}
/**
* @return string
*/
public function getContent()
{
return $this->fields['content'];
}
/**
* @return boolean
*/
public function getIsElevated()
{
return $this->fields['isElevated'];
}
/**
* @return string
*/
public function getType()
{
return $this->fields['type'];
}
/**
* @return integer
*/
public function getId()
{
return $this->fields['id'];
}
/**
* @return float
*/
public function getScore()
{
return $this->fields['score'];
}
/**
* @return string
*/
public function getUrl()
{
return $this->fields['url'];
}
/**
* @return string
*/
public function getTitle()
{
return $this->fields['title'];
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result;
/***************************************************************
* 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\Solr\Document\Document;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* The SearchResultBuilder is responsible to build a SearchResult object from an \WapplerSystems\Meilisearch\System\Solr\Document\Document
* and should use a different class as SearchResult if configured.
*/
class SearchResultBuilder {
/**
* This method is used to wrap the original solr document instance in an instance of the configured SearchResult
* class.
*
* @param Document $originalDocument
* @throws \InvalidArgumentException
* @return SearchResult
*/
public function fromApacheSolrDocument(Document $originalDocument)
{
$searchResultClassName = $this->getResultClassName();
$result = GeneralUtility::makeInstance($searchResultClassName, /** @scrutinizer ignore-type */ $originalDocument->getFields() ?? []);
if (!$result instanceof SearchResult) {
throw new \InvalidArgumentException('Could not create result object with class: ' . (string)$searchResultClassName, 1470037679);
}
return $result;
}
/**
* @return string
*/
protected function getResultClassName()
{
return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName ']) ?
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] : SearchResult::class;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace WapplerSystems\Meilisearch\Domain\Search\ResultSet\Result;
/***************************************************************
* 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\Domain\Search\ResultSet\Grouping\GroupCollection;
use WapplerSystems\Meilisearch\System\Data\AbstractCollection;
/**
* The SearchResultCollection contains the SearchResult object and related objects. e.g groups.
*/
class SearchResultCollection extends AbstractCollection {
/**
* @var GroupCollection
*/
protected $groups = null;
/**
* SearchResultCollection constructor.
* @param array $data
*/
public function __construct(array $data = [])
{
parent::__construct($data);
$this->groups = new GroupCollection();
}
/**
* @return GroupCollection
*/
public function getGroups(): GroupCollection
{
return $this->groups;
}
/**
* @param GroupCollection $groups
*/
public function setGroups(GroupCollection $groups)
{
$this->groups = $groups;
}
/**
* @return bool
*/
public function getHasGroups(): bool
{
return $this->groups->getCount() > 0;
}
}