foldershare-8.x-1.2/src/ManageSearch.php
src/ManageSearch.php
<?php
namespace Drupal\foldershare;
use Drupal\foldershare\Entity\FolderShare;
use Drupal\foldershare\Entity\FolderShareScheduledTask;
/**
* Manages search features for FolderShare entities.
*
* This class provides static methods to manage search indexing for
* FolderShare entities. Supported operations include:
* - Clearing the index.
* - Deleting index entries for specific entities.
* - Marking index entries as out of date for specific entities.
* - Updating the index.
*
* This method also provides methods to query configuration settings for
* indexing and check the amount of indexing work pending.
*
* The following search modules are supported:
* - "search" in Drupal core. See also FolderShare's search plugin.
*
* <B>Access control</B>
* This class's methods do not do access control. The caller should check
* access as needed by their situation.
*
* @ingroup foldershare
*
* @see \Drupal\foldershare\Settings
* @see \Drupal\foldershare\Entity\FolderShare
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
final class ManageSearch {
/*--------------------------------------------------------------------
*
* Search plugin operations.
*
*-------------------------------------------------------------------*/
/**
* Returns the Drupal core "search" plugin instance for FolderShare.
*
* The FolderShare module provides a custom search plugin that supports
* search indexing of FolderShare entities and implements a
* FolderShare-specific search form.
*
* If the Drupal core "search" module is not enabled or FolderShare's
* search plugin is not enabled, this method returns a NULL.
*
* @return \Drupal\search\Plugin\SearchInterface
* Returns the module's search plugin object, or NULL if the plugin
* is not found or if it is currently disabled.
*
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
private static function findSearchPlugin() {
if (\Drupal::moduleHandler()->moduleExists('search') === FALSE) {
return NULL;
}
$searchPageRepository = \Drupal::service('search.search_page_repository');
// Loop over all *indexable* search pages. A page is not indexable if
// the page is disabled or the plugin does not support indexing.
foreach ($searchPageRepository->getIndexableSearchPages() as $page) {
$plugin = $page->getPlugin();
if ($plugin->getType() === Constants::SEARCH_INDEX) {
return $plugin;
}
}
return NULL;
}
/**
* Returns the list of file name extensions for indexed file content.
*
* The FolderShare module's search plugin supports optional indexing of
* text file content. This method returns the configured set of file name
* extensions for which indexing is enabled.
*
* If the Drupal core "search" module is not enabled, FolderShare's
* search plugin is not enabled, or if there are no file extensions
* supported for indexing, this method returns an empty string.
*
* @return string
* Returns a space-separated list of file name extensions supported for
* file content indexing.
*
* @see ::isIndexFileContentEnabled()
* @see ::getIndexFileContentMaximumSize()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function getIndexFileContentExtensions() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL &&
method_exists($plugin, 'getSearchFileExtensions') === TRUE) {
return $plugin->getSearchFileExtensions();
}
return '';
}
/**
* Returns the maximum number of bytes indexed from file content.
*
* The FolderShare module's search plugin supports optional indexing of
* text file content. This method returns the configured maximum number of
* bytes to read and index from the start of the file.
*
* If the Drupal core "search" module is not enabled, FolderShare's
* search plugin is not enabled, or if the maximum indexing size has
* been set to zero, this method returns a zero.
*
* @return int
* Returns the maximum number of bytes indexed from file content.
*
* @see ::isIndexFileContentEnabled()
* @see ::getIndexFileContentExtensions()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function getIndexFileContentMaximumSize() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL &&
method_exists($plugin, 'getSearchFileSize') === TRUE) {
return $plugin->getSearchFileSize();
}
return 0;
}
/**
* Returns TRUE if the module's search plugin indexes file content.
*
* The FolderShare module's search plugin supports optional indexing of
* text file content. This method returns TRUE if this feature is enabled.
*
* If the Drupal core "search" module is not enabled, FolderShare's
* search plugin is not enabled, or if file content indexing has been
* disabled, this method returns FALSE.
*
* @return bool
* Returns TRUE if file content indexing is enabled.
*
* @see ::getIndexFileContentMaximumSize()
* @see ::getIndexFileContentExtensions()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function isIndexFileContentEnabled() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL &&
method_exists($plugin, 'getSearchFileContent') === TRUE) {
return $plugin->getSearchFileContent();
}
return FALSE;
}
/**
* Returns TRUE if the module's search plugin is enabled.
*
* If the Drupal core "search" module is not enabled, FolderShare's
* search plugin is not enabled, this method returns FALSE.
*
* @return bool
* Returns TRUE if the search plugin is enabled.
*
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function isSearchEnabled() {
return (self::findSearchPlugin() !== NULL);
}
/*--------------------------------------------------------------------
*
* Search index operations.
*
*-------------------------------------------------------------------*/
/**
* Clears the search index of all FolderShare entries.
*
* @see ::deleteFromIndex()
* @see ::getIndexStatus()
* @see ::getNumberToIndex()
* @see ::markForReindex()
* @see search_index_clear()
* @see \Drupal\search\Plugin\SearchIndexingInterface::indexClear()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function clearIndex() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL) {
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// indexClear().
if (method_exists($plugin, 'indexClear') === TRUE) {
$plugin->indexClear();
}
else {
search_index_clear(Constants::SEARCH_INDEX);
}
}
elseif (\Drupal::moduleHandler()->moduleExists('search') === TRUE) {
search_index_clear(Constants::SEARCH_INDEX);
}
}
/**
* Deletes the search index entries for a list of FolderShare items.
*
* @param \Drupal\foldershare\Entity\FolderShare[]|\Drupal\foldershare\Entity\FolderShare $items
* (optional, default = NULL) With a NULL, the entire search index is
* cleared by forwarding to clearIndex(). Otherwise each non-NULL
* FolderShare entity given is deleted from the search index.
*
* @see ::clearIndex()
* @see ::getNumberToIndex()
* @see ::markForReindex()
* @see search_index_clear()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function deleteFromIndex($items = NULL) {
if ($items === NULL) {
self::clearIndex();
return;
}
if (\Drupal::moduleHandler()->moduleExists('search') === FALSE) {
return;
}
if (is_object($items) === TRUE) {
$items = [$items];
}
if (is_array($items) === TRUE) {
foreach ($items as $item) {
if ($item !== NULL && $item instanceof FolderShare) {
search_index_clear(
Constants::SEARCH_INDEX,
(int) $item->id(),
$item->langcode->value);
}
}
}
}
/**
* Returns the number of FolderShare items remaining to index.
*
* The number of items remaining to index includes all items that have
* been added, but not yet indexed a first time, and all items that have
* been changed and are now in need of reindexing.
*
* A zero is returned if the Drupal core "search" module is not installed,
* if this module's search plugin is not enabled, or if there is nothing
* pending to index.
*
* @return int
* Returns the number of FolderShare items in need of indexing.
*
* @see ::getIndexStatus()
* @see ::markForReindex()
* @see \Drupal\search\Plugin\SearchIndexingInterface::indexStatus()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function getNumberToIndex() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL && method_exists($plugin, 'indexStatus') === TRUE) {
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// indexStatus().
$answer = $plugin->indexStatus();
return $answer['remaining'];
}
return 0;
}
/**
* Returns the indexing status.
*
* The returned status is an associative array with two values:
* - 'total' has the total number of items.
* - 'remaining' has the number of items to index.
*
* Zeros are returned if the Drupal core "search" module is not installed,
* or if this module's search plugin is not enabled.
*
* @return array
* Returns an associative array with 'total' and 'remaining' values.
*
* @see ::getNumberToIndex()
* @see ::markForReindex()
* @see \Drupal\search\Plugin\SearchIndexingInterface::indexStatus()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function getIndexStatus() {
$plugin = self::findSearchPlugin();
if ($plugin !== NULL && method_exists($plugin, 'indexStatus') === TRUE) {
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// indexStatus().
return $plugin->indexStatus();
}
return [
'total' => 0,
'remaining' => 0,
];
}
/**
* Marks a list of FolderShare items as in need of search reindexing.
*
* When an item is marked, future search indexing runs will rebuild
* the index's entry for the item.
*
* This method is typically called whenever an item is changed and
* saved back to the database.
*
* No action is taken if the Drupal core "search" module is not installed,
* or if this module's search plugin is not enabled.
*
* @param \Drupal\foldershare\Entity\FolderShare[]|\Drupal\foldershare\Entity\FolderShare $items
* (optional, default = NULL) With a NULL, the entire search index is
* marked for reindexing. Otherwise each non-NULL FolderShare entity is
* marked for reindexing.
*
* @see ::clearIndex()
* @see ::getIndexStatus()
* @see ::getNumberToIndex()
* @see ::deleteFromIndex()
* @see search_mark_for_reindex()
* @see \Drupal\search\Plugin\SearchIndexingInterface::markForReindex()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function markForReindex($items = NULL) {
if ($items === NULL) {
$plugin = self::findSearchPlugin();
if ($plugin === NULL) {
return;
}
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// markForReindex().
if (method_exists($plugin, 'markForReindex') === TRUE) {
$plugin->markForReindex();
}
else {
search_mark_for_reindex(Constants::SEARCH_INDEX);
}
return;
}
if (\Drupal::moduleHandler()->moduleExists('search') === FALSE) {
return;
}
if (is_object($items) === TRUE) {
$items = [$items];
}
if (is_array($items) === TRUE) {
foreach ($items as $item) {
if ($item !== NULL && $item instanceof FolderShare) {
search_mark_for_reindex(Constants::SEARCH_INDEX, (int) $item->id());
}
}
}
}
/**
* Updates the search index for pending FolderShare items.
*
* If a list of items is provided, the index entries for those items
* are updated. Otherwise any pending items are updated.
*
* @param \Drupal\foldershare\Entity\FolderShare[]|\Drupal\foldershare\Entity\FolderShare $items
* (optional, default = NULL) With a NULL, pending items are indexed
* by the search plugin. Otherwise each non-NULL FolderShare entity
* is indexed by the search plugin.
*
* @see ::getIndexStatus()
* @see ::getNumberToIndex()
* @see search_cron()
* @see search_update_totals()
* @see \Drupal\search\Plugin\SearchIndexingInterface::updateIndex()
* @see \Drupal\foldershare\Entity\FolderShare\Plugin\Search\FolderShareSearch
*/
public static function updateIndex($items = NULL) {
$plugin = self::findSearchPlugin();
if ($plugin === NULL) {
return;
}
// Register the search module's shutdown function to insure that
// search totals are always updated.
drupal_register_shutdown_function('search_update_totals');
if ($items === NULL) {
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// updateIndex().
if (method_exists($plugin, 'updateIndex') === TRUE) {
$plugin->updateIndex();
}
}
elseif (method_exists($plugin, 'indexItem') === TRUE) {
// FolderShare's search plugin supports indexItem().
foreach ($items as $item) {
if ($item !== NULL && $item instanceof FolderShare) {
$plugin->indexItem($item);
}
}
}
else {
// Mark each of the items as in need of re-indexing, then
// update the whole index.
self::markForReindex($items);
// Search plugins are only guaranteed to implement SearchInterface,
// though most also implement SearchIndexingInterface. This adds
// updateIndex().
if (method_exists($plugin, 'updateIndex') === TRUE) {
$plugin->updateIndex();
}
}
}
/*--------------------------------------------------------------------
*
* Index scheduling.
*
*-------------------------------------------------------------------*/
/**
* Returns the search indexing task run interval.
*
* The run interval indicates the time between execution of the search
* indexing scheduled task. Known values are:
*
* - 'system' = the index is serviced by the system (e.g. CRON).
* - '1min' = the index is serviced every minute.
* - '5min' = the index is serviced every 5 minutes.
* - '10min' = the index is serviced every 10 minutes.
* - '15min' = the index is serviced every 15 minutes.
* - '30min' = the index is serviced every 30 minutes.
* - 'hourly' = the index is serviced every hour.
* - 'daily' = the index is serviced every day.
*
* @return string
* Returns the run interval setting.
*
* @see \Drupal\foldershare\Settings::getSearchIndexInterval()
*/
public static function getIndexInterval() {
return Settings::getSearchIndexInterval();
}
/**
* Returns the search indexing task run interval in seconds.
*
* The current run interval is interpreted to compute and return the
* interval time in seconds. A zero value indicates the interval is
* 'system', which disables task scheduling and leaves indexing to CRON.
*
* @return int
* Returns the run interval in seconds. Zero is returned if the
* interval is 'system'.
*
* @see ::getIndexInterval()
* @see \Drupal\foldershare\Settings::getSearchIndexInterval()
*/
private static function getIndexIntervalSeconds() {
switch (self::getIndexInterval()) {
default:
case 'system':
// No indexing task.
return 0;
case '1min':
// 60 seconds/minute.
return 60;
case '5min':
// 5 min * 60 seconds/minute.
return (5 * 60);
case '10min':
// 10 min * 60 seconds/minute.
return (10 * 60);
case '15min':
// 15 min * 60 seconds/minute.
return (15 * 60);
case '30min':
// 30 min * 60 seconds/minute.
return (30 * 60);
case 'hourly':
// 60 minutes/hour * 60 seconds/minute.
return (60 * 60);
case 'daily':
// 24 hours/day * 60 minutes/hour * 60 seconds/minute.
return (24 * 60 * 60);
}
}
/**
* Schedules search indexing using the current indexing interval.
*
* <B>This method is internal and strictly for use by the FolderShare
* module itself.</B>
*
* The current search indexing task is deleted, if there is one. If the
* current scheduling interval is not 'system' (i.e. CRON-based execution),
* a new search indexing task is scheduled.
*
* No task is scheduled if search indexing is disabled.
*
* @see ::isSearchEnabled()
* @see ::getIndexInterval()
* @see ::taskUpdateIndex()
* @see \Drupal\foldershare\Settings::getSearchIndexInterval()
*/
public static function scheduleSearchIndexing() {
// Delete any pending search tasks.
FolderShareScheduledTask::deleteTasks(
'\Drupal\foldershare\ManageSearch::taskUpdateIndex');
if (self::isSearchEnabled() === FALSE) {
// No search.
return;
}
$seconds = self::getIndexIntervalSeconds();
if ($seconds === 0) {
// There is no scheduled search index task.
return;
}
// Schedule a new task with the current interval.
$t = time();
FolderShareScheduledTask::createTask(
$t + $seconds,
'\Drupal\foldershare\ManageSearch::taskUpdateIndex',
(int) \Drupal::currentUser()->id(),
NULL,
$t,
self::getIndexInterval() . ' search index update',
0);
}
/*---------------------------------------------------------------------
*
* Search index update task.
*
*---------------------------------------------------------------------*/
/**
* Updates the search index as a scheduled task.
*
* <B>This method is internal and strictly for use by the FolderShare
* module itself.</B>
*
* The task updates the search index, processing entries based upon search
* settings. If the current indexing interval is 'system', or if search
* is disabled, this task aborts without re-scheduling itself. Otherwise
* it updates the search index and reschedules itself at the configured
* time interval in the future.
*
* @param int $requester
* The user ID of the user that requested the update. This is ignored.
* @param array $parameters
* The queued task's parameters. This is ignored.
* @param int $started
* The timestamp of the start date & time for an operation that causes
* a chain of tasks.
* @param string $comments
* A comment on the current task.
* @param int $executionTime
* The accumulated total execution time of the task chain, in seconds.
*
* @see ::getIndexInterval()
* @see ::isSearchEnabled()
* @see ::scheduleSearchIndexing()
* @see ::updateIndex()
*/
public static function taskUpdateIndex(
int $requester,
array $parameters,
int $started,
string $comments,
int $executionTime) {
if (self::isSearchEnabled() === FALSE) {
// No search.
return;
}
$seconds = self::getIndexIntervalSeconds();
if ($seconds === 0) {
// There is no scheduled search index task.
return;
}
// Schedule the next task with the current interval.
$t = time();
FolderShareScheduledTask::createTask(
$t + $seconds,
'\Drupal\foldershare\ManageSearch::taskUpdateIndex',
$requester,
NULL,
$t,
self::getIndexInterval() . ' search index update',
0);
// Update the search index.
self::updateIndex();
}
}
