first commit
This commit is contained in:
commit
866c63cc63
30
.editorconfig
Normal file
30
.editorconfig
Normal file
@ -0,0 +1,30 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
# TYPO3 Standard: https://github.com/TYPO3/TYPO3.CMS/blob/master/.editorconfig
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# ReST-Files
|
||||
[*.rst]
|
||||
indent_size = 3
|
||||
max_line_length = 80
|
||||
|
||||
# YAML-Files
|
||||
[*.{yaml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
# TypoScript
|
||||
[*.{typoscript,tsconfig}]
|
||||
indent_size = 2
|
||||
|
||||
# XLF/XML-Files
|
||||
[*.{xlf,xml}]
|
||||
indent_style = tab
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
custom: ["https://wappler.systems/"]
|
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG] Please add a speaking title"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Used versions (please complete the following information):**
|
||||
- TYPO3 Version: [e.g. 10.4.13]
|
||||
- Browser: [e.g. chrome, safari]
|
||||
- EXT:meilisearch Version: [e.g. 10.0.0]
|
||||
- Used Meilisearch Version: [e.g. 8.8.0]
|
||||
- PHP Version: [e.g. 7.4.0]
|
||||
- MySQL Version: [e.g. 8.0.0]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE] Please describe your feature wish here"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
**Target versions**
|
||||
Please add the EXT:meilisearch target versions here.
|
10
.github/ISSUE_TEMPLATE/task.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/task.md
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Task
|
||||
about: This template is used to decribe regular tasks
|
||||
title: "[TASK]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**What should be done in the scope of this task?**
|
14
.github/no-response.yml
vendored
Normal file
14
.github/no-response.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# Configuration for probot-no-response - https://github.com/probot/no-response
|
||||
|
||||
# Number of days of inactivity before an Issue is closed for lack of response
|
||||
daysUntilClose: 28
|
||||
# Label requiring a response
|
||||
responseRequiredLabel: more-information-needed
|
||||
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because there has been no response
|
||||
to our request for more information from the original author. With only the
|
||||
information that is currently in the issue, we don't have enough information
|
||||
to take action. Please reach out if you have or find the answers we need so
|
||||
that we can investigate further.
|
||||
|
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
.buildpath
|
||||
.project/
|
||||
.settings/
|
||||
.idea/
|
||||
atlassian-ide-plugin.xml
|
||||
.DS_Store
|
||||
index.php
|
||||
typo3
|
||||
typo3_src
|
||||
typo3conf
|
||||
typo3temp
|
||||
uploads
|
||||
vendor
|
||||
/composer.lock
|
||||
.Build
|
||||
Documentation/_make
|
||||
|
||||
.php_cs.cache
|
217
Classes/AbstractDataHandlerListener.php
Normal file
217
Classes/AbstractDataHandlerListener.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch;
|
||||
|
||||
/***************************************************************
|
||||
* 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\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Database\QueryGenerator;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Changes in TYPO3 have an impact on the solr content and are caught
|
||||
* by the GarbageCollector and RecordMonitor. Both act as a TCE Main Hook.
|
||||
*
|
||||
* This base class is used to share functionality that are needed for both
|
||||
* to perform the changes in the data handler on the solr index.
|
||||
*
|
||||
* @author Timo Schmidt <timo.schmidt@dkd.de>
|
||||
*/
|
||||
abstract class AbstractDataHandlerListener
|
||||
{
|
||||
/**
|
||||
* Reference to the configuration manager
|
||||
*
|
||||
* @var \WapplerSystems\Meilisearch\Domain\Index\Queue\RecordMonitor\Helper\ConfigurationAwareRecordService
|
||||
*/
|
||||
protected $configurationAwareRecordService;
|
||||
|
||||
/**
|
||||
* @var FrontendEnvironment
|
||||
*/
|
||||
protected $frontendEnvironment = null;
|
||||
|
||||
/**
|
||||
* AbstractDataHandlerListener constructor.
|
||||
* @param ConfigurationAwareRecordService|null $recordService
|
||||
*/
|
||||
public function __construct(ConfigurationAwareRecordService $recordService = null, FrontendEnvironment $frontendEnvironment = null)
|
||||
{
|
||||
$this->configurationAwareRecordService = $recordService ?? GeneralUtility::makeInstance(ConfigurationAwareRecordService::class);
|
||||
$this->frontendEnvironment = $frontendEnvironment ?? GeneralUtility::makeInstance(FrontendEnvironment::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllRelevantFieldsForCurrentState()
|
||||
{
|
||||
$allCurrentStateFieldnames = [];
|
||||
|
||||
foreach ($this->getUpdateSubPagesRecursiveTriggerConfiguration() as $triggerConfiguration) {
|
||||
if (!isset($triggerConfiguration['currentState']) || !is_array($triggerConfiguration['currentState'])) {
|
||||
// when no "currentState" configuration for the trigger exists we can skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
// we collect the currentState fields to return a unique list of all fields
|
||||
$allCurrentStateFieldnames = array_merge($allCurrentStateFieldnames, array_keys($triggerConfiguration['currentState']));
|
||||
}
|
||||
|
||||
return array_unique($allCurrentStateFieldnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the extend to subpages flag was set, we determine the affected subpages and return them.
|
||||
*
|
||||
* @param int $pageId
|
||||
* @return array
|
||||
*/
|
||||
protected function getSubPageIds($pageId)
|
||||
{
|
||||
/** @var $queryGenerator \TYPO3\CMS\Core\Database\QueryGenerator */
|
||||
$queryGenerator = GeneralUtility::makeInstance(QueryGenerator::class);
|
||||
|
||||
// here we retrieve only the subpages of this page because the permission clause is not evaluated
|
||||
// on the root node.
|
||||
$permissionClause = ' 1 ' . BackendUtility::BEenableFields('pages');
|
||||
$treePageIdList = $queryGenerator->getTreeList($pageId, 20, 0, $permissionClause);
|
||||
$treePageIds = array_map('intval', explode(',', $treePageIdList));
|
||||
|
||||
// the first one can be ignored because this is the page itself
|
||||
array_shift($treePageIds);
|
||||
|
||||
return $treePageIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a page update will trigger a recursive update of pages
|
||||
*
|
||||
* This can either be the case if some $changedFields are part of the RecursiveUpdateTriggerConfiguration or
|
||||
* columns have explicitly been configured via plugin.tx_meilisearch.index.queue.recursiveUpdateFields
|
||||
*
|
||||
* @param int $pageId
|
||||
* @param array $changedFields
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRecursivePageUpdateRequired($pageId, $changedFields)
|
||||
{
|
||||
// First check RecursiveUpdateTriggerConfiguration
|
||||
$isRecursiveUpdateRequired = $this->isRecursiveUpdateRequired($pageId, $changedFields);
|
||||
// If RecursiveUpdateTriggerConfiguration is false => check if changeFields are part of recursiveUpdateFields
|
||||
if ($isRecursiveUpdateRequired === false) {
|
||||
$solrConfiguration = $this->frontendEnvironment->getSolrConfigurationFromPageId($pageId);
|
||||
$indexQueueConfigurationName = $this->configurationAwareRecordService->getIndexingConfigurationName('pages', $pageId, $solrConfiguration);
|
||||
if ($indexQueueConfigurationName === null) {
|
||||
return false;
|
||||
}
|
||||
$updateFields = $solrConfiguration->getIndexQueueConfigurationRecursiveUpdateFields($indexQueueConfigurationName);
|
||||
|
||||
// Check if no additional fields have been defined and then skip recursive update
|
||||
if (empty($updateFields)) {
|
||||
return false;
|
||||
}
|
||||
// If the recursiveUpdateFields configuration is not part of the $changedFields skip recursive update
|
||||
if (!array_intersect_key($changedFields, $updateFields)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $pageId
|
||||
* @param array $changedFields
|
||||
* @return bool
|
||||
*/
|
||||
protected function isRecursiveUpdateRequired($pageId, $changedFields)
|
||||
{
|
||||
$fieldsForCurrentState = $this->getAllRelevantFieldsForCurrentState();
|
||||
$fieldListToRetrieve = implode(',', $fieldsForCurrentState);
|
||||
$page = BackendUtility::getRecord('pages', $pageId, $fieldListToRetrieve, '', false);
|
||||
foreach ($this->getUpdateSubPagesRecursiveTriggerConfiguration() as $triggerConfiguration) {
|
||||
$allCurrentStateFieldsMatch = $this->getAllCurrentStateFieldsMatch($triggerConfiguration, $page);
|
||||
$allChangeSetValuesMatch = $this->getAllChangeSetValuesMatch($triggerConfiguration, $changedFields);
|
||||
|
||||
$aMatchingTriggerHasBeenFound = $allCurrentStateFieldsMatch && $allChangeSetValuesMatch;
|
||||
if ($aMatchingTriggerHasBeenFound) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $triggerConfiguration
|
||||
* @param array $pageRecord
|
||||
* @return bool
|
||||
*/
|
||||
protected function getAllCurrentStateFieldsMatch($triggerConfiguration, $pageRecord)
|
||||
{
|
||||
$triggerConfigurationHasNoCurrentStateConfiguration = !array_key_exists('currentState', $triggerConfiguration);
|
||||
if ($triggerConfigurationHasNoCurrentStateConfiguration) {
|
||||
return true;
|
||||
}
|
||||
$diff = array_diff_assoc($triggerConfiguration['currentState'], $pageRecord);
|
||||
return empty($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $triggerConfiguration
|
||||
* @param array $changedFields
|
||||
* @return bool
|
||||
*/
|
||||
protected function getAllChangeSetValuesMatch($triggerConfiguration, $changedFields)
|
||||
{
|
||||
$triggerConfigurationHasNoChangeSetStateConfiguration = !array_key_exists('changeSet', $triggerConfiguration);
|
||||
if ($triggerConfigurationHasNoChangeSetStateConfiguration) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$diff = array_diff_assoc($triggerConfiguration['changeSet'], $changedFields);
|
||||
return empty($diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of this method need to retrieve a configuration to determine which record data
|
||||
* and change combination required a recursive change.
|
||||
*
|
||||
* The structure needs to be:
|
||||
*
|
||||
* [
|
||||
* [
|
||||
* 'currentState' => ['fieldName1' => 'value1'],
|
||||
* 'changeSet' => ['fieldName1' => 'value1']
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* When the all values of the currentState AND all values of the changeSet match, a recursive update
|
||||
* will be triggered.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function getUpdateSubPagesRecursiveTriggerConfiguration();
|
||||
}
|
228
Classes/Access/Rootline.php
Normal file
228
Classes/Access/Rootline.php
Normal file
@ -0,0 +1,228 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Access;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2011-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 TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\RootlineUtility;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
|
||||
/**
|
||||
* "Access Rootline", represents all pages and specifically those setting
|
||||
* frontend user group access restrictions in a page's rootline.
|
||||
*
|
||||
* The access rootline only contains pages which set frontend user access
|
||||
* restrictions and extend them to sub-pages. The format is as follows:
|
||||
*
|
||||
* pageId1:group1,group2/pageId2:group3/c:group1,group4,groupN
|
||||
*
|
||||
* The single elements of the access rootline are separated by a slash
|
||||
* character. All but the last elements represent pages, the last element
|
||||
* defines the access restrictions applied to the page's content elements
|
||||
* and records shown on the page.
|
||||
* Each page element is composed by the page ID of the page setting frontend
|
||||
* user access restrictions, a colon, and a comma separated list of frontend
|
||||
* user group IDs restricting access to the page.
|
||||
* The content access element does not have a page ID, instead it replaces
|
||||
* the ID by a lower case C.
|
||||
*
|
||||
* The groups for page elements are compared using OR, so the user needs to be
|
||||
* a member of only one of the groups listed for a page. The elements are
|
||||
* checked combined using AND, so the user must be member of at least one
|
||||
* group in each page element. However, the groups in the content access
|
||||
* element are checked using AND. So the user must be member of all the groups
|
||||
* listed in the content access element to see the document.
|
||||
*
|
||||
* An access rootline for a generic record could instead be short like this:
|
||||
*
|
||||
* r:group1,group2,groupN
|
||||
*
|
||||
* In this case the lower case R tells us that we're dealing with a record
|
||||
* like tt_news or the like. For records the groups are checked using OR
|
||||
* instead of using AND as it would be the case with content elements.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class Rootline
|
||||
{
|
||||
|
||||
/**
|
||||
* Delimiter for page and content access right elements in the rootline.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const ELEMENT_DELIMITER = '/';
|
||||
|
||||
/**
|
||||
* Storage for access rootline elements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rootlineElements = [];
|
||||
|
||||
/**
|
||||
* Constructor, turns a string representation of an access rootline into an
|
||||
* object representation.
|
||||
*
|
||||
* @param string $accessRootline Access Rootline String representation.
|
||||
*/
|
||||
public function __construct($accessRootline = null)
|
||||
{
|
||||
if (!is_null($accessRootline)) {
|
||||
$rawRootlineElements = explode(self::ELEMENT_DELIMITER, $accessRootline);
|
||||
foreach ($rawRootlineElements as $rawRootlineElement) {
|
||||
try {
|
||||
$this->push(GeneralUtility::makeInstance(RootlineElement::class, /** @scrutinizer ignore-type */ $rawRootlineElement));
|
||||
} catch (RootlineElementFormatException $e) {
|
||||
// just ignore the faulty element for now, might log this later
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Access Rootline Element to the end of the rootline.
|
||||
*
|
||||
* @param RootlineElement $rootlineElement Element to add.
|
||||
*/
|
||||
public function push(RootlineElement $rootlineElement)
|
||||
{
|
||||
$lastElementIndex = max(0, (count($this->rootlineElements) - 1));
|
||||
|
||||
if (!empty($this->rootlineElements[$lastElementIndex])) {
|
||||
if ($this->rootlineElements[$lastElementIndex]->getType() == RootlineElement::ELEMENT_TYPE_CONTENT) {
|
||||
throw new RootlineElementFormatException(
|
||||
'Can not add an element to an Access Rootline whose\' last element is a content type element.',
|
||||
1294422132
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->rootlineElements[$lastElementIndex]->getType() == RootlineElement::ELEMENT_TYPE_RECORD) {
|
||||
throw new RootlineElementFormatException(
|
||||
'Can not add an element to an Access Rootline whose\' last element is a record type element.',
|
||||
1308343423
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$this->rootlineElements[] = $rootlineElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Access Rootline for a specific page Id.
|
||||
*
|
||||
* @param int $pageId The page Id to generate the Access Rootline for.
|
||||
* @param string $mountPointParameter The mount point parameter for generating the rootline.
|
||||
* @return \WapplerSystems\Meilisearch\Access\Rootline Access Rootline for the given page Id.
|
||||
*/
|
||||
public static function getAccessRootlineByPageId(
|
||||
$pageId,
|
||||
$mountPointParameter = ''
|
||||
) {
|
||||
$accessRootline = GeneralUtility::makeInstance(Rootline::class);
|
||||
$rootlineUtility = GeneralUtility::makeInstance(RootlineUtility::class, $pageId, $mountPointParameter);
|
||||
try {
|
||||
$rootline = $rootlineUtility->get();
|
||||
} catch (\RuntimeException $e) {
|
||||
$rootline = [];
|
||||
}
|
||||
$rootline = array_reverse($rootline);
|
||||
// parent pages
|
||||
foreach ($rootline as $pageRecord) {
|
||||
if ($pageRecord['fe_group']
|
||||
&& $pageRecord['extendToSubpages']
|
||||
&& $pageRecord['uid'] != $pageId
|
||||
) {
|
||||
$accessRootline->push(GeneralUtility::makeInstance(
|
||||
RootlineElement::class,
|
||||
/** @scrutinizer ignore-type */ $pageRecord['uid'] . RootlineElement::PAGE_ID_GROUP_DELIMITER . $pageRecord['fe_group']
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/** @var $pageSelector PageRepository */
|
||||
$pageSelector = GeneralUtility::makeInstance(PageRepository::class);
|
||||
|
||||
// current page
|
||||
$currentPageRecord = $pageSelector->getPage($pageId, true);
|
||||
if ($currentPageRecord['fe_group']) {
|
||||
$accessRootline->push(GeneralUtility::makeInstance(
|
||||
RootlineElement::class,
|
||||
/** @scrutinizer ignore-type */ $currentPageRecord['uid'] . RootlineElement::PAGE_ID_GROUP_DELIMITER . $currentPageRecord['fe_group']
|
||||
));
|
||||
}
|
||||
|
||||
return $accessRootline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the access rootline.
|
||||
*
|
||||
* @return string String representation of the access rootline.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$stringElements = [];
|
||||
|
||||
foreach ($this->rootlineElements as $rootlineElement) {
|
||||
$stringElements[] = (string)$rootlineElement;
|
||||
}
|
||||
|
||||
return implode(self::ELEMENT_DELIMITER, $stringElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a the groups in the Access Rootline.
|
||||
*
|
||||
* @return array An array of sorted, unique user group IDs required to access a page.
|
||||
*/
|
||||
public function getGroups()
|
||||
{
|
||||
$groups = [];
|
||||
|
||||
foreach ($this->rootlineElements as $rootlineElement) {
|
||||
$rootlineElementGroups = $rootlineElement->getGroups();
|
||||
$groups = array_merge($groups, $rootlineElementGroups);
|
||||
}
|
||||
|
||||
$groups = $this->cleanGroupArray($groups);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans an array of frontend user group IDs. Removes duplicates and sorts
|
||||
* the array.
|
||||
*
|
||||
* @param array $groups An array of frontend user group IDs
|
||||
* @return array An array of cleaned frontend user group IDs, unique, sorted.
|
||||
*/
|
||||
public static function cleanGroupArray(array $groups)
|
||||
{
|
||||
$groups = array_unique($groups); // removes duplicates
|
||||
sort($groups, SORT_NUMERIC); // sort
|
||||
|
||||
return $groups;
|
||||
}
|
||||
}
|
187
Classes/Access/RootlineElement.php
Normal file
187
Classes/Access/RootlineElement.php
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Access;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2011-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 TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* An element in the "Access Rootline". Represents the frontend user group
|
||||
* access restrictions for a page, a page's content, or a generic record.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class RootlineElement
|
||||
{
|
||||
|
||||
/**
|
||||
* Page access rootline element.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const ELEMENT_TYPE_PAGE = 1;
|
||||
|
||||
/**
|
||||
* Content access rootline element.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const ELEMENT_TYPE_CONTENT = 2;
|
||||
|
||||
/**
|
||||
* Record access rootline element.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const ELEMENT_TYPE_RECORD = 3;
|
||||
|
||||
/**
|
||||
* Delimiter between the page ID and the groups set for a page.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PAGE_ID_GROUP_DELIMITER = ':';
|
||||
|
||||
/**
|
||||
* Access type, either page (default) or content. Depending on the type,
|
||||
* access is granted differently. For pages the user must meet at least one
|
||||
* group requirement, for content all group requirements must be met.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $type = self::ELEMENT_TYPE_PAGE;
|
||||
|
||||
/**
|
||||
* Page Id for the element. NULL for the content type.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $pageId = null;
|
||||
|
||||
/**
|
||||
* Set of access groups assigned to the element.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $accessGroups = [];
|
||||
|
||||
/**
|
||||
* Constructor for RootlineElement.
|
||||
*
|
||||
* @param string $element String representation of an element in the access rootline, usually of the form pageId:commaSeparatedPageAccessGroups
|
||||
* @throws RootlineElementFormatException on wrong access format.
|
||||
*/
|
||||
public function __construct($element)
|
||||
{
|
||||
$elementAccess = explode(self::PAGE_ID_GROUP_DELIMITER, $element);
|
||||
|
||||
if (count($elementAccess) === 1 || $elementAccess[0] === 'c') {
|
||||
// the content access groups part of the access rootline
|
||||
$this->type = self::ELEMENT_TYPE_CONTENT;
|
||||
|
||||
if (count($elementAccess) === 1) {
|
||||
$elementGroups = $elementAccess[0];
|
||||
} else {
|
||||
$elementGroups = $elementAccess[1];
|
||||
}
|
||||
} elseif ($elementAccess[0] === 'r') {
|
||||
// record element type
|
||||
if (count($elementAccess) !== 2) {
|
||||
throw new RootlineElementFormatException(
|
||||
'Wrong Access Rootline Element format for a record type element.',
|
||||
1308342937
|
||||
);
|
||||
}
|
||||
|
||||
$this->type = self::ELEMENT_TYPE_RECORD;
|
||||
$elementGroups = $elementAccess[1];
|
||||
} else {
|
||||
// page element type
|
||||
if (count($elementAccess) !== 2 || !is_numeric($elementAccess[0])) {
|
||||
throw new RootlineElementFormatException(
|
||||
'Wrong Access Rootline Element format for a page type element.',
|
||||
1294421105
|
||||
);
|
||||
}
|
||||
|
||||
$this->pageId = intval($elementAccess[0]);
|
||||
$elementGroups = $elementAccess[1];
|
||||
}
|
||||
|
||||
$this->accessGroups = GeneralUtility::intExplode(',', $elementGroups);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the String representation of an access rootline element.
|
||||
*
|
||||
* @return string Access Rootline Element string representation
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
$rootlineElement = '';
|
||||
|
||||
if ($this->type == self::ELEMENT_TYPE_CONTENT) {
|
||||
$rootlineElement .= 'c';
|
||||
} elseif ($this->type == self::ELEMENT_TYPE_RECORD) {
|
||||
$rootlineElement .= 'r';
|
||||
} else {
|
||||
$rootlineElement .= $this->pageId;
|
||||
}
|
||||
|
||||
$rootlineElement .= self::PAGE_ID_GROUP_DELIMITER;
|
||||
$rootlineElement .= implode(',', $this->accessGroups);
|
||||
|
||||
return $rootlineElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the access rootline element's type.
|
||||
*
|
||||
* @return int ELEMENT_TYPE_PAGE for page, ELEMENT_TYPE_CONTENT for content access rootline elements
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the page Id for page type elements.
|
||||
*
|
||||
* @return int Page Id.
|
||||
*/
|
||||
public function getPageId()
|
||||
{
|
||||
return $this->pageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element's access group restrictions.
|
||||
*
|
||||
* @return array Array of user group Ids
|
||||
*/
|
||||
public function getGroups()
|
||||
{
|
||||
return $this->accessGroups;
|
||||
}
|
||||
}
|
34
Classes/Access/RootlineElementFormatException.php
Normal file
34
Classes/Access/RootlineElementFormatException.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Access;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2011-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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Signals a wrong format for the access definition of a page or the content.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class RootlineElementFormatException extends \InvalidArgumentException
|
||||
{
|
||||
}
|
126
Classes/AdditionalFieldsIndexer.php
Normal file
126
Classes/AdditionalFieldsIndexer.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2011-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\ContentObject\ContentObjectService;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Document\Document;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Additional fields indexer.
|
||||
*
|
||||
* @todo Move this to an Index Queue frontend helper
|
||||
*
|
||||
* Adds page document fields as configured in
|
||||
* plugin.tx_meilisearch.index.additionalFields.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class AdditionalFieldsIndexer implements SubstitutePageIndexer
|
||||
{
|
||||
|
||||
/**
|
||||
* @var TypoScriptConfiguration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $additionalIndexingFields = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $additionalFieldNames = [];
|
||||
|
||||
/**
|
||||
* @var ContentObjectService
|
||||
*/
|
||||
protected $contentObjectService = null;
|
||||
|
||||
/**
|
||||
* @param TypoScriptConfiguration $configuration
|
||||
* @param ContentObjectService $contentObjectService
|
||||
*/
|
||||
public function __construct(TypoScriptConfiguration $configuration = null, ContentObjectService $contentObjectService = null)
|
||||
{
|
||||
$this->configuration = $configuration === null ? Util::getSolrConfiguration() : $configuration;
|
||||
$this->additionalIndexingFields = $this->configuration->getIndexAdditionalFieldsConfiguration();
|
||||
$this->additionalFieldNames = $this->configuration->getIndexMappedAdditionalFieldNames();
|
||||
$this->contentObjectService = $contentObjectService === null ? GeneralUtility::makeInstance(ContentObjectService::class) : $contentObjectService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a substitute document for the currently being indexed page.
|
||||
*
|
||||
* Uses the original document and adds fields as defined in
|
||||
* plugin.tx_meilisearch.index.additionalFields.
|
||||
*
|
||||
* @param Document $pageDocument The original page document.
|
||||
* @return Document A Apache Solr Document object that replace the default page document
|
||||
*/
|
||||
public function getPageDocument(Document $pageDocument)
|
||||
{
|
||||
$substitutePageDocument = clone $pageDocument;
|
||||
$additionalFields = $this->getAdditionalFields();
|
||||
|
||||
foreach ($additionalFields as $fieldName => $fieldValue) {
|
||||
if (!isset($pageDocument->{$fieldName})) {
|
||||
// making sure we only _add_ new fields
|
||||
$substitutePageDocument->setField($fieldName, $fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $substitutePageDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the additional fields as an array mapping field names to values.
|
||||
*
|
||||
* @return array An array mapping additional field names to their values.
|
||||
*/
|
||||
protected function getAdditionalFields()
|
||||
{
|
||||
$additionalFields = [];
|
||||
|
||||
foreach ($this->additionalFieldNames as $additionalFieldName) {
|
||||
$additionalFields[$additionalFieldName] = $this->getFieldValue($additionalFieldName);
|
||||
}
|
||||
|
||||
return $additionalFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the page's cObj instance to resolve the additional field's value.
|
||||
*
|
||||
* @param string $fieldName The name of the field to get.
|
||||
* @return string The field's value.
|
||||
*/
|
||||
protected function getFieldValue($fieldName)
|
||||
{
|
||||
return $this->contentObjectService->renderSingleContentObjectByArrayAndKey($this->additionalIndexingFields, $fieldName);
|
||||
}
|
||||
}
|
49
Classes/AdditionalPageIndexer.php
Normal file
49
Classes/AdditionalPageIndexer.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-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.
|
||||
* 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 WapplerSystems\Meilisearch\System\Solr\Document\Document;
|
||||
|
||||
/**
|
||||
* Interface that defines the method an indexer must implement to provide
|
||||
* additional documents to index for a page being indexed.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
interface AdditionalPageIndexer
|
||||
{
|
||||
|
||||
/**
|
||||
* Provides additional documents that should be indexed together with a page.
|
||||
*
|
||||
* @param Document $pageDocument The original page document.
|
||||
* @param array $allDocuments An array containing all the documents collected until here, including the page document
|
||||
* @return array An array of additional \WapplerSystems\Meilisearch\System\Solr\Document\Document objects
|
||||
*/
|
||||
public function getAdditionalPageDocuments(Document $pageDocument, array $allDocuments);
|
||||
}
|
58
Classes/Api.php
Normal file
58
Classes/Api.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2012-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!
|
||||
***************************************************************/
|
||||
|
||||
/**
|
||||
* Remote API related methods
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks whether a string is a valid API key.
|
||||
*
|
||||
* @param string $apiKey API key to check for validity
|
||||
* @return bool TRUE if the API key is valid, FALSE otherwise
|
||||
*/
|
||||
public static function isValidApiKey($apiKey)
|
||||
{
|
||||
return ($apiKey === self::getApiKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the API key for the REST API
|
||||
*
|
||||
* @return string API key for this installation
|
||||
*/
|
||||
public static function getApiKey()
|
||||
{
|
||||
return sha1(
|
||||
$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] .
|
||||
'tx_meilisearch_api'
|
||||
);
|
||||
}
|
||||
}
|
213
Classes/Backend/IndexingConfigurationSelectorField.php
Normal file
213
Classes/Backend/IndexingConfigurationSelectorField.php
Normal file
@ -0,0 +1,213 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch\Backend;
|
||||
|
||||
/***************************************************************
|
||||
* 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\Domain\Site\Site;
|
||||
use TYPO3\CMS\Backend\Form\FormResultCompiler;
|
||||
use TYPO3\CMS\Backend\Form\NodeFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Index Queue indexing configuration selector form field.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class IndexingConfigurationSelectorField
|
||||
{
|
||||
|
||||
/**
|
||||
* Site used to determine indexing configurations
|
||||
*
|
||||
* @var Site
|
||||
*/
|
||||
protected $site;
|
||||
|
||||
/**
|
||||
* Form element name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $formElementName = 'tx_meilisearch-index-queue-indexing-configuration-selector';
|
||||
|
||||
/**
|
||||
* Selected values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $selectedValues = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Site $site The site to use to determine indexing configurations
|
||||
*/
|
||||
public function __construct(Site $site = null)
|
||||
{
|
||||
$this->site = $site;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the form element name.
|
||||
*
|
||||
* @param string $formElementName Form element name
|
||||
*/
|
||||
public function setFormElementName($formElementName)
|
||||
{
|
||||
$this->formElementName = $formElementName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form element name.
|
||||
*
|
||||
* @return string form element name
|
||||
*/
|
||||
public function getFormElementName()
|
||||
{
|
||||
return $this->formElementName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected values.
|
||||
*
|
||||
* @param array $selectedValues
|
||||
*/
|
||||
public function setSelectedValues(array $selectedValues)
|
||||
{
|
||||
$this->selectedValues = $selectedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the selected values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSelectedValues()
|
||||
{
|
||||
return $this->selectedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a field to select which indexing configurations to initialize.
|
||||
*
|
||||
* Uses \TYPO3\CMS\Backend\Form\FormEngine.
|
||||
*
|
||||
* @return string Markup for the select field
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
// transform selected values into the format used by TCEforms
|
||||
$selectedValues = $this->selectedValues;
|
||||
$tablesToIndex = $this->getIndexQueueConfigurationTableMap();
|
||||
|
||||
$formField = $this->renderSelectCheckbox($this->buildSelectorItems($tablesToIndex), $selectedValues);
|
||||
|
||||
// need to wrap the field in a TCEforms table to make the CSS apply
|
||||
$form[] = '<div class="typo3-TCEforms tx_meilisearch-TCEforms">';
|
||||
$form[] = $formField;
|
||||
$form[] = '</div>';
|
||||
|
||||
return implode(LF, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a map of indexing configuration names to tables to to index.
|
||||
*
|
||||
* @return array Indexing configuration to database table map
|
||||
*/
|
||||
protected function getIndexQueueConfigurationTableMap()
|
||||
{
|
||||
$indexingTableMap = [];
|
||||
|
||||
$solrConfiguration = $this->site->getSolrConfiguration();
|
||||
$configurationNames = $solrConfiguration->getEnabledIndexQueueConfigurationNames();
|
||||
foreach ($configurationNames as $configurationName) {
|
||||
$indexingTableMap[$configurationName] = $solrConfiguration->getIndexQueueTableNameOrFallbackToConfigurationName($configurationName);
|
||||
}
|
||||
|
||||
return $indexingTableMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the items to render in the TCEforms select field.
|
||||
*
|
||||
* @param array $tablesToIndex A map of indexing configuration to database tables
|
||||
*
|
||||
* @return array Selectable items for the TCEforms select field
|
||||
*/
|
||||
protected function buildSelectorItems(array $tablesToIndex)
|
||||
{
|
||||
$selectorItems = [];
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
|
||||
foreach ($tablesToIndex as $configurationName => $tableName) {
|
||||
$icon = $iconFactory->mapRecordTypeToIconIdentifier($tableName, []);
|
||||
|
||||
$labelTableName = '';
|
||||
if ($configurationName !== $tableName) {
|
||||
$labelTableName = ' (' . $tableName . ')';
|
||||
}
|
||||
|
||||
$selectorItems[] = [$configurationName . $labelTableName, $configurationName, $icon];
|
||||
}
|
||||
|
||||
return $selectorItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param string $selectedValues
|
||||
*
|
||||
* @return string
|
||||
* @throws \TYPO3\CMS\Backend\Form\Exception
|
||||
*/
|
||||
protected function renderSelectCheckbox($items, $selectedValues)
|
||||
{
|
||||
$parameterArray = [
|
||||
'fieldChangeFunc' => [],
|
||||
'itemFormElName' => $this->formElementName,
|
||||
'itemFormElValue' => $selectedValues,
|
||||
'fieldConf' => ['config' => ['items' => $items]],
|
||||
'fieldTSConfig' => ['noMatchingValue_label' => '']
|
||||
];
|
||||
|
||||
$nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
|
||||
$options = [
|
||||
'renderType' => 'selectCheckBox', 'table' => 'tx_meilisearch_classes_backend_indexingconfigurationselector',
|
||||
'fieldName' => 'additionalFields', 'databaseRow' => [], 'parameterArray' => $parameterArray
|
||||
];
|
||||
$options['parameterArray']['fieldConf']['config']['items'] = $items;
|
||||
$options['parameterArray']['fieldTSConfig']['noMatchingValue_label'] = '';
|
||||
|
||||
$selectCheckboxResult = $nodeFactory->create($options)->render();
|
||||
$formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
|
||||
$formResultCompiler->mergeResult($selectCheckboxResult);
|
||||
|
||||
$formHtml = isset($selectCheckboxResult['html']) ? $selectCheckboxResult['html'] : '';
|
||||
$content = $formResultCompiler->addCssFiles() . $formHtml . $formResultCompiler->printNeededJSFunctions();
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
72
Classes/Backend/SiteSelectorField.php
Normal file
72
Classes/Backend/SiteSelectorField.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace WapplerSystems\Meilisearch\Backend;
|
||||
|
||||
/***************************************************************
|
||||
* 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\Domain\Site\SiteRepository;
|
||||
use WapplerSystems\Meilisearch\Domain\Site\Site;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* SiteSelectorField
|
||||
*
|
||||
* Responsible for generating SiteSelectorField
|
||||
*
|
||||
* @author Thomas Hohn <tho@systime.dk>
|
||||
*/
|
||||
class SiteSelectorField
|
||||
{
|
||||
/**
|
||||
* Creates a dropdown selector of available TYPO3 sites with Solr configured.
|
||||
*
|
||||
* @param string $selectorName Name to be used in the select's name attribute
|
||||
* @param Site $selectedSite Optional, currently selected site
|
||||
* @return string Site selector HTML code
|
||||
*/
|
||||
public function getAvailableSitesSelector(
|
||||
$selectorName,
|
||||
Site $selectedSite = null
|
||||
) {
|
||||
$siteRepository = GeneralUtility::makeInstance(SiteRepository::class);
|
||||
|
||||
$sites = $siteRepository->getAvailableSites();
|
||||
$selector = '<select name="' . htmlspecialchars($selectorName) . '" class="form-control">';
|
||||
|
||||
foreach ($sites as $site) {
|
||||
$selectedAttribute = '';
|
||||
if ($selectedSite !== null && $site->getRootPageId() === $selectedSite->getRootPageId()) {
|
||||
$selectedAttribute = ' selected="selected"';
|
||||
}
|
||||
|
||||
$selector .= '<option value="' . htmlspecialchars($site->getRootPageId()) . '"' . $selectedAttribute . '>'
|
||||
. htmlspecialchars($site->getLabel())
|
||||
. '</option>';
|
||||
}
|
||||
|
||||
$selector .= '</select>';
|
||||
|
||||
return $selector;
|
||||
}
|
||||
}
|
267
Classes/ConnectionManager.php
Normal file
267
Classes/ConnectionManager.php
Normal file
@ -0,0 +1,267 @@
|
||||
<?php
|
||||
namespace WapplerSystems\Meilisearch;
|
||||
|
||||
/***************************************************************
|
||||
* Copyright notice
|
||||
*
|
||||
* (c) 2010-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\Domain\Site\Site;
|
||||
use WapplerSystems\Meilisearch\Domain\Site\SiteRepository;
|
||||
use WapplerSystems\Meilisearch\System\Records\Pages\PagesRepository as PagesRepositoryAtExtSolr;
|
||||
use WapplerSystems\Meilisearch\System\Records\SystemLanguage\SystemLanguageRepository;
|
||||
use WapplerSystems\Meilisearch\System\Solr\Node;
|
||||
use WapplerSystems\Meilisearch\System\Solr\SolrConnection;
|
||||
use InvalidArgumentException;
|
||||
use TYPO3\CMS\Core\Registry;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use function json_encode;
|
||||
|
||||
/**
|
||||
* ConnectionManager is responsible to create SolrConnection objects.
|
||||
*
|
||||
* @author Ingo Renner <ingo@typo3.org>
|
||||
*/
|
||||
class ConnectionManager implements SingletonInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $connections = [];
|
||||
|
||||
/**
|
||||
* @var SystemLanguageRepository
|
||||
*/
|
||||
protected $systemLanguageRepository;
|
||||
|
||||
/**
|
||||
* @var PagesRepositoryAtExtSolr
|
||||
*/
|
||||
protected $pagesRepositoryAtExtSolr;
|
||||
|
||||
/**
|
||||
* @var SiteRepository
|
||||
*/
|
||||
protected $siteRepository;
|
||||
|
||||
|
||||
/**
|
||||
* @param SystemLanguageRepository $systemLanguageRepository
|
||||
* @param PagesRepositoryAtExtSolr|null $pagesRepositoryAtExtSolr
|
||||
* @param SiteRepository $siteRepository
|
||||
*/
|
||||
public function __construct(
|
||||
SystemLanguageRepository $systemLanguageRepository = null,
|
||||
PagesRepositoryAtExtSolr $pagesRepositoryAtExtSolr = null,
|
||||
SiteRepository $siteRepository = null
|
||||
)
|
||||
{
|
||||
$this->systemLanguageRepository = $systemLanguageRepository ?? GeneralUtility::makeInstance(SystemLanguageRepository::class);
|
||||
$this->siteRepository = $siteRepository ?? GeneralUtility::makeInstance(SiteRepository::class);
|
||||
$this->pagesRepositoryAtExtSolr = $pagesRepositoryAtExtSolr ?? GeneralUtility::makeInstance(PagesRepositoryAtExtSolr::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a solr connection for read and write endpoints
|
||||
*
|
||||
* @param array $readNodeConfiguration
|
||||
* @param array $writeNodeConfiguration
|
||||
* @return SolrConnection|object
|
||||
*/
|
||||
public function getSolrConnectionForNodes(array $readNodeConfiguration, array $writeNodeConfiguration)
|
||||
{
|
||||
$connectionHash = md5(json_encode($readNodeConfiguration) . json_encode($writeNodeConfiguration));
|
||||
if (!isset(self::$connections[$connectionHash])) {
|
||||
$readNode = Node::fromArray($readNodeConfiguration);
|
||||
$writeNode = Node::fromArray($writeNodeConfiguration);
|
||||
self::$connections[$connectionHash] = GeneralUtility::makeInstance(SolrConnection::class, $readNode, $writeNode);
|
||||
}
|
||||
return self::$connections[$connectionHash];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a solr configuration from the configuration array and returns it.
|
||||
*
|
||||
* @param array $config The solr configuration array
|
||||
* @return SolrConnection
|
||||
*/
|
||||
public function getConnectionFromConfiguration(array $config)
|
||||
{
|
||||
if(empty($config['read']) && !empty($config['solrHost'])) {
|
||||
throw new InvalidArgumentException('Invalid registry data please re-initialize your solr connections');
|
||||
}
|
||||
|
||||
return $this->getSolrConnectionForNodes($config['read'], $config['write']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Solr connection for a page ID.
|
||||
*
|
||||
* @param int $pageId A page ID.
|
||||
* @param int $language The language ID to get the connection for as the path may differ. Optional, defaults to 0.
|
||||
* @param string $mount Comma list of MountPoint parameters
|
||||
* @return SolrConnection A solr connection.
|
||||
* @throws NoSolrConnectionFoundException
|
||||
*/
|
||||
public function getConnectionByPageId($pageId, $language = 0, $mount = '')
|
||||
{
|
||||
try {
|
||||
$site = $this->siteRepository->getSiteByPageId($pageId, $mount);
|
||||
$this->throwExceptionOnInvalidSite($site, 'No site for pageId ' . $pageId);
|
||||
$config = $site->getSolrConnectionConfiguration($language);
|
||||
$solrConnection = $this->getConnectionFromConfiguration($config);
|
||||