mediatheque
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\TNTSearch;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Yaml;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Plugin\TNTSearchPlugin;
|
||||
use PDO;
|
||||
|
||||
class GravConnector extends PDO
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $attribute
|
||||
* @return bool
|
||||
*/
|
||||
public function getAttribute($attribute): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/***
|
||||
* @param string $statement
|
||||
* @param int|null $fetch_style
|
||||
* @param mixed ...$extra
|
||||
* @return GravResultObject
|
||||
*/
|
||||
public function query(string $statement, ?int $fetch_style = null, ...$extra): GravResultObject
|
||||
{
|
||||
$counter = 0;
|
||||
$results = [];
|
||||
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
$filter = $config->get('plugins.tntsearch.filter');
|
||||
$default_process = $config->get('plugins.tntsearch.index_page_by_default');
|
||||
$gtnt = TNTSearchPlugin::getSearchObjectType();
|
||||
|
||||
|
||||
if ($filter && array_key_exists('items', $filter)) {
|
||||
|
||||
if (is_string($filter['items'])) {
|
||||
$filter['items'] = Yaml::parse($filter['items']);
|
||||
}
|
||||
|
||||
$page = new Page;
|
||||
$collection = $page->collection($filter, false);
|
||||
} else {
|
||||
$collection = Grav::instance()['pages']->all();
|
||||
$collection->published()->routable();
|
||||
}
|
||||
|
||||
foreach ($collection as $page) {
|
||||
$counter++;
|
||||
$process = $default_process;
|
||||
$header = $page->header();
|
||||
$url = $page->url();
|
||||
|
||||
if (isset($header->tntsearch['process'])) {
|
||||
$process = $header->tntsearch['process'];
|
||||
}
|
||||
|
||||
// Only process what's configured
|
||||
if (!$process) {
|
||||
echo("Skipped {$counter} {$url}\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$fields = $gtnt->indexPageData($page);
|
||||
$results[] = (array) $fields;
|
||||
echo("Added {$counter} {$url}\n");
|
||||
} catch (\Exception $e) {
|
||||
echo("Skipped {$counter} {$url} - {$e->getMessage()}\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return new GravResultObject($results);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\TNTSearch;
|
||||
|
||||
class GravResultObject
|
||||
{
|
||||
/** @var array */
|
||||
protected $items;
|
||||
/** @var int */
|
||||
protected $counter;
|
||||
|
||||
/**
|
||||
* GravResultObject constructor.
|
||||
* @param array $items
|
||||
*/
|
||||
public function __construct($items)
|
||||
{
|
||||
$this->counter = 0;
|
||||
$this->items = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
*/
|
||||
public function fetch($options)
|
||||
{
|
||||
return $this->items[$this->counter++];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
namespace Grav\Plugin\TNTSearch;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Twig\Twig;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Yaml;
|
||||
use Grav\Common\Page\Collection;
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use TeamTNT\TNTSearch\Exceptions\IndexNotFoundException;
|
||||
use TeamTNT\TNTSearch\TNTSearch;
|
||||
|
||||
class GravTNTSearch
|
||||
{
|
||||
/** @var TNTSearch */
|
||||
public $tnt;
|
||||
/** @var array */
|
||||
protected $options;
|
||||
/** @var string[] */
|
||||
protected $bool_characters = ['-', '(', ')', 'or'];
|
||||
/** @var string */
|
||||
protected $index = 'grav.index';
|
||||
/** @var false|string */
|
||||
protected $language;
|
||||
|
||||
/**
|
||||
* GravTNTSearch constructor.
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($options = [])
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
$search_type = $config->get('plugins.tntsearch.search_type', 'auto');
|
||||
$fuzzy = $config->get('plugins.tntsearch.fuzzy', false);
|
||||
$distance = $config->get('plugins.tntsearch.distance', 2);
|
||||
$stemmer = $config->get('plugins.tntsearch.stemmer', 'no');
|
||||
$limit = $config->get('plugins.tntsearch.limit', 20);
|
||||
$snippet = $config->get('plugins.tntsearch.snippet', 300);
|
||||
$data_path = $locator->findResource('user://data', true) . '/tntsearch';
|
||||
|
||||
/** @var Language $language */
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
if ($language->enabled()) {
|
||||
$active = $language->getActive();
|
||||
$default = $language->getDefault();
|
||||
$this->language = $active ?: $default;
|
||||
$this->index = $this->language . '.index';
|
||||
}
|
||||
|
||||
if (!file_exists($data_path)) {
|
||||
mkdir($data_path);
|
||||
}
|
||||
|
||||
$defaults = [
|
||||
'json' => false,
|
||||
'search_type' => $search_type,
|
||||
'fuzzy' => $fuzzy,
|
||||
'distance' => $distance,
|
||||
'stemmer' => $stemmer,
|
||||
'limit' => $limit,
|
||||
'as_you_type' => true,
|
||||
'snippet' => $snippet,
|
||||
'phrases' => true,
|
||||
];
|
||||
|
||||
$this->options = array_replace($defaults, $options);
|
||||
$this->tnt = new TNTSearch();
|
||||
$this->tnt->loadConfig(
|
||||
[
|
||||
'storage' => $data_path,
|
||||
'driver' => 'sqlite',
|
||||
'charset' => 'utf8'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @return object|string
|
||||
* @throws IndexNotFoundException
|
||||
*/
|
||||
public function search($query)
|
||||
{
|
||||
/** @var Uri $uri */
|
||||
$uri = Grav::instance()['uri'];
|
||||
$type = $uri->query('search_type');
|
||||
$this->tnt->selectIndex($this->index);
|
||||
$this->tnt->asYouType = $this->options['as_you_type'];
|
||||
|
||||
if (isset($this->options['fuzzy']) && $this->options['fuzzy']) {
|
||||
$this->tnt->fuzziness = true;
|
||||
$this->tnt->fuzzy_distance = $this->options['distance'];
|
||||
}
|
||||
|
||||
$limit = (int)$this->options['limit'];
|
||||
$type = $type ?? $this->options['search_type'];
|
||||
|
||||
// TODO: Multiword parameter has been removed from $tnt->search(), please check if this works
|
||||
$multiword = null;
|
||||
if (isset($this->options['phrases']) && $this->options['phrases']) {
|
||||
if (strlen($query) > 2) {
|
||||
if ($query[0] === '"' && $query[strlen($query) - 1] === '"') {
|
||||
$multiword = substr($query, 1, -1);
|
||||
$type = 'basic';
|
||||
$query = $multiword;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch ($type) {
|
||||
case 'basic':
|
||||
$results = $this->tnt->search($query, $limit);
|
||||
break;
|
||||
case 'boolean':
|
||||
$results = $this->tnt->searchBoolean($query, $limit);
|
||||
break;
|
||||
case 'default':
|
||||
case 'auto':
|
||||
default:
|
||||
$guess = 'search';
|
||||
foreach ($this->bool_characters as $char) {
|
||||
if (strpos($query, $char) !== false) {
|
||||
$guess = 'searchBoolean';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$results = $this->tnt->{$guess}($query, $limit);
|
||||
}
|
||||
|
||||
return $this->processResults($results, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $res
|
||||
* @param string $query
|
||||
* @return object|string
|
||||
*/
|
||||
protected function processResults($res, $query)
|
||||
{
|
||||
$data = new \stdClass();
|
||||
$data->number_of_hits = $res['hits'] ?? 0;
|
||||
$data->execution_time = $res['execution_time'];
|
||||
|
||||
/** @var Pages $pages */
|
||||
$pages = Grav::instance()['pages'];
|
||||
|
||||
$counter = 0;
|
||||
foreach ($res['ids'] as $path) {
|
||||
if ($counter++ > $this->options['limit']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$page = $pages->find($path);
|
||||
|
||||
if ($page) {
|
||||
$event = new Event(
|
||||
[
|
||||
'page' => $page,
|
||||
'query' => $query,
|
||||
'options' => $this->options,
|
||||
'fields' => $data,
|
||||
'gtnt' => $this
|
||||
]
|
||||
);
|
||||
Grav::instance()->fireEvent('onTNTSearchQuery', $event);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->options['json']) {
|
||||
return json_encode($data, JSON_PRETTY_PRINT) ?: '';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PageInterface $page
|
||||
* @return string
|
||||
*/
|
||||
public static function getCleanContent($page)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$activePage = $grav['page'];
|
||||
|
||||
// Set active page in grav to the one we are currently processing.
|
||||
unset($grav['page']);
|
||||
$grav['page'] = $page;
|
||||
|
||||
/** @var Twig $twig */
|
||||
$twig = $grav['twig'];
|
||||
$header = $page->header();
|
||||
|
||||
// @phpstan-ignore-next-line
|
||||
if (isset($header->tntsearch['template'])) {
|
||||
$processed_page = $twig->processTemplate($header->tntsearch['template'] . '.html.twig', ['page' => $page]);
|
||||
$content = $processed_page;
|
||||
} else {
|
||||
$content = $page->content();
|
||||
}
|
||||
|
||||
$content = strip_tags($content);
|
||||
$content = preg_replace(['/[ \t]+/', '/\s*$^\s*/m'], [' ', "\n"], $content) ?? $content;
|
||||
|
||||
// Restore active page in Grav.
|
||||
unset($grav['page']);
|
||||
$grav['page'] = $activePage;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function createIndex()
|
||||
{
|
||||
$this->tnt->setDatabaseHandle(new GravConnector);
|
||||
$indexer = $this->tnt->createIndex($this->index);
|
||||
|
||||
// Disable stemmer for users with older configuration.
|
||||
if ($this->options['stemmer'] == 'default') {
|
||||
$indexer->setLanguage('no');
|
||||
} else {
|
||||
$indexer->setLanguage($this->options['stemmer']);
|
||||
}
|
||||
|
||||
$indexer->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws IndexNotFoundException
|
||||
*/
|
||||
public function selectIndex()
|
||||
{
|
||||
$this->tnt->selectIndex($this->index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @return void
|
||||
*/
|
||||
public function deleteIndex($object)
|
||||
{
|
||||
if (!$object instanceof Page) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tnt->setDatabaseHandle(new GravConnector);
|
||||
try {
|
||||
$this->tnt->selectIndex($this->index);
|
||||
} catch (IndexNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$indexer = $this->tnt->getIndex();
|
||||
|
||||
// Delete existing if it exists
|
||||
$indexer->delete($object->route());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
* @return void
|
||||
*/
|
||||
public function updateIndex($object)
|
||||
{
|
||||
if (!$object instanceof Page) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tnt->setDatabaseHandle(new GravConnector);
|
||||
|
||||
try {
|
||||
$this->tnt->selectIndex($this->index);
|
||||
} catch (IndexNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$indexer = $this->tnt->getIndex();
|
||||
|
||||
// Delete existing if it exists
|
||||
$indexer->delete($object->route());
|
||||
|
||||
$filter = Grav::instance()['config']->get('plugins.tntsearch.filter');
|
||||
if ($filter && array_key_exists('items', $filter)) {
|
||||
|
||||
if (is_string($filter['items'])) {
|
||||
$filter['items'] = Yaml::parse($filter['items']);
|
||||
}
|
||||
|
||||
$apage = new Page;
|
||||
/** @var Collection $collection */
|
||||
$collection = $apage->collection($filter, false);
|
||||
|
||||
$path = $object->path();
|
||||
if ($path && array_key_exists($path, $collection->toArray())) {
|
||||
$fields = $this->indexPageData($object);
|
||||
$document = (array) $fields;
|
||||
|
||||
// Insert document
|
||||
$indexer->insert($document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PageInterface $page
|
||||
* @return object
|
||||
*/
|
||||
public function indexPageData($page)
|
||||
{
|
||||
$header = (array) $page->header();
|
||||
$redirect = (bool) $page->redirect();
|
||||
|
||||
if (!$page->published()) {
|
||||
throw new \RuntimeException('not published...');
|
||||
}
|
||||
if (!$page->routable()) {
|
||||
throw new \RuntimeException('not routable...');
|
||||
}
|
||||
if ($redirect || (isset($header['tntsearch']['index']) && $header['tntsearch']['index'] === false )) {
|
||||
throw new \RuntimeException('redirect only...');
|
||||
}
|
||||
|
||||
$route = $page->route();
|
||||
|
||||
$fields = new \stdClass();
|
||||
$fields->id = $route;
|
||||
$fields->name = $page->title();
|
||||
$fields->content = static::getCleanContent($page);
|
||||
|
||||
Grav::instance()->fireEvent('onTNTSearchIndex', new Event(['page' => $page, 'fields' => $fields]));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user