mirror of
https://github.com/Codappix/search_core.git
synced 2024-11-26 07:56:12 +01:00
commit
d3c4ea2389
28 changed files with 435 additions and 115 deletions
|
@ -5,7 +5,7 @@ addons:
|
||||||
packages:
|
packages:
|
||||||
- oracle-java8-set-default
|
- oracle-java8-set-default
|
||||||
before_install:
|
before_install:
|
||||||
- curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.0.deb && sudo dpkg -i --force-confnew elasticsearch-5.2.0.deb && sudo service elasticsearch start
|
- curl -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.2.0.deb && sudo dpkg -i --force-confnew elasticsearch-5.2.0.deb && sudo service elasticsearch restart
|
||||||
- mysql -u root -e 'GRANT ALL ON `typo3_ci_ft%`.* TO travis@127.0.0.1;'
|
- mysql -u root -e 'GRANT ALL ON `typo3_ci_ft%`.* TO travis@127.0.0.1;'
|
||||||
|
|
||||||
language: php
|
language: php
|
||||||
|
|
|
@ -42,7 +42,7 @@ class Service
|
||||||
*
|
*
|
||||||
* @param array|string $configuration Either the full configuration or only the class name.
|
* @param array|string $configuration Either the full configuration or only the class name.
|
||||||
*/
|
*/
|
||||||
public function executeDataProcessor($configuration, array $data) : array
|
public function executeDataProcessor($configuration, array $data, string $recordType = '') : array
|
||||||
{
|
{
|
||||||
if (is_string($configuration)) {
|
if (is_string($configuration)) {
|
||||||
$configuration = [
|
$configuration = [
|
||||||
|
@ -50,6 +50,10 @@ class Service
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($configuration['_table']) && $recordType !== '') {
|
||||||
|
$configuration['_table'] = $recordType;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->objectManager->get($configuration['_typoScriptNodeValue'])
|
return $this->objectManager->get($configuration['_typoScriptNodeValue'])
|
||||||
->processData($data, $configuration);
|
->processData($data, $configuration);
|
||||||
}
|
}
|
||||||
|
|
101
Classes/DataProcessing/TcaRelationResolvingProcessor.php
Normal file
101
Classes/DataProcessing/TcaRelationResolvingProcessor.php
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\DataProcessing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 Codappix\SearchCore\Domain\Index\TcaIndexer\RelationResolver;
|
||||||
|
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableServiceInterface;
|
||||||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves relations from TCA using RelationResolver.
|
||||||
|
*/
|
||||||
|
class TcaRelationResolvingProcessor implements ProcessorInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ObjectManagerInterface
|
||||||
|
*/
|
||||||
|
protected $objectManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RelationResolver
|
||||||
|
*/
|
||||||
|
protected $relationResolver;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ObjectManagerInterface $objectManager,
|
||||||
|
RelationResolver $relationResolver
|
||||||
|
) {
|
||||||
|
$this->objectManager = $objectManager;
|
||||||
|
$this->relationResolver = $relationResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \InvalidArgumentException If _table is not configured.
|
||||||
|
*/
|
||||||
|
public function processData(array $record, array $configuration) : array
|
||||||
|
{
|
||||||
|
$this->initializeConfiguration($configuration);
|
||||||
|
|
||||||
|
$tcaTableService = $this->objectManager->get(
|
||||||
|
TcaTableServiceInterface::class,
|
||||||
|
$configuration['_table']
|
||||||
|
);
|
||||||
|
|
||||||
|
$processedRecord = $this->relationResolver->resolveRelationsForRecord(
|
||||||
|
$tcaTableService,
|
||||||
|
$this->getRecordToProcess($record, $configuration)
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_merge($record, $processedRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \InvalidArgumentException If _table is not configured.
|
||||||
|
*/
|
||||||
|
protected function initializeConfiguration(array &$configuration)
|
||||||
|
{
|
||||||
|
if (!isset($configuration['_table'])) {
|
||||||
|
throw new \InvalidArgumentException('The configuration "_table" is mandantory.', 1524552631);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($configuration['excludeFields'])) {
|
||||||
|
$configuration['excludeFields'] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$configuration['excludeFields'] = GeneralUtility::trimExplode(',', $configuration['excludeFields'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRecordToProcess(array $record, array $configuration) : array
|
||||||
|
{
|
||||||
|
if ($configuration['excludeFields'] === []) {
|
||||||
|
return $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newRecord = [];
|
||||||
|
$keysToUse = array_diff(array_keys($record), $configuration['excludeFields']);
|
||||||
|
foreach ($keysToUse as $keyToUse) {
|
||||||
|
$newRecord[$keyToUse] = $record[$keyToUse];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newRecord;
|
||||||
|
}
|
||||||
|
}
|
|
@ -129,7 +129,7 @@ abstract class AbstractIndexer implements IndexerInterface
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
foreach ($this->configuration->get('indexing.' . $this->identifier . '.dataProcessing') as $configuration) {
|
foreach ($this->configuration->get('indexing.' . $this->identifier . '.dataProcessing') as $configuration) {
|
||||||
$record = $this->dataProcessorService->executeDataProcessor($configuration, $record);
|
$record = $this->dataProcessorService->executeDataProcessor($configuration, $record, $this->identifier);
|
||||||
}
|
}
|
||||||
} catch (InvalidArgumentException $e) {
|
} catch (InvalidArgumentException $e) {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
|
|
|
@ -60,6 +60,8 @@ class PagesIndexer extends TcaIndexer
|
||||||
|
|
||||||
protected function prepareRecord(array &$record)
|
protected function prepareRecord(array &$record)
|
||||||
{
|
{
|
||||||
|
parent::prepareRecord($record);
|
||||||
|
|
||||||
$possibleTitleFields = ['nav_title', 'tx_tqseo_pagetitle_rel', 'title'];
|
$possibleTitleFields = ['nav_title', 'tx_tqseo_pagetitle_rel', 'title'];
|
||||||
foreach ($possibleTitleFields as $searchTitleField) {
|
foreach ($possibleTitleFields as $searchTitleField) {
|
||||||
if (isset($record[$searchTitleField]) && trim($record[$searchTitleField])) {
|
if (isset($record[$searchTitleField]) && trim($record[$searchTitleField])) {
|
||||||
|
@ -74,7 +76,6 @@ class PagesIndexer extends TcaIndexer
|
||||||
$record['content'] = $content['content'];
|
$record['content'] = $content['content'];
|
||||||
$record['media'] = array_values(array_unique(array_merge($record['media'], $content['images'])));
|
$record['media'] = array_values(array_unique(array_merge($record['media'], $content['images'])));
|
||||||
}
|
}
|
||||||
parent::prepareRecord($record);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function fetchContentForPage(int $uid) : array
|
protected function fetchContentForPage(int $uid) : array
|
||||||
|
|
|
@ -33,11 +33,11 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
*/
|
*/
|
||||||
class RelationResolver implements Singleton
|
class RelationResolver implements Singleton
|
||||||
{
|
{
|
||||||
public function resolveRelationsForRecord(TcaTableServiceInterface $service, array &$record)
|
public function resolveRelationsForRecord(TcaTableServiceInterface $service, array $record) : array
|
||||||
{
|
{
|
||||||
foreach (array_keys($record) as $column) {
|
foreach (array_keys($record) as $column) {
|
||||||
// TODO: Define / configure fields to exclude?!
|
if (in_array($column, ['pid', $service->getLanguageUidColumn()])) {
|
||||||
if ($column === 'pid') {
|
$record[$column] = (int) $record[$column];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,8 @@ class RelationResolver implements Singleton
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function resolveValue($value, array $tcaColumn)
|
protected function resolveValue($value, array $tcaColumn)
|
||||||
|
@ -82,7 +84,7 @@ class RelationResolver implements Singleton
|
||||||
protected function isRelation(array &$config) : bool
|
protected function isRelation(array &$config) : bool
|
||||||
{
|
{
|
||||||
return isset($config['foreign_table'])
|
return isset($config['foreign_table'])
|
||||||
|| (isset($config['renderType']) && $config['renderType'] !== 'selectSingle')
|
|| (isset($config['renderType']) && !in_array($config['renderType'], ['selectSingle', 'inputDateTime']))
|
||||||
|| (isset($config['internal_type']) && strtolower($config['internal_type']) === 'db')
|
|| (isset($config['internal_type']) && strtolower($config['internal_type']) === 'db')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,6 @@ class TcaTableService implements TcaTableServiceInterface
|
||||||
*/
|
*/
|
||||||
protected $configuration;
|
protected $configuration;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var RelationResolver
|
|
||||||
*/
|
|
||||||
protected $relationResolver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \TYPO3\CMS\Core\Log\Logger
|
* @var \TYPO3\CMS\Core\Log\Logger
|
||||||
*/
|
*/
|
||||||
|
@ -93,7 +88,6 @@ class TcaTableService implements TcaTableServiceInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$tableName,
|
$tableName,
|
||||||
RelationResolver $relationResolver,
|
|
||||||
ConfigurationContainerInterface $configuration
|
ConfigurationContainerInterface $configuration
|
||||||
) {
|
) {
|
||||||
if (!isset($GLOBALS['TCA'][$tableName])) {
|
if (!isset($GLOBALS['TCA'][$tableName])) {
|
||||||
|
@ -106,7 +100,6 @@ class TcaTableService implements TcaTableServiceInterface
|
||||||
$this->tableName = $tableName;
|
$this->tableName = $tableName;
|
||||||
$this->tca = &$GLOBALS['TCA'][$this->tableName];
|
$this->tca = &$GLOBALS['TCA'][$this->tableName];
|
||||||
$this->configuration = $configuration;
|
$this->configuration = $configuration;
|
||||||
$this->relationResolver = $relationResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTableName() : string
|
public function getTableName() : string
|
||||||
|
@ -151,8 +144,6 @@ class TcaTableService implements TcaTableServiceInterface
|
||||||
|
|
||||||
public function prepareRecord(array &$record)
|
public function prepareRecord(array &$record)
|
||||||
{
|
{
|
||||||
$this->relationResolver->resolveRelationsForRecord($this, $record);
|
|
||||||
|
|
||||||
if (isset($record['uid']) && !isset($record['search_identifier'])) {
|
if (isset($record['uid']) && !isset($record['search_identifier'])) {
|
||||||
$record['search_identifier'] = $record['uid'];
|
$record['search_identifier'] = $record['uid'];
|
||||||
}
|
}
|
||||||
|
@ -283,6 +274,15 @@ class TcaTableService implements TcaTableServiceInterface
|
||||||
return $this->tca['columns'][$columnName]['config'];
|
return $this->tca['columns'][$columnName]['config'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLanguageUidColumn() : string
|
||||||
|
{
|
||||||
|
if (!isset($this->tca['ctrl']['languageField'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->tca['ctrl']['languageField'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given record was blacklisted by root line.
|
* Checks whether the given record was blacklisted by root line.
|
||||||
* This can be configured by typoscript as whole root lines can be black listed.
|
* This can be configured by typoscript as whole root lines can be black listed.
|
||||||
|
|
|
@ -50,11 +50,6 @@ class TcaTableService76 implements TcaTableServiceInterface
|
||||||
*/
|
*/
|
||||||
protected $configuration;
|
protected $configuration;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var RelationResolver
|
|
||||||
*/
|
|
||||||
protected $relationResolver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var \TYPO3\CMS\Core\Log\Logger
|
* @var \TYPO3\CMS\Core\Log\Logger
|
||||||
*/
|
*/
|
||||||
|
@ -89,7 +84,6 @@ class TcaTableService76 implements TcaTableServiceInterface
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$tableName,
|
$tableName,
|
||||||
RelationResolver $relationResolver,
|
|
||||||
ConfigurationContainerInterface $configuration
|
ConfigurationContainerInterface $configuration
|
||||||
) {
|
) {
|
||||||
if (!isset($GLOBALS['TCA'][$tableName])) {
|
if (!isset($GLOBALS['TCA'][$tableName])) {
|
||||||
|
@ -102,7 +96,6 @@ class TcaTableService76 implements TcaTableServiceInterface
|
||||||
$this->tableName = $tableName;
|
$this->tableName = $tableName;
|
||||||
$this->tca = &$GLOBALS['TCA'][$this->tableName];
|
$this->tca = &$GLOBALS['TCA'][$this->tableName];
|
||||||
$this->configuration = $configuration;
|
$this->configuration = $configuration;
|
||||||
$this->relationResolver = $relationResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTableName() : string
|
public function getTableName() : string
|
||||||
|
@ -157,8 +150,6 @@ class TcaTableService76 implements TcaTableServiceInterface
|
||||||
|
|
||||||
public function prepareRecord(array &$record)
|
public function prepareRecord(array &$record)
|
||||||
{
|
{
|
||||||
$this->relationResolver->resolveRelationsForRecord($this, $record);
|
|
||||||
|
|
||||||
if (isset($record['uid']) && !isset($record['search_identifier'])) {
|
if (isset($record['uid']) && !isset($record['search_identifier'])) {
|
||||||
$record['search_identifier'] = $record['uid'];
|
$record['search_identifier'] = $record['uid'];
|
||||||
}
|
}
|
||||||
|
@ -290,6 +281,15 @@ class TcaTableService76 implements TcaTableServiceInterface
|
||||||
return $this->tca['columns'][$columnName]['config'];
|
return $this->tca['columns'][$columnName]['config'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getLanguageUidColumn() : string
|
||||||
|
{
|
||||||
|
if (!isset($this->tca['ctrl']['languageField'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->tca['ctrl']['languageField'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the given record was blacklisted by root line.
|
* Checks whether the given record was blacklisted by root line.
|
||||||
* This can be configured by typoscript as whole root lines can be black listed.
|
* This can be configured by typoscript as whole root lines can be black listed.
|
||||||
|
|
|
@ -41,4 +41,6 @@ interface TcaTableServiceInterface
|
||||||
public function getRecords(int $offset, int $limit) : array;
|
public function getRecords(int $offset, int $limit) : array;
|
||||||
|
|
||||||
public function getRecord(int $identifier) : array;
|
public function getRecord(int $identifier) : array;
|
||||||
|
|
||||||
|
public function getLanguageUidColumn() : string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,10 @@ Changelog
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
:glob:
|
:glob:
|
||||||
|
|
||||||
changelog/20180415-134-make-conent-fields-configurable
|
changelog/20180424-149-extract-relation-resolver-to-data-processing
|
||||||
changelog/20180409-25-provide-sys-language-uid
|
changelog/20180410-148-keep-sys_language_uid
|
||||||
changelog/20180408-131-respect-page-cache-clear
|
changelog/20180315-134-make-conent-fields-configurable
|
||||||
changelog/20180408-introduce-php70-type-hints
|
changelog/20180309-25-provide-sys-language-uid
|
||||||
changelog/20180406-120-facet-configuration
|
changelog/20180308-131-respect-page-cache-clear
|
||||||
|
changelog/20180308-introduce-php70-type-hints
|
||||||
|
changelog/20180306-120-facet-configuration
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Breacking Change 120 "Pass facets configuration to Elasticsearch"
|
Breaking Change 120 "Pass facets configuration to Elasticsearch"
|
||||||
=================================================================
|
================================================================
|
||||||
|
|
||||||
In order to allow arbitrary facet configuration, we do not process the facet configuration anymore.
|
In order to allow arbitrary facet configuration, we do not process the facet configuration anymore.
|
||||||
Instead integrators are able to configure facets for search service "as is". We just pipe the
|
Instead integrators are able to configure facets for search service "as is". We just pipe the
|
|
@ -1,5 +1,5 @@
|
||||||
Breacking Change "Introduce PHP 7.0 TypeHints"
|
Breaking Change "Introduce PHP 7.0 TypeHints"
|
||||||
==============================================
|
=============================================
|
||||||
|
|
||||||
As PHP evolved, we now migrate the whole code base to use PHP 7.0 type hints.
|
As PHP evolved, we now migrate the whole code base to use PHP 7.0 type hints.
|
||||||
We do not use PHP 7.1 Type Hints, as some customers still need PHP 7.0 support.
|
We do not use PHP 7.1 Type Hints, as some customers still need PHP 7.0 support.
|
|
@ -1,4 +1,4 @@
|
||||||
FEATURE 25 "Respect multiple languages" - Provide sys_language_uid
|
Feature 25 "Respect multiple languages" - Provide sys_language_uid
|
||||||
==================================================================
|
==================================================================
|
||||||
|
|
||||||
Previously we did not fetch ``sys_language_uid`` field from database. This prevented everyone from
|
Previously we did not fetch ``sys_language_uid`` field from database. This prevented everyone from
|
|
@ -1,4 +1,4 @@
|
||||||
FEATURE 134 "Enable indexing of tt_content records of CType Header"
|
Feature 134 "Enable indexing of tt_content records of CType Header"
|
||||||
===================================================================
|
===================================================================
|
||||||
|
|
||||||
Before, only ``bodytext`` was used to generate content while indexing pages.
|
Before, only ``bodytext`` was used to generate content while indexing pages.
|
|
@ -0,0 +1,10 @@
|
||||||
|
Feature 148 "Cast sys_language_uid to int"
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
While resolving relations the configured language uid field, fetched from TCA, will
|
||||||
|
be casted to integer and returned immediately.
|
||||||
|
|
||||||
|
This change prevents the bug mentioned in :issue:`148`, where `0` is casted to an
|
||||||
|
empty string, which makes filtering hard.
|
||||||
|
|
||||||
|
See :issue:`148`.
|
|
@ -0,0 +1,24 @@
|
||||||
|
Breaking Change 149 "Extract RelationResolver to a new DataProcessor"
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
The resolving of relation, based on TCA, is no longer done by the indexer. Instead we
|
||||||
|
now provide a DataProcessor to solve this job.
|
||||||
|
|
||||||
|
As this makes it necessary to configure the DataProcessor, this is a breaking change.
|
||||||
|
Before the resolving was done out of the box.
|
||||||
|
|
||||||
|
So why did we change that? The resolving of relations was already implemented before
|
||||||
|
we added the data processors. As the concept of data processors is far more flexible,
|
||||||
|
we try to migrate hard coupled components step by step. The benefit of this change is
|
||||||
|
basically that you can now configure the resolving of relations and far more
|
||||||
|
important, the order of execution.
|
||||||
|
|
||||||
|
Now it's possible to first copy some fields, e.g. ``starttime`` and ``endtime`` to
|
||||||
|
further fields and to resolve relations afterwards. As the copied fields are not
|
||||||
|
configured in TCA, they will be skipped. This way an integrator can keep certain
|
||||||
|
information as they are.
|
||||||
|
|
||||||
|
Also the processor is now configured as all other processors. You can now optionally
|
||||||
|
configure fields to not process.
|
||||||
|
|
||||||
|
See :issue:`149` and :issue:`147`.
|
|
@ -59,10 +59,9 @@ author = u'Daniel Siepmann'
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = u'0.0.3'
|
version = u'0.0.4'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = u'0.0.3'
|
release = u'0.0.4'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#
|
#
|
||||||
|
@ -133,7 +132,7 @@ html_theme_options = {
|
||||||
|
|
||||||
# The name for this set of Sphinx documents.
|
# The name for this set of Sphinx documents.
|
||||||
# "<project> v<release> documentation" by default.
|
# "<project> v<release> documentation" by default.
|
||||||
#html_title = u'TYPO3 Extension search_core v0.0.3'
|
#html_title = u'TYPO3 Extension search_core v0.0.4'
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
#html_short_title = None
|
#html_short_title = None
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
``Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor``
|
||||||
|
====================================================================
|
||||||
|
|
||||||
|
Will resolve relations through TCA for record.
|
||||||
|
The result will be the same as in list view of TYPO3 Backend. E.g. Check boxes will be
|
||||||
|
resolved to their label, dates will be resolved to human readable representation and
|
||||||
|
relations will be resolved to their configured labels.
|
||||||
|
|
||||||
|
Combine with CopyToProcessor or exclude certain fields to keep original value for
|
||||||
|
further processing.
|
||||||
|
|
||||||
|
Mandatory Options:
|
||||||
|
|
||||||
|
``_table``
|
||||||
|
The TCA table as found on top level of ``$GLOBALS['TCA']``.
|
||||||
|
|
||||||
|
This will auto filled for indexing through the provided indexers. Still you can
|
||||||
|
apply processors on results, where no information about the table exists anymore.
|
||||||
|
|
||||||
|
Possible Options:
|
||||||
|
|
||||||
|
``excludeFields``
|
||||||
|
Comma separated list of fields to not resolve relations for.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
plugin.tx_searchcore.settings.indexing.tt_content.dataProcessing {
|
||||||
|
1 = Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor
|
||||||
|
1 {
|
||||||
|
_table = tt_content
|
||||||
|
excludeFields = starttime, endtime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ Available DataProcessors
|
||||||
/configuration/dataProcessing/CopyToProcessor
|
/configuration/dataProcessing/CopyToProcessor
|
||||||
/configuration/dataProcessing/GeoPointProcessor
|
/configuration/dataProcessing/GeoPointProcessor
|
||||||
/configuration/dataProcessing/RemoveProcessor
|
/configuration/dataProcessing/RemoveProcessor
|
||||||
|
/configuration/dataProcessing/TcaRelationResolvingProcessor
|
||||||
|
|
||||||
.. _dataprocessors_plannedDataProcessors:
|
.. _dataprocessors_plannedDataProcessors:
|
||||||
|
|
||||||
|
@ -81,18 +82,3 @@ Planned DataProcessors
|
||||||
``Codappix\SearchCore\DataProcessing\ChannelProcessor``
|
``Codappix\SearchCore\DataProcessing\ChannelProcessor``
|
||||||
Will add a configurable channel to the record, e.g. if you have different areas in your
|
Will add a configurable channel to the record, e.g. if you have different areas in your
|
||||||
website like "products" and "infos".
|
website like "products" and "infos".
|
||||||
|
|
||||||
``Codappix\SearchCore\DataProcessing\RelationResolverProcessor``
|
|
||||||
Resolves all relations using the TCA.
|
|
||||||
This is currently done through indexer.
|
|
||||||
|
|
||||||
.. Of course you are able to provide further processors. Just implement
|
|
||||||
.. ``Codappix\SearchCore\DataProcessing\ProcessorInterface`` and use the FQCN (=Fully qualified
|
|
||||||
.. class name) as done in the examples above.
|
|
||||||
|
|
||||||
.. By implementing also the same interface as necessary for TYPO3
|
|
||||||
.. :ref:`t3tsref:cobj-fluidtemplate-properties-dataprocessing`, you are able to reuse the same code
|
|
||||||
.. also for Fluid to prepare the same record fetched from DB for your fluid.
|
|
||||||
|
|
||||||
.. Dependency injection is possible inside of processors, as we instantiate through extbase
|
|
||||||
.. ``ObjectManager``.
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ Composer
|
||||||
|
|
||||||
The extension can be installed through composer::
|
The extension can be installed through composer::
|
||||||
|
|
||||||
composer require "codappix/search_core" "~0.0.3"
|
composer require "codappix/search_core" "~0.0.4"
|
||||||
|
|
||||||
Note that you have to allow unstable packages:
|
Note that you have to allow unstable packages:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Codappix\SearchCore\Tests\Indexing\TcaIndexer;
|
namespace Codappix\SearchCore\Tests\Functional\DataProcessing;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
* Copyright (C) 2016 Daniel Siepmann <coding@daniel-siepmann.de>
|
||||||
|
@ -20,13 +20,13 @@ namespace Codappix\SearchCore\Tests\Indexing\TcaIndexer;
|
||||||
* 02110-1301, USA.
|
* 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Codappix\SearchCore\Domain\Index\TcaIndexer\TcaTableService;
|
use Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor;
|
||||||
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
use Codappix\SearchCore\Tests\Functional\AbstractFunctionalTestCase;
|
||||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||||
|
|
||||||
class RelationResolverTest extends AbstractFunctionalTestCase
|
class TcaRelationResolvingProcessorTest extends AbstractFunctionalTestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
|
@ -37,9 +37,8 @@ class RelationResolverTest extends AbstractFunctionalTestCase
|
||||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||||
$table = 'sys_file';
|
$table = 'sys_file';
|
||||||
|
|
||||||
$subject = $objectManager->get(TcaTableService::class, $table);
|
$subject = $objectManager->get(TcaRelationResolvingProcessor::class);
|
||||||
$record = BackendUtility::getRecord($table, 1);
|
$record = $subject->processData(BackendUtility::getRecord($table, 1), ['_table' => $table]);
|
||||||
$subject->prepareRecord($record);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
|
@ -60,9 +59,8 @@ class RelationResolverTest extends AbstractFunctionalTestCase
|
||||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||||
$table = 'tt_content';
|
$table = 'tt_content';
|
||||||
|
|
||||||
$subject = $objectManager->get(TcaTableService::class, $table);
|
$subject = $objectManager->get(TcaRelationResolvingProcessor::class);
|
||||||
$record = BackendUtility::getRecord($table, 1);
|
$record = $subject->processData(BackendUtility::getRecord($table, 1), ['_table' => $table]);
|
||||||
$subject->prepareRecord($record);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'Insert Plugin',
|
'Insert Plugin',
|
||||||
|
@ -80,9 +78,8 @@ class RelationResolverTest extends AbstractFunctionalTestCase
|
||||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||||
$table = 'tt_content';
|
$table = 'tt_content';
|
||||||
|
|
||||||
$subject = $objectManager->get(TcaTableService::class, $table);
|
$subject = $objectManager->get(TcaRelationResolvingProcessor::class);
|
||||||
$record = BackendUtility::getRecord($table, 1);
|
$record = $subject->processData(BackendUtility::getRecord($table, 1), ['_table' => $table]);
|
||||||
$subject->prepareRecord($record);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
|
@ -103,9 +100,8 @@ class RelationResolverTest extends AbstractFunctionalTestCase
|
||||||
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
|
||||||
$table = 'tt_content';
|
$table = 'tt_content';
|
||||||
|
|
||||||
$subject = $objectManager->get(TcaTableService::class, $table);
|
$subject = $objectManager->get(TcaRelationResolvingProcessor::class);
|
||||||
$record = BackendUtility::getRecord($table, 1);
|
$record = $subject->processData(BackendUtility::getRecord($table, 1), ['_table' => $table]);
|
||||||
$subject->prepareRecord($record);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
|
@ -22,6 +22,10 @@ plugin {
|
||||||
type = keyword
|
type = keyword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dataProcessing {
|
||||||
|
1 = Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pages {
|
pages {
|
||||||
|
@ -34,6 +38,10 @@ plugin {
|
||||||
type = keyword
|
type = keyword
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dataProcessing {
|
||||||
|
1 = Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,40 +77,4 @@ class TcaIndexerTest extends AbstractFunctionalTestCase
|
||||||
|
|
||||||
$objectManager->get(TcaIndexer::class, $tableService, $connection)->indexAllDocuments();
|
$objectManager->get(TcaIndexer::class, $tableService, $connection)->indexAllDocuments();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function sysLanguageIsKept()
|
|
||||||
{
|
|
||||||
$this->importDataSet('Tests/Functional/Fixtures/Indexing/TcaIndexer/KeepSysLanguageUid.xml');
|
|
||||||
$objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(ObjectManager::class);
|
|
||||||
$tableName = 'tt_content';
|
|
||||||
$tableService = $objectManager->get(
|
|
||||||
TcaTableServiceInterface::class,
|
|
||||||
$tableName,
|
|
||||||
$objectManager->get(RelationResolver::class),
|
|
||||||
$objectManager->get(ConfigurationContainerInterface::class)
|
|
||||||
);
|
|
||||||
|
|
||||||
$connection = $this->getMockBuilder(Elasticsearch::class)
|
|
||||||
->setMethods(['addDocuments'])
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$connection->expects($this->once())
|
|
||||||
->method('addDocuments')
|
|
||||||
->with(
|
|
||||||
$this->stringContains('tt_content'),
|
|
||||||
$this->callback(function ($documents) {
|
|
||||||
if ($this->isLegacyVersion()) {
|
|
||||||
return isset($documents[0]['sys_language_uid']) && $documents[0]['sys_language_uid'] === '2';
|
|
||||||
} else {
|
|
||||||
return isset($documents[0]['sys_language_uid']) && $documents[0]['sys_language_uid'] === 2;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
$objectManager->get(TcaIndexer::class, $tableService, $connection)->indexAllDocuments();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,14 +39,9 @@ abstract class AbstractUnitTestCase extends CoreTestCase
|
||||||
$this->singletonInstances = GeneralUtility::getSingletonInstances();
|
$this->singletonInstances = GeneralUtility::getSingletonInstances();
|
||||||
|
|
||||||
// Disable caching backends to make TYPO3 parts work in unit test mode.
|
// Disable caching backends to make TYPO3 parts work in unit test mode.
|
||||||
|
|
||||||
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
|
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
|
||||||
\TYPO3\CMS\Core\Cache\CacheManager::class
|
\TYPO3\CMS\Core\Cache\CacheManager::class
|
||||||
)->setCacheConfigurations([
|
)->setCacheConfigurations($this->getCacheConfiguration());
|
||||||
'extbase_object' => [
|
|
||||||
'backend' => \TYPO3\CMS\Core\Cache\Backend\NullBackend::class,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown()
|
||||||
|
@ -100,4 +95,25 @@ abstract class AbstractUnitTestCase extends CoreTestCase
|
||||||
{
|
{
|
||||||
return \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000;
|
return \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) < 8000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getCacheConfiguration() : array
|
||||||
|
{
|
||||||
|
$cacheConfiguration = [
|
||||||
|
'extbase_object' => [
|
||||||
|
'backend' => \TYPO3\CMS\Core\Cache\Backend\NullBackend::class,
|
||||||
|
],
|
||||||
|
'cache_runtime' => [
|
||||||
|
'backend' => \TYPO3\CMS\Core\Cache\Backend\NullBackend::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (class_exists(\TYPO3\CMS\Fluid\Core\Cache\FluidTemplateCache::class)) {
|
||||||
|
$cacheConfiguration['fluid_template'] = [
|
||||||
|
'backend' => \TYPO3\CMS\Core\Cache\Backend\NullBackend::class,
|
||||||
|
'frontend' => \TYPO3\CMS\Fluid\Core\Cache\FluidTemplateCache::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cacheConfiguration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,4 +6,6 @@ if (getenv('TYPO3_VERSION') === '~7.6') {
|
||||||
$filePath = '.Build/vendor/typo3/cms/typo3/sysext/core/Build/UnitTestsBootstrap.php';
|
$filePath = '.Build/vendor/typo3/cms/typo3/sysext/core/Build/UnitTestsBootstrap.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
|
||||||
require_once dirname(dirname(__DIR__)) . '/' . $filePath;
|
require_once dirname(dirname(__DIR__)) . '/' . $filePath;
|
||||||
|
|
169
Tests/Unit/DataProcessing/TcaRelationResolvingProcessorTest.php
Normal file
169
Tests/Unit/DataProcessing/TcaRelationResolvingProcessorTest.php
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
namespace Codappix\SearchCore\Tests\Unit\DataProcessing;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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 Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
|
||||||
|
use Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor;
|
||||||
|
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
|
||||||
|
use TYPO3\CMS\Extbase\Object\ObjectManager;
|
||||||
|
use \TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||||
|
|
||||||
|
class TcaRelationResolvingProcessorTest extends AbstractUnitTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var TcaRelationResolvingProcessor
|
||||||
|
*/
|
||||||
|
protected $subject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ConfigurationContainerInterface
|
||||||
|
*/
|
||||||
|
protected $configurationMock;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->configurationMock = $this->getMockBuilder(ConfigurationContainerInterface::class)->getMock();
|
||||||
|
|
||||||
|
GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\Container::class)
|
||||||
|
->registerImplementation(
|
||||||
|
ConfigurationContainerInterface::class,
|
||||||
|
get_class($this->configurationMock)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->subject = GeneralUtility::makeInstance(ObjectManager::class)
|
||||||
|
->get(TcaRelationResolvingProcessor::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function exceptionIsThrownIfTableIsNotConfigured()
|
||||||
|
{
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->subject->processData([], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function sysLanguageUidZeroIsKept()
|
||||||
|
{
|
||||||
|
$originalRecord = [
|
||||||
|
'sys_language_uid' => '0',
|
||||||
|
];
|
||||||
|
$record = [
|
||||||
|
'sys_language_uid' => 0,
|
||||||
|
];
|
||||||
|
$GLOBALS['TCA'] = [
|
||||||
|
'tt_content' => [
|
||||||
|
'ctrl' => [
|
||||||
|
'languageField' => 'sys_language_uid',
|
||||||
|
],
|
||||||
|
'columns' => [
|
||||||
|
'sys_language_uid' => [
|
||||||
|
'config' => [
|
||||||
|
'default' => 0,
|
||||||
|
'items' => [
|
||||||
|
[
|
||||||
|
'LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.allLanguages',
|
||||||
|
'-1',
|
||||||
|
'flags-multiple',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'renderType' => 'selectSingle',
|
||||||
|
'special' => 'languages',
|
||||||
|
'type' => 'select',
|
||||||
|
'exclude' => '1',
|
||||||
|
'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.language',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$configuration = [
|
||||||
|
'_table' => 'tt_content',
|
||||||
|
];
|
||||||
|
$record = $this->subject->processData($originalRecord, $configuration);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'sys_language_uid' => 0,
|
||||||
|
],
|
||||||
|
$record,
|
||||||
|
'sys_language_uid was not kept as zero.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function renderTypeInputDateTimeIsHandled()
|
||||||
|
{
|
||||||
|
$originalRecord = [
|
||||||
|
'endtime' => 99999999999,
|
||||||
|
'starttime' => 1523010960,
|
||||||
|
];
|
||||||
|
$record = $originalRecord;
|
||||||
|
$GLOBALS['TCA'] = [
|
||||||
|
'tt_content' => [
|
||||||
|
'columns' => [
|
||||||
|
'starttime' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'default' => 0,
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
],
|
||||||
|
'exclude' => true,
|
||||||
|
'l10n_display' => 'defaultAsReadonly',
|
||||||
|
'l10n_mode' => 'exclude',
|
||||||
|
'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
],
|
||||||
|
'endtime' => [
|
||||||
|
'config' => [
|
||||||
|
'type' => 'input',
|
||||||
|
'default' => 0,
|
||||||
|
'eval' => 'datetime,int',
|
||||||
|
'renderType' => 'inputDateTime',
|
||||||
|
],
|
||||||
|
'exclude' => true,
|
||||||
|
'l10n_display' => 'defaultAsReadonly',
|
||||||
|
'l10n_mode' => 'exclude',
|
||||||
|
'label' => 'LLL:EXT:lang/Resources/Private/Language/locallang_general.xlf:LGL.starttime',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$configuration = [
|
||||||
|
'_table' => 'tt_content',
|
||||||
|
'excludeFields' => 'starttime',
|
||||||
|
];
|
||||||
|
$record = $this->subject->processData($originalRecord, $configuration);
|
||||||
|
$this->assertSame(
|
||||||
|
[
|
||||||
|
'endtime' => '16-11-38 09:46',
|
||||||
|
'starttime' => 1523010960,
|
||||||
|
],
|
||||||
|
$record,
|
||||||
|
'Exclude fields were not respected.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ $EM_CONF[$_EXTKEY] = [
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'state' => 'beta',
|
'state' => 'beta',
|
||||||
'version' => '0.0.3',
|
'version' => '0.0.4',
|
||||||
'author' => 'Daniel Siepmann',
|
'author' => 'Daniel Siepmann',
|
||||||
'author_email' => 'coding@daniel-siepmann.de',
|
'author_email' => 'coding@daniel-siepmann.de',
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue