diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 611e042..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# These are supported funding model platforms
-
-github: ['Firesphere']
-custom: ['https://www.paypal.com/donate?hosted_button_id=C7VA5RQB6TWB6', 'https://ko-fi.com/B0B11GKLY']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index dd84ea7..0000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-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.
-
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index bbcbbe7..0000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-title: ''
-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.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index da0e903..0000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Hi there and thank you for taking the time and effort to make a Pull Request
-
-No, seriously, we are delighted you made time and effort to help us, even in the smallest way!
-
-
-# Please check the following
-
-- [ ] Have you opened an issue to discuss the feature and agree its general design?
-- [ ] Do you have a use case and, ideally, an example program using the feature?
-- [ ] Do you have tests covering 90%+ of the feature code (and, of course passing)
-- [ ] Have you written complete and accurate doc comments?
-- [ ] Have you updated the README or docs where needed?
-- [ ] Code is readable
-- [ ] Code is tested
-- [ ] I've read the Code of Conduct (Or, at least, I do believe I agree with it)
-- [ ] Frontend features don't look completely uncared for
-- [ ] Code is PSR-2 compliant
-- [ ] I'm fairly sure this is useful
-- [ ] I'm not just making this PR for HacktoberFest
-- [ ] You rock. Thanks a lot.
diff --git a/Solr/9/templates/types.ss b/Solr/9/templates/types.ss
index bdfbb5e..9ba904c 100644
--- a/Solr/9/templates/types.ss
+++ b/Solr/9/templates/types.ss
@@ -119,28 +119,8 @@
NOTE: autoGeneratePhraseQueries="true" tends to not work well for non whitespace delimited languages.
-->
-
-
-
-
-
-
-
-
-
-
+
@@ -158,16 +138,23 @@
-
+
+
+
-
+
@@ -175,7 +162,7 @@
-
+
@@ -185,7 +172,7 @@
-
+
@@ -199,7 +186,7 @@
-
+
@@ -266,7 +253,7 @@
-
+
@@ -278,7 +265,7 @@
-
+
1 Although not required, it's highly recommended
diff --git a/readme.md b/readme.md
index 006bb94..ebbdd23 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,5 @@
+**BASED ON [firesphere/solr-search](https://codeberg.org/Firesphere/silverstripe-solr)**
+
[![Maintainability](https://api.codeclimate.com/v1/badges/55c8967ef25e37182e3d/maintainability)](https://codeclimate.com/github/Firesphere/silverstripe-solr-search/maintainability)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Firesphere/silverstripe-solr-search/badges/quality-score.png?b=primary)](https://scrutinizer-ci.com/g/Firesphere/silverstripe-solr-search/?branch=primary)
[![Code Coverage](https://scrutinizer-ci.com/g/Firesphere/silverstripe-solr-search/badges/coverage.png?b=primary)](https://scrutinizer-ci.com/g/Firesphere/silverstripe-solr-search/?branch=primary)
diff --git a/src/Admins/SearchAdmin.php b/src/Admins/SearchAdmin.php
index ac95779..49a0e03 100644
--- a/src/Admins/SearchAdmin.php
+++ b/src/Admins/SearchAdmin.php
@@ -57,4 +57,12 @@ public function init()
Requirements::css('firesphere/solr-search:client/dist/main.css');
}
+
+ protected function getManagedModelTabs()
+ {
+ $tabs = parent::getManagedModelTabs();
+ return $tabs->filterByCallback(function ($tab) {
+ return singleton($tab->ClassName)->canView();
+ });
+ }
}
diff --git a/src/Extensions/DataObjectExtension.php b/src/Extensions/DataObjectExtension.php
index 8f2c541..dfb2c5e 100644
--- a/src/Extensions/DataObjectExtension.php
+++ b/src/Extensions/DataObjectExtension.php
@@ -14,7 +14,6 @@
use Firesphere\SolrSearch\Helpers\SolrLogger;
use Firesphere\SolrSearch\Models\DirtyClass;
use Firesphere\SolrSearch\Services\SolrCoreService;
-use Firesphere\SolrSearch\Tests\DataObjectExtensionTest;
use Psr\Log\LoggerInterface;
use Psr\SimpleCache\InvalidArgumentException;
use ReflectionException;
@@ -94,7 +93,7 @@ protected function shouldPush()
*/
protected function pushToSolr(DataObject $owner)
{
- $service = new SolrCoreService();
+ $service = Injector::inst()->get(SolrCoreService::class);
if (!$service->isValidClass($owner->ClassName)) {
return;
}
@@ -102,7 +101,7 @@ protected function pushToSolr(DataObject $owner)
/** @var DataObject $owner */
$record = $this->getDirtyClass(SolrCoreService::UPDATE_TYPE);
- $ids = json_decode($record->IDs, 1) ?: [];
+ $ids = json_decode($record->IDs ?? '[]', 1) ?: [];
$mode = Versioned::get_reading_mode();
try {
Versioned::set_reading_mode(Versioned::LIVE);
@@ -129,19 +128,21 @@ protected function pushToSolr(DataObject $owner)
* Find or create a new DirtyClass for recording dirty IDs
*
* @param string $type
+ * @param string $class optional class to use. If not set uses current owner class
* @return DirtyClass
* @throws ValidationException
*/
- protected function getDirtyClass(string $type)
+ protected function getDirtyClass(string $type, string $class = null)
{
+ $params = [
+ 'Class' => ($class ?? $this->owner->ClassName),
+ 'Type' => $type
+ ];
// Get the DirtyClass object for this item
/** @var null|DirtyClass $record */
- $record = DirtyClass::get()->filter(['Class' => $this->owner->ClassName, 'Type' => $type])->first();
+ $record = DirtyClass::get()->filter($params)->first();
if (!$record || !$record->exists()) {
- $record = DirtyClass::create([
- 'Class' => $this->owner->ClassName,
- 'Type' => $type,
- ]);
+ $record = DirtyClass::create($params);
$record->write();
}
@@ -213,6 +214,53 @@ public function doReindex()
$this->pushToSolr($this->owner);
}
+ /**
+ * Attempt to remove the item from Solr
+ *
+ * @throws ValidationException
+ * @throws HTTPException
+ */
+ private function removeItem(DataObject $item = null)
+ {
+ /** @var DataObject $owner */
+ $owner = $item ?? $this->owner;
+ /** @var DirtyClass $record */
+ $record = $this->getDirtyClass(SolrCoreService::DELETE_TYPE, $owner->ClassName);
+ $record->IDs = $record->IDs ?? '[]'; // If the record is new, or the IDs list is null, default
+ $ids = json_decode($record->IDs, 1);
+
+ try {
+ Injector::inst()->get(SolrCoreService::class)
+ ->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
+ // If successful, remove it from the array
+ // Added bonus, array_flip removes duplicates
+ $this->clearIDs($owner, $ids, $record);
+ // @codeCoverageIgnoreStart
+ } catch (Exception $error) {
+ $this->registerException($ids, $record, $error);
+ }
+ // @codeCoverageIgnoreEnd
+ }
+
+ /**
+ * Clear old page type from Solr before publishing if required
+ *
+ * @throws ValidationException
+ * @throws HTTPException
+ * @throws ReflectionException
+ * @throws InvalidArgumentException
+ */
+ public function onBeforePublish()
+ {
+ // Check for changed classname and delete old record before pushing new if required.
+ if ($this->owner instanceof SiteTree) {
+ $lastPublished = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)->byID($this->owner->ID);
+ if ($lastPublished && $this->owner->ClassName !== $lastPublished->ClassName) {
+ $this->removeItem($lastPublished);
+ }
+ }
+ }
+
/**
* Push the item to Solr after publishing
*
@@ -231,31 +279,14 @@ public function onAfterPublish()
}
/**
- * Attempt to remove the item from Solr
+ * Attempt to remove the item from Solr when deleted
*
* @throws ValidationException
* @throws HTTPException
*/
public function onAfterDelete(): void
{
- /** @var DataObject $owner */
- $owner = $this->owner;
- /** @var DirtyClass $record */
- $record = $this->getDirtyClass(SolrCoreService::DELETE_TYPE);
- $record->IDs = $record->IDs ?? '[]'; // If the record is new, or the IDs list is null, default
- $ids = json_decode($record->IDs, 1);
-
- try {
- (new SolrCoreService())
- ->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
- // If successful, remove it from the array
- // Added bonus, array_flip removes duplicates
- $this->clearIDs($owner, $ids, $record);
- // @codeCoverageIgnoreStart
- } catch (Exception $error) {
- $this->registerException($ids, $record, $error);
- }
- // @codeCoverageIgnoreEnd
+ $this->removeItem();
}
/**
diff --git a/src/Factories/DocumentFactory.php b/src/Factories/DocumentFactory.php
index 7f34320..e72f281 100644
--- a/src/Factories/DocumentFactory.php
+++ b/src/Factories/DocumentFactory.php
@@ -179,7 +179,7 @@ protected function addField($doc, $object, $options): void
$type = $typeMap[$options['type']] ?? $typeMap['*'];
foreach ($valuesForField as $value) {
- $this->extend('onBeforeAddDoc', $options, $value);
+ $this->extend('onBeforeAddDoc', $options, $value, $object);
$this->addToDoc($doc, $options, $type, $value);
}
}
diff --git a/src/Factories/SchemaFactory.php b/src/Factories/SchemaFactory.php
index e3ba1e5..d94fd70 100644
--- a/src/Factories/SchemaFactory.php
+++ b/src/Factories/SchemaFactory.php
@@ -188,7 +188,7 @@ public function getCopyFieldDefinitions()
$this->getFieldDefinition($copyField, $return, $field);
}
}
-
+ $this->extend('onBeforeCopyFieldDefinitions', $return);
return $return;
}
diff --git a/src/Indexes/BaseIndex.php b/src/Indexes/BaseIndex.php
index d3d9249..1224330 100644
--- a/src/Indexes/BaseIndex.php
+++ b/src/Indexes/BaseIndex.php
@@ -106,9 +106,10 @@ abstract class BaseIndex
public function __construct()
{
// Set up the client
- $config = Config::inst()->get(SolrCoreService::class, 'config');
+ $service = Injector::inst()->get(SolrCoreService::class);
+ $config = $service->getClient()->getOptions();
$config['endpoint'] = $this->getConfig($config['endpoint']);
- $this->client = (new SolrCoreService())->getClient();
+ $this->client = $service->getClient();
$this->client->setOptions($config);
// Set up the schema service, only used in the generation of the schema
@@ -287,7 +288,7 @@ protected function buildFactory(BaseQuery $query, Query $clientQuery)
* Conditions are:
* It is not already a retry with spellchecking
* Spellchecking is enabled
- * If spellchecking is enabled and nothing is found OR it should follow spellchecking none the less
+ * Spellcheck following is enabled and nothing is found
* There is a spellcheck output
*
* @param BaseQuery $query
@@ -299,7 +300,7 @@ protected function doRetry(BaseQuery $query, Result $result, SearchResult $searc
{
return !$this->retry &&
$query->hasSpellcheck() &&
- ($query->shouldFollowSpellcheck() || $result->getNumFound() === 0) &&
+ ($query->shouldFollowSpellcheck() && $result->getNumFound() === 0) &&
$searchResult->getCollatedSpellcheck();
}
diff --git a/src/Models/DirtyClass.php b/src/Models/DirtyClass.php
index de98a9b..b6e78c3 100644
--- a/src/Models/DirtyClass.php
+++ b/src/Models/DirtyClass.php
@@ -13,6 +13,8 @@
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
+use SilverStripe\Security\Permission;
+use SilverStripe\Security\PermissionProvider;
/**
* Class \Firesphere\SolrSearch\Models\DirtyClass
@@ -23,7 +25,7 @@
* @property string $Class
* @property string $IDs
*/
-class DirtyClass extends DataObject
+class DirtyClass extends DataObject implements PermissionProvider
{
/**
* @var string Table name
@@ -109,4 +111,37 @@ public function canCreate($member = null, $context = [])
{
return false;
}
+
+ /**
+ * Member has view access?
+ *
+ * @param null|Member $member
+ * @return bool|mixed
+ */
+ public function canView($member = null)
+ {
+ return Permission::checkMember($member, 'VIEW_DIRTY_CLASSES');
+ }
+
+ /**
+ * Return a map of permission codes to add to the dropdown shown in the Security section of the CMS.
+ * array(
+ * 'VIEW_SITE' => 'View the site',
+ * );
+ *
+ * @return array
+ */
+ public function providePermissions()
+ {
+ return [
+ 'VIEW_DIRTY_CLASSES' => [
+ 'name' => _t(self::class . '.PERMISSION_VIEW_CLASSES_DESCRIPTION', 'View Solr dirty classes'),
+ 'category' => _t('Permissions.LOGS_CATEGORIES', 'Solr logs permissions'),
+ 'help' => _t(
+ self::class . '.PERMISSION_VIEW_CLASSES_HELP',
+ 'Permission required to view existing Solr dirty classes.'
+ ),
+ ],
+ ];
+ }
}
diff --git a/src/Models/SearchSynonym.php b/src/Models/SearchSynonym.php
index a2f926f..b109c6c 100644
--- a/src/Models/SearchSynonym.php
+++ b/src/Models/SearchSynonym.php
@@ -9,8 +9,11 @@
namespace Firesphere\SolrSearch\Models;
+use Firesphere\SolrSearch\Admins\SearchAdmin;
use SilverStripe\Forms\FieldList;
use SilverStripe\ORM\DataObject;
+use SilverStripe\Security\Permission;
+use SilverStripe\Security\PermissionProvider;
/**
* Class \Firesphere\SolrSearch\Models\SearchSynonym
@@ -20,7 +23,7 @@
* @property string $Keyword
* @property string $Synonym
*/
-class SearchSynonym extends DataObject
+class SearchSynonym extends DataObject implements PermissionProvider
{
/**
* @var string Table name
@@ -81,4 +84,70 @@ public function getCombinedSynonym()
{
return sprintf("\n%s,%s", $this->Keyword, $this->Synonym);
}
+
+ /**
+ * Member has view access?
+ *
+ * @param null|Member $member
+ * @return bool|mixed
+ */
+ public function canView($member = null)
+ {
+ return SearchAdmin::singleton()->canView($member);
+ }
+
+ /**
+ * Only deleteable by members with permission
+ *
+ * @param null|Member $member
+ * @return bool|mixed
+ */
+ public function canDelete($member = null)
+ {
+ return Permission::checkMember($member, 'EDIT_SYNONYMS');
+ }
+
+ /**
+ * Only createable by members with permission
+ *
+ * @param null|Member $member
+ * @return boolean
+ */
+ public function canCreate($member = null, $context = [])
+ {
+ return Permission::checkMember($member, 'EDIT_SYNONYMS');
+ }
+
+ /**
+ * Only editable by members with permission
+ *
+ * @param null|Member $member
+ * @return boolean
+ */
+ public function canEdit($member = null)
+ {
+ return Permission::checkMember($member, 'EDIT_SYNONYMS');
+ }
+
+ /**
+ * Return a map of permission codes to add to the dropdown shown in the Security section of the CMS.
+ * array(
+ * 'VIEW_SITE' => 'View the site',
+ * );
+ *
+ * @return array
+ */
+ public function providePermissions()
+ {
+ return [
+ 'EDIT_SYNONYMS' => [
+ 'name' => _t(self::class . '.PERMISSION_EDIT_SYNONYMS_DESCRIPTION', 'Edit Solr synonyms'),
+ 'category' => _t('Permissions.LOGS_CATEGORIES', 'Solr logs permissions'),
+ 'help' => _t(
+ self::class . '.PERMISSION_EDIT_SYNONYMS_HELP',
+ 'Permission required to create, edit and delete existing Solr synonyms.'
+ ),
+ ],
+ ];
+ }
}
diff --git a/src/Models/SolrLog.php b/src/Models/SolrLog.php
index 6c413da..c32b537 100644
--- a/src/Models/SolrLog.php
+++ b/src/Models/SolrLog.php
@@ -13,6 +13,7 @@
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\Security\Member;
+use SilverStripe\Security\Permission;
use SilverStripe\Security\PermissionProvider;
/**
@@ -132,22 +133,18 @@ public function canEdit($member = null)
*/
public function canView($member = null)
{
- return parent::canView($member);
+ return Permission::checkMember($member, 'VIEW_LOG');
}
/**
- * Only deleteable by admins or when in dev mode to clean up
+ * Only deleteable by members with permission or when in dev mode to clean up
*
* @param null|Member $member
* @return bool|mixed
*/
public function canDelete($member = null)
{
- if ($member) {
- return $member->inGroup('administrators') || Director::isDev();
- }
-
- return parent::canDelete($member) || Director::isDev();
+ return Permission::checkMember($member, 'DELETE_LOG') || Director::isDev();
}
/**
@@ -162,7 +159,6 @@ public function getExtraClass()
return $classMap[$this->Level] ?? 'alert alert-info';
}
-
/**
* Return a map of permission codes to add to the dropdown shown in the Security section of the CMS.
* array(
diff --git a/src/Services/SolrCoreService.php b/src/Services/SolrCoreService.php
index 5a58335..40ca194 100644
--- a/src/Services/SolrCoreService.php
+++ b/src/Services/SolrCoreService.php
@@ -98,6 +98,16 @@ class SolrCoreService
"4.99999999.0",
];
+ /**
+ * @var array|null Option for use in Solarium\QueryType\Update\Query\Query::addDocuments() function
+ */
+ private static $add_docs_overwrite;
+
+ /**
+ * @var array|null Option for use in Solarium\QueryType\Update\Query\Query::addDocuments() function
+ */
+ private static $add_docs_commitWithin;
+
/**
* SolrCoreService constructor.
*
@@ -282,7 +292,11 @@ public function updateIndex($index, $items, $update): void
$factory = $this->getFactory($items);
$docs = $factory->buildItems($fields, $index, $update);
if (count($docs)) {
- $update->addDocuments($docs);
+ $update->addDocuments(
+ $docs,
+ $this->config()->get('add_docs_overwrite'),
+ $this->config()->get('add_docs_commitWithin')
+ );
}
}
diff --git a/src/Tasks/SolrIndexTask.php b/src/Tasks/SolrIndexTask.php
index a232ee8..45c7f0e 100644
--- a/src/Tasks/SolrIndexTask.php
+++ b/src/Tasks/SolrIndexTask.php
@@ -398,8 +398,11 @@ private function indexStateClass(string $group, string $class): void
// Generate filtered list of local records
$baseClass = DataObject::getSchema()->baseDataClass($class);
/** @var DataList|DataObject[] $items */
- $items = DataObject::get($baseClass)
- ->sort('ID ASC')
+ $items = DataObject::get($baseClass);
+ if (!empty($classes = $this->getIndex()->config()->get('exclude_classes'))) {
+ $items = $items->exclude(['ClassName' => $classes]);
+ }
+ $items = $items->sort('ID ASC')
->limit($this->getBatchLength(), ($group * $this->getBatchLength()));
if ($items->count()) {
$this->updateIndex($items);
diff --git a/src/Traits/CoreTraits/CoreServiceTrait.php b/src/Traits/CoreTraits/CoreServiceTrait.php
index 2854659..07a7012 100644
--- a/src/Traits/CoreTraits/CoreServiceTrait.php
+++ b/src/Traits/CoreTraits/CoreServiceTrait.php
@@ -104,6 +104,9 @@ public function getValidClasses(): array
$classes = [];
foreach ($indexes as $index) {
$classes = $this->getClassesInHierarchy($index, $classes);
+ if (!empty($exclude = singleton($index)->config()->get('exclude_classes'))) {
+ $classes = array_diff($classes, $exclude);
+ }
}
$cache->set('ValidClasses', array_unique($classes));