From ba712361ca30bd7f5ab9b49f433b2998a9cd9e3e Mon Sep 17 00:00:00 2001 From: Fanny Mugnier Date: Fri, 12 Feb 2021 16:03:09 +0100 Subject: [PATCH] import plugins pagination + simplesearch --- .gitignore | 2 - user/plugins/error | 1 + user/plugins/markdown-notices | 1 + user/plugins/pagination/CHANGELOG.md | 89 ++++ user/plugins/pagination/LICENSE | 21 + user/plugins/pagination/README.md | 151 ++++++ user/plugins/pagination/blueprints.yaml | 48 ++ .../pagination/classes/paginationhelper.php | 132 +++++ .../pagination/classes/paginationpage.php | 84 ++++ user/plugins/pagination/css/pagination.css | 17 + user/plugins/pagination/hebe.json | 15 + user/plugins/pagination/languages.yaml | 20 + user/plugins/pagination/pagination.php | 141 ++++++ user/plugins/pagination/pagination.yaml | 3 + .../templates/partials/pagination.html.twig | 34 ++ .../twig/PaginationTwigExtension.php | 42 ++ user/plugins/problems | 1 + user/plugins/simplesearch/.gitignore | 2 + user/plugins/simplesearch/CHANGELOG.md | 279 +++++++++++ user/plugins/simplesearch/LICENSE | 21 + user/plugins/simplesearch/README.md | 209 ++++++++ user/plugins/simplesearch/assets/readme_1.png | Bin 0 -> 113109 bytes user/plugins/simplesearch/assets/search.svg | 7 + user/plugins/simplesearch/blueprints.yaml | 172 +++++++ user/plugins/simplesearch/composer.json | 35 ++ user/plugins/simplesearch/composer.lock | 22 + .../plugins/simplesearch/css/simplesearch.css | 42 ++ user/plugins/simplesearch/hebe.json | 15 + user/plugins/simplesearch/js/simplesearch.js | 32 ++ user/plugins/simplesearch/languages.yaml | 226 +++++++++ .../simplesearch/pages/simplesearch.md | 7 + user/plugins/simplesearch/simplesearch.php | 450 ++++++++++++++++++ user/plugins/simplesearch/simplesearch.yaml | 21 + .../partials/simplesearch_base.html.twig | 1 + .../partials/simplesearch_item.html.twig | 23 + .../partials/simplesearch_searchbox.html.twig | 23 + .../templates/simplesearch_results.html.twig | 23 + .../templates/simplesearch_results.json.twig | 5 + user/plugins/simplesearch/vendor/autoload.php | 7 + .../vendor/composer/ClassLoader.php | 445 +++++++++++++++++ .../simplesearch/vendor/composer/LICENSE | 21 + .../vendor/composer/autoload_classmap.php | 10 + .../vendor/composer/autoload_namespaces.php | 9 + .../vendor/composer/autoload_psr4.php | 9 + .../vendor/composer/autoload_real.php | 52 ++ .../vendor/composer/autoload_static.php | 20 + .../vendor/composer/installed.json | 1 + 47 files changed, 2989 insertions(+), 2 deletions(-) create mode 160000 user/plugins/error create mode 160000 user/plugins/markdown-notices create mode 100644 user/plugins/pagination/CHANGELOG.md create mode 100644 user/plugins/pagination/LICENSE create mode 100644 user/plugins/pagination/README.md create mode 100644 user/plugins/pagination/blueprints.yaml create mode 100644 user/plugins/pagination/classes/paginationhelper.php create mode 100644 user/plugins/pagination/classes/paginationpage.php create mode 100644 user/plugins/pagination/css/pagination.css create mode 100644 user/plugins/pagination/hebe.json create mode 100644 user/plugins/pagination/languages.yaml create mode 100644 user/plugins/pagination/pagination.php create mode 100644 user/plugins/pagination/pagination.yaml create mode 100644 user/plugins/pagination/templates/partials/pagination.html.twig create mode 100644 user/plugins/pagination/twig/PaginationTwigExtension.php create mode 160000 user/plugins/problems create mode 100644 user/plugins/simplesearch/.gitignore create mode 100644 user/plugins/simplesearch/CHANGELOG.md create mode 100644 user/plugins/simplesearch/LICENSE create mode 100644 user/plugins/simplesearch/README.md create mode 100644 user/plugins/simplesearch/assets/readme_1.png create mode 100644 user/plugins/simplesearch/assets/search.svg create mode 100644 user/plugins/simplesearch/blueprints.yaml create mode 100644 user/plugins/simplesearch/composer.json create mode 100644 user/plugins/simplesearch/composer.lock create mode 100644 user/plugins/simplesearch/css/simplesearch.css create mode 100644 user/plugins/simplesearch/hebe.json create mode 100644 user/plugins/simplesearch/js/simplesearch.js create mode 100644 user/plugins/simplesearch/languages.yaml create mode 100644 user/plugins/simplesearch/pages/simplesearch.md create mode 100644 user/plugins/simplesearch/simplesearch.php create mode 100644 user/plugins/simplesearch/simplesearch.yaml create mode 100644 user/plugins/simplesearch/templates/partials/simplesearch_base.html.twig create mode 100644 user/plugins/simplesearch/templates/partials/simplesearch_item.html.twig create mode 100644 user/plugins/simplesearch/templates/partials/simplesearch_searchbox.html.twig create mode 100644 user/plugins/simplesearch/templates/simplesearch_results.html.twig create mode 100644 user/plugins/simplesearch/templates/simplesearch_results.json.twig create mode 100644 user/plugins/simplesearch/vendor/autoload.php create mode 100644 user/plugins/simplesearch/vendor/composer/ClassLoader.php create mode 100644 user/plugins/simplesearch/vendor/composer/LICENSE create mode 100644 user/plugins/simplesearch/vendor/composer/autoload_classmap.php create mode 100644 user/plugins/simplesearch/vendor/composer/autoload_namespaces.php create mode 100644 user/plugins/simplesearch/vendor/composer/autoload_psr4.php create mode 100644 user/plugins/simplesearch/vendor/composer/autoload_real.php create mode 100644 user/plugins/simplesearch/vendor/composer/autoload_static.php create mode 100644 user/plugins/simplesearch/vendor/composer/installed.json diff --git a/.gitignore b/.gitignore index a41fa66..aa62f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,6 @@ user/accounts/* !user/accounts/.* user/data/* !user/data/.* -user/plugins/* -!user/plugins/.* !user/themes/.* user/localhost/config/security.yaml user/config/security.yaml diff --git a/user/plugins/error b/user/plugins/error new file mode 160000 index 0000000..15748fd --- /dev/null +++ b/user/plugins/error @@ -0,0 +1 @@ +Subproject commit 15748fd1e024aad3108e2a1a2238c7bb1deb6ab9 diff --git a/user/plugins/markdown-notices b/user/plugins/markdown-notices new file mode 160000 index 0000000..b7b8961 --- /dev/null +++ b/user/plugins/markdown-notices @@ -0,0 +1 @@ +Subproject commit b7b8961eff19a760e7b2e9db52eff489234e6067 diff --git a/user/plugins/pagination/CHANGELOG.md b/user/plugins/pagination/CHANGELOG.md new file mode 100644 index 0000000..1d80bf1 --- /dev/null +++ b/user/plugins/pagination/CHANGELOG.md @@ -0,0 +1,89 @@ +# v1.4.2 +## 05/09/2019 + +1. [](#new) + * Add active class for active page [#37](https://github.com/getgrav/grav-plugin-pagination/pull/37) + * Added `ru` and `uk` translations [#38](https://github.com/getgrav/grav-plugin-pagination/pull/38) + +# v1.4.1 +## 08/28/2018 + +1. [](#bugfix) + * Reverted Twig fix to address broken `url` in Twig [#34](https://github.com/getgrav/grav-plugin-pagination/issues/34) + * Removed duplicate README text [#30](https://github.com/getgrav/grav-plugin-pagination/issues/30) + +# v1.4.0 +## 08/20/2018 + +1. [](#new) + * Added Twig pagination function [#22](https://github.com/getgrav/grav-plugin-pagination/pull/22) +1. [](#improved) + * Removed Grav trait in favor of `Grav::instance()` + * Changed delta blueprint type from `text` to `number` + * Code cleanup + +# v1.3.2 +## 05/03/2016 + +1. [](#new) + * Added default pagination to `page.collection.params.pagination` and `base_url` to `page.url` +1. [](#improved) + * Use common lang strings in blueprints +1. [](#bugfix) + * Improved and fixed README.md + +# v1.3.1 +## 08/29/2015 + +1. [](#bugfix) + * Fixed pagination URLs in certain situations + +# v1.3.0 +## 08/25/2015 + +1. [](#improved) + * Added blueprints for Grav Admin plugin + +# v1.2.7 +## 07/13/2015 + +1. [](#new) + * Added ability to provide `/tmpl:template_name` parameter to pagination URL +2. [](#improved) + * Allow checking of `header.content.pagination` **or** `header.pagination` to activate + +# v1.2.6 +## 07/12/2015 + +2. [](#improved) + * no `/page:1` for first page in pagination set + +# v1.2.5 +## 02/19/2015 + +2. [](#improved) + * Implemented new `param_sep` variable from Grav 0.9.18 + +# v1.2.4 +## 02/05/2015 + +2. [](#improved) + * Added support for HHVM + +# v1.2.3 +## 01/23/2015 + +2. [](#improved) + * Added microdata information for links + +# v1.2.2 +## 01/09/2015 + +2. [](#improved) + * NOTE: BREAKING CHANGE: Moved templates into `partials/` subfolder for consistency. + +# v1.2.1 +## 11/30/2014 + +1. [](#new) + * ChangeLog started... diff --git a/user/plugins/pagination/LICENSE b/user/plugins/pagination/LICENSE new file mode 100644 index 0000000..484793a --- /dev/null +++ b/user/plugins/pagination/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Grav + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/user/plugins/pagination/README.md b/user/plugins/pagination/README.md new file mode 100644 index 0000000..eae03eb --- /dev/null +++ b/user/plugins/pagination/README.md @@ -0,0 +1,151 @@ +# Grav Pagination Plugin + + +`Pagination` is a [Grav][grav] Plugin that allows to divide articles into discrete pages. + + +# Installation + +To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `pagination`. + +You should now have all the plugin files under + + /your/site/grav/user/plugins/pagination + +>> NOTE: This plugin is a modular component for Grav which requires [Grav](http://github.com/getgrav/grav), the [Error](https://github.com/getgrav/grav-plugin-error) and [Problems](https://github.com/getgrav/grav-plugin-problems) plugins, and a theme to be installed in order to operate. + +# Config Defaults + +``` +enabled: true +built_in_css: true +delta: 0 +``` + +The 'delta' value controls how many pages left and right of the current page are visible. If set to 0 all pages will be shown. + +If you need to change any value, then the best process is to copy the [pagination.yaml](pagination.yaml) file into your `users/config/plugins/` folder (create it if it doesn't exist), and then modify there. This will override the default settings. + +# Usage for content authors + +To use this plugin: + +- the `pagination` plugin must be installed and enabled +- the active theme must have pagination support (most Grav themes support pagination; if you’re building your own theme, see the next section for adding pagination support) + +On the content side, you should have a blog-like structure, for example: + +``` +blog/ + blog.md + my-cool-blog-post/ + item.md + another-post/ + item.md +``` + +Then in your `blog.md`, set up the page’s collection using YAML front-matter: + +```yaml +--- +title: My Gravtastic Blog +content: + items: '@self.children' + order: + by: header.date + dir: desc + pagination: true + limit: 10 +--- + +# My Gravtastic Blog +## A tale of **awesomazing** adventures +``` + +Your `/blog` page should now list the 10 most recent blog posts, and show pagination links. + + +# Usage for theme developers + +### Including the default pagination template + +If you are developing your own theme and want to support pagination, you need to include the pagination template in the relevant pages. For instance in `blog.html.twig`: + +```twig +{# /your/site/grav/user/themes/custom-theme/templates/blog.html.twig #} + +{% set collection = page.collection() %} + +{# Render the list of blog posts (automatically filtered when using pagination) #} +{% for child in collection %} + ... +{% endfor %} + +{# Render the pagination list #} +{% if config.plugins.pagination.enabled and collection.params.pagination %} + {% include 'partials/pagination.html.twig' with {'base_url':page.url, 'pagination':collection.params.pagination} %} +{% endif %} +``` + +### Overriding the pagination HTML + +If you want to override the look and feel of the pagination, copy the template file [pagination.html.twig][pagination] into the templates folder of your custom theme: + +``` +/your/site/grav/user/themes/custom-theme/templates/partials/pagination.html.twig +``` + +You can now edit the override and tweak it to meet your needs. + +[pagination]: templates/partials/pagination.html.twig +[grav]: http://github.com/getgrav/grav + +# Twig pagination function + +It is now possible to create paginated collections on demand in your twig file. You only need to: + +* activate the pagination plugin +* pick or create the collection you want +* paginate it +* render the paginated collection + +## Creating a paginated collection + +### Basic usage + +```twig +{# some collection #} +{% set collection = page.collection() %} +{# number of items per page #} +{% set limit = 5 %} +{% do paginate( collection, limit ) %} +``` + +This creates a paginated collection with `limit` items per page. As usual, any url parameters - except the page parameter, which is recreated - are passed on to the pagination bar's links. + +### Extended usage + +```twig +{% set collection = page.find( '/other/_events' ).children %} +{% set limit = 5 %} +{% set ignore_url_param_array = [ 'event' ] %} +{% do paginate( collection, limit, ignore_url_param_array ) %} +``` + +The above example is taken from http://ami-web.nl/events. This code creates a paginated collection with 5 items per page (the event summary list) which is presented together with an active event. The active event appears in only one of the summary pages. Consequently, the url parameter 'event' should be filtered out so it does not show up in the pagination bar's links, preventing inconsistencies with different page parameters. Any non listed url parameters (except the page parameter) are passed through unaffected. The requested page contains logic to pick a sensible default event. + +### Rendering the paginated collection + +The rest is identical to the standard procedure: + +```twig +{# create list of items #} +{% for item in collection %} + ... +{% endfor %} + +{# include the pagination bar #} +{% if config.plugins.pagination.enabled and collection.params.pagination %} + {% include 'partials/pagination.html.twig' with {'base_url':page.url, 'pagination':collection.params.pagination} %} +{% endif %} +``` diff --git a/user/plugins/pagination/blueprints.yaml b/user/plugins/pagination/blueprints.yaml new file mode 100644 index 0000000..2d08b8a --- /dev/null +++ b/user/plugins/pagination/blueprints.yaml @@ -0,0 +1,48 @@ +name: Pagination +version: 1.4.2 +description: "**Pagination** is a very useful plugin to help navigate a large collection of pages, such as for a **blog**." +icon: list-ol +author: + name: Team Grav + email: devs@getgrav.org + url: http://getgrav.org +homepage: https://github.com/getgrav/grav-plugin-pagination +keywords: pagination, plugin, pages, navigation +bugs: https://github.com/getgrav/grav-plugin-pagination/issues +license: MIT + +form: + validation: strict + fields: + enabled: + type: toggle + label: PLUGIN_ADMIN.PLUGIN_STATUS + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + delta: + type: number + size: x-small + label: PLUGIN_PAGINATION.DELTA + default: 0 + help: PLUGIN_PAGINATION.DELTA_HELP + validate: + type: number + min: 0 + + built_in_css: + type: toggle + label: PLUGIN_PAGINATION.BUILTIN_CSS + help: PLUGIN_PAGINATION.BUILTIN_CSS_HELP + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool diff --git a/user/plugins/pagination/classes/paginationhelper.php b/user/plugins/pagination/classes/paginationhelper.php new file mode 100644 index 0000000..a780824 --- /dev/null +++ b/user/plugins/pagination/classes/paginationhelper.php @@ -0,0 +1,132 @@ +current = $uri->currentPage(); + + // get params + $url_params = explode('/', ltrim($uri->params(), '/')); + + $params = $collection->params(); + + foreach ($url_params as $key => $value) { + if (strpos($value, 'page' . $config->get('system.param_sep')) !== false) { + unset($url_params[$key]); + } + if (isset($params['ignore_url_params'])) { + foreach ((array)$params['ignore_params'] as $ignore_param) { + if (strpos($value, $ignore_param . $config->get('system.param_sep')) !== false) { + unset($url_params[$key]); + } + } + } + } + + $this->url_params = '/'.implode('/', $url_params); + + // check for empty params + if ($this->url_params === '/') { + $this->url_params = ''; + } + + $this->items_per_page = $params['limit']; + $this->page_count = ceil($collection->count() / $this->items_per_page); + + for ($x=1; $x <= $this->page_count; $x++) { + if ($x === 1) { + $this->items[$x] = new PaginationPage($x, ''); + } else { + $this->items[$x] = new PaginationPage($x, '/page' . $config->get('system.param_sep') . $x); + } + } + } + + /** + * Returns true if current item has previous sibling. + * + * @return bool + */ + public function hasPrev() + { + if (array_key_exists($this->current -1, $this->items)) { + return true; + } + + return false; + } + + /** + * Returns true if current item has next sibling. + * + * @return bool + */ + public function hasNext() + { + if (array_key_exists($this->current +1, $this->items)) { + return true; + } + + return false; + } + + /** + * Return previous url. + * + * @return string|null + */ + public function prevUrl() + { + if (array_key_exists($this->current -1, $this->items)) { + return $this->items[$this->current -1]->url; + } + + return null; + } + + /** + * Return next url. + * + * @return string|null + */ + public function nextUrl() + { + if (array_key_exists($this->current +1, $this->items)) { + return $this->items[$this->current +1]->url; + } + + return null; + } + + public function params() + { + return $this->url_params; + } +} diff --git a/user/plugins/pagination/classes/paginationpage.php b/user/plugins/pagination/classes/paginationpage.php new file mode 100644 index 0000000..0a9e144 --- /dev/null +++ b/user/plugins/pagination/classes/paginationpage.php @@ -0,0 +1,84 @@ +grav = Grav::instance(); + $this->number = $number; + $this->url = $url; + $this->delta = $this->grav['config']->get('plugins.pagination.delta'); + } + + /** + * Returns true if the page is the current one. + * + * @return bool + */ + public function isCurrent() + { + if ($this->grav['uri']->currentPage() == $this->number) { + return true; + } + + return false; + } + + /** + * Returns true if the page is within a configurable delta of the current one + * + * @return bool + */ + public function isInDelta() + { + if (!$this->delta) { + return true; + } + + return abs($this->grav['uri']->currentPage() - $this->number) < $this->delta; + } + + /** + * Returns true is this page is the last/first one at the border of the delta range + * (Used to display a "gap" li element ...) + * + * @return bool + */ + public function isDeltaBorder() + { + if (!$this->delta) { + return false; + } + + return abs($this->grav['uri']->currentPage() - $this->number) == $this->delta; + } +} diff --git a/user/plugins/pagination/css/pagination.css b/user/plugins/pagination/css/pagination.css new file mode 100644 index 0000000..5103d37 --- /dev/null +++ b/user/plugins/pagination/css/pagination.css @@ -0,0 +1,17 @@ +.pagination { + list-style: none; + padding: 0; + margin: 3rem 0 1rem; + text-align: center; + color: #aaa; +} + +.pagination li { + display: inline-block; + border: 1px solid #eee; +} + +.pagination a, .pagination span { + display: inline-block; + padding: 4px 15px; +} diff --git a/user/plugins/pagination/hebe.json b/user/plugins/pagination/hebe.json new file mode 100644 index 0000000..c6e7d2f --- /dev/null +++ b/user/plugins/pagination/hebe.json @@ -0,0 +1,15 @@ +{ + "project":"grav-plugin-pagination", + "platforms":{ + "grav":{ + "nodes":{ + "plugin":[ + { + "source":"/", + "destination":"/user/plugins/pagination" + } + ] + } + } + } +} diff --git a/user/plugins/pagination/languages.yaml b/user/plugins/pagination/languages.yaml new file mode 100644 index 0000000..c8c32f4 --- /dev/null +++ b/user/plugins/pagination/languages.yaml @@ -0,0 +1,20 @@ +en: + PLUGIN_PAGINATION: + DELTA: 'Delta' + DELTA_HELP: 'How many pages to show left and right of the current page' + BUILTIN_CSS: 'Use built in CSS' + BUILTIN_CSS_HELP: 'Include the CSS provided by the Pagination plugin' + +ru: + PLUGIN_PAGINATION: + DELTA: 'Дельта' + DELTA_HELP: 'Сколько страниц показывать слева и справа от текущей страницы' + BUILTIN_CSS: 'Использовать встроенный CSS' + BUILTIN_CSS_HELP: 'Использовать CSS, предоставленный плагином Pagination' + +uk: + PLUGIN_PAGINATION: + DELTA: 'Дельта' + DELTA_HELP: 'Скільки сторінок відображатиметься ліворуч і праворуч від поточної сторінки' + BUILTIN_CSS: 'Використовувати вбудований CSS' + BUILTIN_CSS_HELP: 'Використовувати CSS, наданий плагіном Pagination' diff --git a/user/plugins/pagination/pagination.php b/user/plugins/pagination/pagination.php new file mode 100644 index 0000000..531ca0e --- /dev/null +++ b/user/plugins/pagination/pagination.php @@ -0,0 +1,141 @@ + ['onPluginsInitialized', 0] + ]; + } + + /** + * Initialize configuration + */ + public function onPluginsInitialized() + { + if ($this->isAdmin()) { + $this->active = false; + return; + } + + $this->enable([ + 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0], + 'onPageInitialized' => ['onPageInitialized', 0], + 'onTwigExtensions' => ['onTwigExtensions', 0] + ]); + } + + /** + * Add current directory to twig lookup paths. + */ + public function onTwigTemplatePaths() + { + $this->grav['twig']->twig_paths[] = __DIR__ . '/templates'; + } + + /** + * Add Twig Extensions + */ + public function onTwigExtensions() + { + require_once(__DIR__.'/twig/PaginationTwigExtension.php'); + + $this->grav['twig']->twig->addExtension(new PaginationTwigExtension()); + } + + /** + * Enable pagination if page has params.pagination = true. + */ + public function onPageInitialized() + { + /** @var Page $page */ + $page = $this->grav['page']; + + if ($page && ($page->value('header.content.pagination') || $page->value('header.pagination'))) { + $this->enable([ + 'onCollectionProcessed' => ['onCollectionProcessed', 0], + 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] + ]); + + $template = $this->grav['uri']->param('tmpl'); + if ($template) { + $page->template($template); + } + } + } + + /** + * Create pagination object for the page. + * + * @param Event $event + */ + public function onCollectionProcessed(Event $event) + { + /** @var Collection $collection */ + $collection = $event['collection']; + $params = $collection->params(); + + // Only add pagination if it has been enabled for the collection. + if (empty($params['pagination'])) { + return; + } + + if (!empty($params['limit']) && $collection->count() > $params['limit']) { + require_once __DIR__ . '/classes/paginationhelper.php'; + $this->pagination = new PaginationHelper($collection); + $collection->setParams(['pagination' => $this->pagination]); + } + } + + /** + * Set needed variables to display pagination. + */ + public function onTwigSiteVariables() + { + if ($this->config->get('plugins.pagination.built_in_css')) { + $this->grav['assets']->add('plugin://pagination/css/pagination.css'); + } + } + + /** + * pagination + * + * @param Collection $collection + * @param int $limit + * @param array $ignore_param_array url parameters to be ignored in page links + */ + public function paginateCollection( $collection, $limit, $ignore_param_array = []) + { + $collection->setParams(['pagination' => 'true']); + $collection->setParams(['limit' => $limit]); + $collection->setParams(['ignore_params' => $ignore_param_array]); + + if ($collection->count() > $limit) { + require_once __DIR__ . '/classes/paginationhelper.php'; + $this->pagination = new PaginationHelper($collection); + $collection->setParams(['pagination' => $this->pagination]); + + $uri = $this->grav['uri']; + $start = ($uri->currentPage() - 1) * $limit; + + if ($limit && $collection->count() > $limit) { + $collection->slice($start, $limit); + } + } + } +} diff --git a/user/plugins/pagination/pagination.yaml b/user/plugins/pagination/pagination.yaml new file mode 100644 index 0000000..c83c903 --- /dev/null +++ b/user/plugins/pagination/pagination.yaml @@ -0,0 +1,3 @@ +enabled: true +built_in_css: true +delta: 0 diff --git a/user/plugins/pagination/templates/partials/pagination.html.twig b/user/plugins/pagination/templates/partials/pagination.html.twig new file mode 100644 index 0000000..d7f05ff --- /dev/null +++ b/user/plugins/pagination/templates/partials/pagination.html.twig @@ -0,0 +1,34 @@ +{% set pagination = pagination|default(page.collection.params.pagination) %} +{% set base_url = base_url|default(page.url) %} + +{% if pagination|length > 1 %} + + + +{% endif %} diff --git a/user/plugins/pagination/twig/PaginationTwigExtension.php b/user/plugins/pagination/twig/PaginationTwigExtension.php new file mode 100644 index 0000000..ca1689d --- /dev/null +++ b/user/plugins/pagination/twig/PaginationTwigExtension.php @@ -0,0 +1,42 @@ +config = Grav::instance()['config']; + } + + /** + * Returns extension name. + * + * @return string + */ + public function getName() + { + return 'PaginationTwigExtension'; + } + + /** + * Return a list of all functions. + * + * @return array + */ + public function getFunctions() + { + return [ + new \Twig_SimpleFunction('paginate', [$this, 'paginateFunc']), + ]; + } + + public function paginateFunc($collection, $limit, $ignore_url_param_array = []) { + $pag = new PaginationPlugin($this, Grav::instance(), $this->config); + $pag->paginateCollection($collection, $limit, $ignore_url_param_array); + } +} diff --git a/user/plugins/problems b/user/plugins/problems new file mode 160000 index 0000000..404bcfc --- /dev/null +++ b/user/plugins/problems @@ -0,0 +1 @@ +Subproject commit 404bcfce6b95f5a3f3de39c1d2a8939abcd6db20 diff --git a/user/plugins/simplesearch/.gitignore b/user/plugins/simplesearch/.gitignore new file mode 100644 index 0000000..8828462 --- /dev/null +++ b/user/plugins/simplesearch/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +/.idea diff --git a/user/plugins/simplesearch/CHANGELOG.md b/user/plugins/simplesearch/CHANGELOG.md new file mode 100644 index 0000000..8da3cac --- /dev/null +++ b/user/plugins/simplesearch/CHANGELOG.md @@ -0,0 +1,279 @@ +# v2.2.1 +## 12/02/2020 + +1. [](#new) + * Require Grav 1.6.0+ + * Added autoloader + * Pass phpstan level 3 tests +1. [](#bugfix) + * Fixed `header_keys_ignored` configuration option not taking effect + +# v2.2.0 +## 09/14/2020 + +1. [](#improved) + * Added config option to choose keys to ignore when searching header + * Added ability to opt-out of page search by setting: `simplesearch: process: false` in page header + * Added `form` and `forms` to default list of ignores in page header searches +1. [](#bugfix) + * Fix issue to ensure matching ignore keys only checked at root level + * Fix issue where searchable header content was growing exponentially + +# v2.1.0 +## 05/27/2020 + +1. [](#new) + * Added ability to search page headers (excluding title, taxonomy, content) + +# v2.0.0 +## 05/13/2020 + +1. [](#new) + * Allow searching the content of modular pages [#170](https://github.com/getgrav/grav-plugin-simplesearch/pull/170) + * Allow configuration of Title/Content/Taxononmy searchable types [#184](https://github.com/getgrav/grav-plugin-simplesearch/pull/184) +1. [](#improved) + * Improved default vs custom search page functionality [#186](https://github.com/getgrav/grav-plugin-simplesearch/pull/186) + * Add field label for screen reader accessibility [#171](https://github.com/getgrav/grav-plugin-simplesearch/pull/171) + * Added Traditional Chinese [#169](https://github.com/getgrav/grav-plugin-simplesearch/pull/169) + * Updated Russian and English [#165](https://github.com/getgrav/grav-plugin-simplesearch/pull/165) + * Added Galacian [#155](https://github.com/getgrav/grav-plugin-simplesearch/pull/155) +1. [](#bugfix) + * Fixed input attribute [#165](https://github.com/getgrav/grav-plugin-simplesearch/pull/165) + +# v1.14.2 +## 12/07/2018 + +1. [](#improved) + * Optimize, cleanup and remove typos [#163](https://github.com/getgrav/grav-plugin-simplesearch/pull/163) + * Removed `blog` as default filter [#166](https://github.com/getgrav/grav-plugin-simplesearch/pull/166) + * Polish translation [#144](https://github.com/getgrav/grav-plugin-simplesearch/pull/144) + * Kazakh translation [#153](https://github.com/getgrav/grav-plugin-simplesearch/pull/153) + * Spelling corrections [#145](https://github.com/getgrav/grav-plugin-simplesearch/pull/145) +1. [](#bugfix) + * Fix JS to work with IE11 [#161](https://github.com/getgrav/grav-plugin-simplesearch/pull/161) + * Updated javascript to be compatible with IE11 [#161](https://github.com/getgrav/grav-plugin-simplesearch/pull/161) + * Ensure `$values` is an array to prevent PHP error on implode [#146](https://github.com/getgrav/grav-plugin-simplesearch/pull/146) + +# v1.14.1 +## 01/11/2018 + +1. [](#bugfix) + * Fix for Gantry5 themes + +# v1.14.0 +## 01/08/2018 + +1. [](#new) + * Added Danish translations [#127](https://github.com/getgrav/grav-plugin-simplesearch/pull/127) +1. [](#improved) + * New option to disable built-in JS [#130](https://github.com/getgrav/grav-plugin-simplesearch/pull/130) + * Changed elipsis from `...` to `…` [#133](https://github.com/getgrav/grav-plugin-simplesearch/pull/133) + * Added missing French translations [#136](https://github.com/getgrav/grav-plugin-simplesearch/pull/136) + * Added missing German translations [#128](https://github.com/getgrav/grav-plugin-simplesearch/pull/128) +1. [](#bugfix) + * Escaped `query` in Twig templates for XSS protection + +# v1.13.0 +## 07/26/2017 + +1. [](#improved) + * Support for multiple forms and fields in the same page +1. [](#bugfix) + * Fix typo in `SEARCH_FIELD_MINIUMUM_CHARACTERS` translation string + * Fixed validation and JS submission + * Separated JS from inline to file + * Fixed issue with min query length always enforced. It is now possible to have no minimum length by setting to `false` or `0` + +# v1.12.0 +## 05/31/2017 + +1. [](#new) + * Added option to switch between Rendered HTML and Raw Markdown content searching. Raw Markdown is faster than default. + +# v1.11.0 +## 05/29/2017 + +1. [](#new) + * Allow to use "@none"/"none@" in the "Category filter" in Admin to allow removing the filter + +# v1.10.2 +## 04/19/2017 + +1. [](#bugfix) + * Only check ACL if the Login plugin is installed [#112](https://github.com/getgrav/grav-plugin-simplesearch/pull/112) + +# v1.10.1 +## 04/11/2017 + +1. [](#new) + * Added Portuguese translation + * Add hint when the minimum search field length is not matched +1. [](#bugfix) + * Default `ignore_accented_characters` to false + * Fallback to regular search if searching with `ignore_accented_characters` on an unsupported charset raises an exception [#107](https://github.com/getgrav/grav-plugin-simplesearch/issues/107) + * Check ACL before listing a page in the search results [#102](https://github.com/getgrav/grav-plugin-simplesearch/pull/102) + * Fix with ignoring `min_query_length` when using the button [#99](https://github.com/getgrav/grav-plugin-simplesearch/issues/99) + +# v1.10.0 +## 01/23/2017 + +1. [](#new) + * Added spanish translation + * Added japanese translation + * Added persian translation +1. [](#improved) + * Added option to switch between Rendered HTML and Raw Markdown content searching. Raw Markdown is faster than default. + * Removed jQuery dependency, fixes issue when jQuery is loaded in the footer [#57](https://github.com/getgrav/grav-plugin-simplesearch/pull/57) + * Added option to ignore accents when searching [#89](https://github.com/getgrav/grav-plugin-simplesearch/pull/89) +1. [](#bugfix) + * Remove unpublished and un-routable pages from the result set + * Fixed issue when using @self as route + * Fix overloaded property issue when searching on a page with simplesearch header [#80](https://github.com/getgrav/grav-plugin-simplesearch/issues/80) + * Fix issue with empty string and leading commas [#71](https://github.com/getgrav/grav-plugin-simplesearch/issues/71) + +# v1.9.3 +## 10/19/2016 + +1. [](#bugfix) + * Fixed an issue with invalid syntax in `route: @self` logic + +# v1.9.2 +## 09/19/2016 + +1. [](#bugfix) + * Reverted change in events - causing problems + * Reverted fix for `route: @self`, breaking `filter: @self` (used in getgrav.org) + +# v1.9.1 +## 09/08/2016 + +1. [](#bugfix) + * Fixed logic to use `onPageInitialized` event + +# v1.9.0 +## 09/06/2016 + +1. [](#new) + * Multiple search boxes support [#52](https://github.com/getgrav/grav-plugin-simplesearch/pull/52) + * Added Croatian and Russian translation +1. [](#improved) + * Added support for Grav's autoescape twig setting +1. [](#bugfix) + * Fix searching on `@self `[#53](https://github.com/getgrav/grav-plugin-simplesearch/pull/53) + +# v1.8.0 +## 07/14/2016 + +1. [](#new) + * Added dutch and romanian +1. [](#bugfix) + * Fix translating the search input placeholder + +# v1.7.1 +## 05/03/2016 + +1. [](#new) + * Added configurable `min length` option for how many characters needed before you can search + +# v1.7.0 +## 04/30/2016 + +1. [](#new) + * Added support for taxonomy searching in regular searches (not just on-page searches as was the case previously) + * Added support for `route: '@self'` to use the route of the current page without specifying it. + * Added display search button option - #33 + * Refactored code for clarity + +# v1.6.2 +## 01/06/2016 + +1. [](#improved) + * Improved the README instructions on how to save all pages + +# v1.6.1 +## 11/11/2015 + +1. [](#improved) + * Strip HTML tags from title and content before searching + +# v1.6.0 +## 11/11/2015 + +1. [](#new) + * Removing `filter:` from configuration will search **ALL** pages + +# v1.5.1 +## 10/15/2015 + +1. [](#improved) + * Minor performance fix + * Updated README.md with more help +1. [](#bugfix) + * Fix for special character searches + +# v1.5.0 +## 10/07/2015 + +1. [](#new) + * Allow simplesearch to work with on-page collections + +# v1.4.1 +## 08/31/2015 + +1. [](#improved) + * Fixed some blueprint issues + +# v1.4.0 +## 08/25/2015 + +1. [](#improved) + * Added blueprints for Grav Admin plugin + * Added results sorting + +# v1.3.0 +## 07/21/2015 + +1. [](#new) + * Added support for modular pages in results + +# v1.2.7 +## 07/17/2015 + +1. [](#bugfix) + * Fixed "Undefined index: extension" error + +# v1.2.6 +## 07/14/2015 + +1. [](#bugfix) + * Fixed URL issue that showed up with multi-languages + +# v1.2.5 +## 04/24/2015 + +1. [](#bugfix) + * Fixed issue with broken image + +# v1.2.4 +## 02/19/2015 + +2. [](#improved) + * Implemented new `param_sep` variable from Grav 0.9.18 + +# v1.2.3 +## 01/06/2015 + +1. [](#improved) + * Improved `README.md` file with more information + +# v1.2.2 +## 12/21/2014 + +1. [](#bugfix) + * Fix for invalid base_url in some instances + +# v1.2.1 +## 11/30/2014 + +1. [](#new) + * ChangeLog started... diff --git a/user/plugins/simplesearch/LICENSE b/user/plugins/simplesearch/LICENSE new file mode 100644 index 0000000..484793a --- /dev/null +++ b/user/plugins/simplesearch/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Grav + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/user/plugins/simplesearch/README.md b/user/plugins/simplesearch/README.md new file mode 100644 index 0000000..f8d7d0a --- /dev/null +++ b/user/plugins/simplesearch/README.md @@ -0,0 +1,209 @@ +# Grav SimpleSearch Plugin + +![SimpleSearch](assets/readme_1.png) + +`SimpleSearch` is a simple, yet very powerful [Grav][grav] plugin that adds search capabilities to your Grav instance. By default it can search Page **Titles**, **Content**, **Taxonomy**, and also a raw page **Header**. + +# Installation + +Installing the SimpleSearch plugin can be done in one of two ways. Our GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. + +## GPM Installation (Preferred) + +The simplest way to install this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm) through your system's Terminal (also called the command line). From the root of your Grav install type: + + bin/gpm install simplesearch + +This will install the SimpleSearch plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/simplesearch`. + +## Manual Installation + +To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `simplesearch`. You can find these files either on [GitHub](https://github.com/getgrav/grav-plugin-simplesearch) or via [GetGrav.org](http://getgrav.org/downloads/plugins#extras). + +You should now have all the plugin files under + + /your/site/grav/user/plugins/simplesearch + +> NOTE: This plugin is a modular component for Grav which requires [Grav](http://github.com/getgrav/grav), the [Error](https://github.com/getgrav/grav-plugin-error) and [Problems](https://github.com/getgrav/grav-plugin-problems) plugins, and a theme to be installed in order to operate. + +# Config Options + +To effectively use the plugin, you first need to create an override config. To do so, create the folder `user/config/plugins` (if it doesn't exist already) and copy the [simplesearch.yaml][simplesearch] config file in there. + +``` +enabled: true +built_in_css: true +built_in_js: true +display_button: false +min_query_length: 3 +route: /search +search_content: rendered +template: simplesearch_results +filters: + category: +filter_combinator: and +ignore_accented_characters: false +order: + by: date + dir: desc +searchable_types: + title: true + content: true + taxonomy: true + header: false +header_keys_ignored: ['title', 'taxonomy','content', 'form', 'forms', 'media_order'] +``` + +By creating the configuration file: `user/config/plugins/simplesearch.yaml` you have effectively created a site-wide configuration for SimpleSearch. However, you may want to have multiple searches. + +> NOTE: If you want to search **ALL PAGES** just keep the `filters` section empty. + +To accomplish multiple search types in a single site, you should use **page-based** configuration. This is simple to do, simply provide any or all of the configuration options under a `simplesearch:` header in your page frontmatter. For example: + +``` +simplesearch: + process: true + route: @self + filters: + - @self + - @taxonomy: [tag] + filter_combinator: and +``` + + These page headers will only be taken into account if the search route points to this page. For example: here the route points to `@self` which in turn resolves to `/blog`. You can also specify the route explicitly with `route: /blog` if you so choose. This header is within the `/user/pages/blog/blog.md` file. We will cover this self-controlled form of search handling below. + +# Usage + +There are two approaches to using SimpleSearch. + +## 1. Standalone Search Page + +This is the traditional approach and involves having a searchbox 'somewhere' on your site. When you search you are shown a new page that displays the search results. On this page you will see a summary of the results and be able to click a link to visit each applicable page within your site. Think about how **Google** and other traditional search engines work. + +After installing the SimpleSearch plugin, you can add a simple **searchbox** to your site by including the provided twig template. Or copy it from the plugin to your theme and customize it as you please: + +``` +{% include 'partials/simplesearch_searchbox.html.twig' %} +``` + +By default the **simplesearch_searchbox** Twig template uses the `route` as defined in the configuration. The SimpleSearch plugin uses this route and then appends a `query:` parameter to create the following final URL. + +``` +http://yoursite.com/search/query:something +``` + +1. `/search`: This is the **route** setting and it can be changed +2. `/query:something`: This is the query itself, where `something` is what you are searching for. + +The plugin actively looks for URLs requested that match the configured `route` and if so it intercepts the call and renders the results template as specified by the configuration options, (defaults to `simplesearch_results.html.twig` as provided by the plugin). + +With this approach, the filters control which pages are searched. You can have multiple taxonomy filters here, and can configure the combinator to require **any** match (via `or`) or require **all** conditions to match (via `and`). + +You can also completely customize the look and feel of the results by overriding the template. There are two methods to do this. + +1. Copy the file [templates/simplesearch_results.html.twig][results] under your theme templates `user/themes/_your-theme_/templates/` and customize it. + +2. Create your very own results output. For this you need to change the `template` reference in the config (let's say **mysearch_results**). In your theme you would then create the new template under `user/themes/_your-theme_/templates/mysearch_results.html.twig` and write your customizations. This is how it looks by default: + + ``` + {% extends 'partials/simplesearch_base.html.twig' %} + + {% block content %} +
+

Search Results

+

Query: "{{ query }}" - Found {{ search_results.count }} {{ 'Item'|pluralize(search_results.count) }}

+ + {% for page in search_results %} + {% include 'partials/simplesearch_item.html.twig' with {'page':page} %} + {% endfor %} +
+ {% endblock %} + ``` + +## 2. Self-Controlled Search Page + +This is a new feature of SimpleSearch and it's a very useful and simple way to provide a 'filter' like search of a collection listing page. In this example, we will assume you have a Blog listing page you wish to be able to search and filter based on a search box. + +To accomplish this, you need to use the page-based configuration as described above, and configure multiple filters, `@self` to use the page's content collection: http://learn.getgrav.org/content/headers#collection-headers + +``` +content: + items: @self.children + order: + by: date + dir: desc +``` + +This will mean the search will only search pages that this page already is using for the collection. The Items could be anything the page collections support: + +For further help with the `filters` and `order` settings, please refer to our [Taxonomy][taxonomy] and [Headers][headers] documentation. + +Multiple filters can be provided, and in order to search in the page's **Tag** field you would add `- @taxonomy: [tag]` as shown in the configuration example above. + +The only thing needed to provide this functionality is a search box that points to the current page and appends the `query` parameter. You can again simple include the sample `simplesearch_searchbox.html.twig` file or add your own. Because the route is configured to point to the blog page, and because the blog page already iterates over a collection, SimpleSearch will replace the page collection with the search-filtered collection. No results page is required. + +## Performance + +Simplesearch is not a full-fledged index-powered search engine. It merely iterates over the pages and searches the content and title for matching strings. That's it. This is not going to result in screaming fast searches if your site has lots of content. One way to optimize things a little is to change the `search_content` configuration option from `rendered` to `raw`. This means the `rawMarkdown()` method is used rather than the `content()` method, to retrieve the page content, and in turn means plugin events, markdown processing, image processing, and other time-consuming tasks are not performed. This can often yield adequate search results without the need for all this extra work. + +## Searching Taxonomy + +By default **SimpleSearch** will search in the **Title**, **Content**, and **Taxonomy**. All taxonomy will be searched unless you provide a **taxonomy filter** either in the page, or in the global plugin configuration: + +``` +filters: + - @taxonomy: [tag] +``` + +This will ensure that only **tag** taxonomy types will be searched for the query. + +``` +filters: + - @taxonomy: [tag, author] +``` + +Will ensure that both **tag** and **author** taxonomy types are searched. + +As **all taxonomy types are searched by default**, in order to stop searching into taxonomies completely simply set the filter to false: + +``` +filters: + - '@taxonomy': false +``` + +## Ignoring a page + +A page can be setup to "opt-out" of being included in the search results by setting the following in page frontmatter: + +```yaml +simplesearch: + process: false +``` + +## Ignoring accented characters + +You can tell Simplesearch to return a positive value when searching for characters that have an accent. So `éè` for example will be both equivalent to `e`. + +To do so, enable _Ignore accented characters_ in Admin, or manually set `ignore_accented_characters` to true in the plugin configuration. +The `en_US` locale must be installed on the server. + +# Updating + +As development for SimpleSearch continues, new versions may become available that add additional features and functionality, improve compatibility with newer Grav releases, and generally provide a better user experience. Updating SimpleSearch is easy, and can be done through Grav's GPM system, as well as manually. + +## GPM Update (Preferred) + +The simplest way to update this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm). You can do this with this by navigating to the root directory of your Grav install using your system's Terminal (also called command line) and typing the following: + + bin/gpm update simplesearch + +This command will check your Grav install to see if your SimpleSearch plugin is due for an update. If a newer release is found, you will be asked whether or not you wish to update. To continue, type `y` and hit enter. The plugin will automatically update and clear Grav's cache. + + +> Note: Any changes you have made to any of the files listed under this directory will also be removed and replaced by the new set. Any files located elsewhere (for example a YAML settings file placed in `user/config/plugins`) will remain intact. + +[taxonomy]: http://learn.getgrav.org/content/taxonomy +[headers]: http://learn.getgrav.org/content/headers +[grav]: http://github.com/getgrav/grav +[simplesearch]: simplesearch.yaml +[results]: templates/simplesearch_results.html.twig diff --git a/user/plugins/simplesearch/assets/readme_1.png b/user/plugins/simplesearch/assets/readme_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a5156cf10b7f99b25b04e341c9ca07384b06c40c GIT binary patch literal 113109 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%0D*XeHzs967WMN3fbXn-Op2~d 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 R9^*m4=5z{uvc*0q{Wqgs=NSM1 literal 0 HcmV?d00001 diff --git a/user/plugins/simplesearch/assets/search.svg b/user/plugins/simplesearch/assets/search.svg new file mode 100644 index 0000000..67d4f0c --- /dev/null +++ b/user/plugins/simplesearch/assets/search.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/user/plugins/simplesearch/blueprints.yaml b/user/plugins/simplesearch/blueprints.yaml new file mode 100644 index 0000000..d30cd61 --- /dev/null +++ b/user/plugins/simplesearch/blueprints.yaml @@ -0,0 +1,172 @@ +name: SimpleSearch +type: plugin +slug: simplesearch +version: 2.2.1 +description: "Don't be fooled, the **SimpleSearch** plugin provides a **fast** and highly **configurable** way to search your content." +icon: search +author: + name: Team Grav + email: devs@getgrav.org + url: http://getgrav.org +homepage: https://github.com/getgrav/grav-plugin-simplesearch +demo: http://demo.getgrav.org/blog-skeleton +keywords: simplesearch, plugin, search, page, content, find +bugs: https://github.com/getgrav/grav-plugin-simplesearch/issues +license: MIT + +dependencies: + - { name: grav, version: '>=1.6.0' } + +form: + validation: strict + fields: + enabled: + type: toggle + label: PLUGIN_ADMIN.PLUGIN_STATUS + highlight: 1 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + search_content: + type: select + size: medium + classes: fancy + label: PLUGIN_SIMPLESEARCH.SEARCH_CONTENT + default: rendered + options: + rendered: PLUGIN_SIMPLESEARCH.RENDERED_CONTENT + raw: PLUGIN_SIMPLESEARCH.RAW_CONTENT + + built_in_css: + type: toggle + label: PLUGIN_SIMPLESEARCH.BUILTIN_CSS + help: PLUGIN_SIMPLESEARCH.BUILTIN_CSS_HELP + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + built_in_js: + type: toggle + label: PLUGIN_SIMPLESEARCH.BUILTIN_JS + help: PLUGIN_SIMPLESEARCH.BUILTIN_JS_HELP + highlight: 1 + default: 1 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + display_button: + type: toggle + label: PLUGIN_SIMPLESEARCH.DISPLAY_SEARCH_BUTTON + help: PLUGIN_SIMPLESEARCH.DISPLAY_SEARCH_BUTTON_HELP + highlight: 0 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + ignore_accented_characters: + type: toggle + label: PLUGIN_SIMPLESEARCH.IGNORE_ACCENDED_CHARACTERS + help: PLUGIN_SIMPLESEARCH.IGNORE_ACCENDED_CHARACTERS_HELP + highlight: 0 + default: 0 + options: + 1: PLUGIN_ADMIN.ENABLED + 0: PLUGIN_ADMIN.DISABLED + validate: + type: bool + + min_query_length: + type: text + size: x-small + label: PLUGIN_SIMPLESEARCH.MIN_QUERY_LENGTH + help: PLUGIN_SIMPLESEARCH.MIN_QUERY_LENGTH_HELP + validate: + type: number + min: 0 + + route: + type: text + size: medium + label: PLUGIN_SIMPLESEARCH.SEARCH_PAGE_ROUTE + help: PLUGIN_SIMPLESEARCH.SEARCH_PAGE_ROUTE_HELP + default: /random + + searchable_types: + type: checkboxes + label: PLUGIN_SIMPLESEARCH.SEARCHABLE_TYPES + description: PLUGIN_SIMPLESEARCH.SEARCHABLE_TYPES_DESCRIPTION + options: + title: Title + content: Content + header: Header + taxonomy: Taxonomy + use: keys + + header_keys_ignored: + type: selectize + size: large + label: PLUGIN_SIMPLESEARCH.HEADER_KEYS_IGNORED + help: PLUGIN_SIMPLESEARCH.HEADER_KEYS_IGNORED_HELP + classes: fancy + validate: + type: commalist + + template: + type: text + size: medium + label: PLUGIN_SIMPLESEARCH.SEARCH_PAGE_TEMPLATE + help: PLUGIN_SIMPLESEARCH.SEARCH_PAGE_TEMPLATE_HELP + default: simplesearch_results + + filters.category: + type: selectize + label: PLUGIN_SIMPLESEARCH.CATEGORY_FILTER + help: PLUGIN_SIMPLESEARCH.CATEGORY_FILTER_HELP + validate: + type: commalist + + filter_combinator: + type: select + size: medium + classes: fancy + label: PLUGIN_SIMPLESEARCH.FILTER_COMBINATOR + default: and + options: + and: PLUGIN_SIMPLESEARCH.AND_COMBINATOR + or: PLUGIN_SIMPLESEARCH.OR_COMBINATOR + + order.by: + type: select + size: long + classes: fancy + label: PLUGIN_ADMIN.DEFAULT_ORDERING + help: PLUGIN_ADMIN.DEFAULT_ORDERING_HELP + options: + default: PLUGIN_ADMIN.DEFAULT_ORDERING_DEFAULT + folder: PLUGIN_ADMIN.DEFAULT_ORDERING_FOLDER + title: PLUGIN_ADMIN.DEFAULT_ORDERING_TITLE + date: PLUGIN_ADMIN.DEFAULT_ORDERING_DATE + + order.dir: + type: toggle + label: PLUGIN_ADMIN.DEFAULT_ORDER_DIRECTION + highlight: asc + default: desc + help: PLUGIN_ADMIN.DEFAULT_ORDER_DIRECTION_HELP + options: + asc: PLUGIN_ADMIN.ASCENDING + desc: PLUGIN_ADMIN.DESCENDING diff --git a/user/plugins/simplesearch/composer.json b/user/plugins/simplesearch/composer.json new file mode 100644 index 0000000..491eff2 --- /dev/null +++ b/user/plugins/simplesearch/composer.json @@ -0,0 +1,35 @@ +{ + "name": "getgrav/grav-plugin-simplesearch", + "type": "grav-plugin", + "description": "Simple search plugin for Grav CMS", + "keywords": ["simplesearch", "search", "plugin"], + "homepage": "https://github.com/getgrav/grav-plugin-simplesearch", + "license": "MIT", + "authors": [ + { + "name": "Team Grav", + "email": "devs@getgrav.org", + "homepage": "https://getgrav.org", + "role": "Developer" + } + ], + "support": { + "issues": "https://github.com/getgrav/grav-plugin-simplesearch/issues", + "irc": "https://chat.getgrav.org", + "forum": "https://discourse.getgrav.org", + "docs": "https://github.com/getgrav/grav-plugin-simplesearch/blob/master/README.md" + }, + "require": { + "php": ">=7.1.3" + }, + "autoload": { + "classmap": [ + "simplesearch.php" + ] + }, + "config": { + "platform": { + "php": "7.1.3" + } + } +} diff --git a/user/plugins/simplesearch/composer.lock b/user/plugins/simplesearch/composer.lock new file mode 100644 index 0000000..209fc50 --- /dev/null +++ b/user/plugins/simplesearch/composer.lock @@ -0,0 +1,22 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "9386d6cf08fcab33be418ed5d678b2ce", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1.3" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.1.3" + } +} diff --git a/user/plugins/simplesearch/css/simplesearch.css b/user/plugins/simplesearch/css/simplesearch.css new file mode 100644 index 0000000..3d0eba7 --- /dev/null +++ b/user/plugins/simplesearch/css/simplesearch.css @@ -0,0 +1,42 @@ +.search-wrapper .search-input { + width: 80%; + display: inline-block; +} + +.search-submit { + display: inline-block; + border-radius: 4px; + background: #eee; + border: 1px solid #ccc; + vertical-align: top; +} + +.search-submit img { + width: 20px; + vertical-align: middle; +} + +.search-image { + float: left; +} + +.search-item { + margin-left: 130px; + margin-bottom: 50px; +} + +.search-item p { + margin: 0; +} + +.search-title h3 { + margin: 0; +} + +.search-details { + font-size: 13px; +} + +.search-row:last-child hr { + display: none; +} \ No newline at end of file diff --git a/user/plugins/simplesearch/hebe.json b/user/plugins/simplesearch/hebe.json new file mode 100644 index 0000000..521b391 --- /dev/null +++ b/user/plugins/simplesearch/hebe.json @@ -0,0 +1,15 @@ +{ + "project":"grav-plugin-simplesearch", + "platforms":{ + "grav":{ + "nodes":{ + "plugin":[ + { + "source":"/", + "destination":"/user/plugins/simplesearch" + } + ] + } + } + } +} diff --git a/user/plugins/simplesearch/js/simplesearch.js b/user/plugins/simplesearch/js/simplesearch.js new file mode 100644 index 0000000..ce0f7a8 --- /dev/null +++ b/user/plugins/simplesearch/js/simplesearch.js @@ -0,0 +1,32 @@ +((function(){ + if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; + } + var findAncestor = function(el, selector) { + while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el, selector))) {} + return el; + }; + + var fields = document.querySelectorAll('input[name="searchfield"][data-search-input]'); + Array.prototype.forEach.call(fields, function(field) { + var form = findAncestor(field, 'form[data-simplesearch-form]'), + min = field.getAttribute('data-min') || false, + location = field.getAttribute('data-search-input'), + separator = field.getAttribute('data-search-separator'); + + if (min) { + var invalid = field.getAttribute('data-search-invalid'); + field.addEventListener('keydown', function() { + field.setCustomValidity(field.value.length >= min ? '' : invalid); + }); + } + + form.addEventListener('submit', function(event) { + event.preventDefault(); + + if (field.checkValidity()) { + window.location.href = location + separator + field.value; + } + }); + }); +})()); diff --git a/user/plugins/simplesearch/languages.yaml b/user/plugins/simplesearch/languages.yaml new file mode 100644 index 0000000..1ba12a4 --- /dev/null +++ b/user/plugins/simplesearch/languages.yaml @@ -0,0 +1,226 @@ +en: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: 'Search …' + SEARCH_RESULTS: 'Search Results' + SEARCH_RESULTS_SUMMARY_SINGULAR: 'Query: %s found one result' + SEARCH_RESULTS_SUMMARY_PLURAL: 'Query: %s found %s results' + SEARCH_FIELD_MINIMUM_CHARACTERS: 'Please add at least %s characters' + SEARCH_CONTENT: 'Search Content' + RENDERED_CONTENT: 'Rendered Content (Slower)' + RAW_CONTENT: 'Raw Markdown Content (Faster)' + BUILTIN_CSS: 'Use built in CSS' + BUILTIN_CSS_HELP: 'Include the CSS provided by the simplesearch plugin' + BUILTIN_JS: 'Use built in JavaScript' + BUILTIN_JS_HELP: 'Include the JavaScript provided by the simplesearch plugin' + DISPLAY_SEARCH_BUTTON: 'Display Search Button' + DISPLAY_SEARCH_BUTTON_HELP: 'Display a search button near the search field' + IGNORE_ACCENDED_CHARACTERS: 'Ignore accented characters' + IGNORE_ACCENDED_CHARACTERS_HELP: 'If enabled, search terms will match accented characters regardless to their diacritics i.e. search results will show up for "cafe" and "café" no matter how you typed it.' + MIN_QUERY_LENGTH: 'Minimum query length' + MIN_QUERY_LENGTH_HELP: 'The minimum number of characters needed before search can be submitted' + SEARCH_PAGE_ROUTE: 'Route' + SEARCH_PAGE_ROUTE_HELP: 'Default route of the simplesearch plugin' + SEARCH_PAGE_TEMPLATE: 'Template' + SEARCH_PAGE_TEMPLATE_HELP: 'Name of the template for the search results' + CATEGORY_FILTER: 'Category filter' + CATEGORY_FILTER_HELP: 'Comma separated list of category names. Enter "@none" to search in all pages.' + FILTER_COMBINATOR: 'Filter Combinator' + AND_COMBINATOR: 'And - Boolean &&' + OR_COMBINATOR: 'Or - Boolean ||' + SEARCHABLE_TYPES: "Searchable Types" + SEARCHABLE_TYPES_DESCRIPTION: "Title = Search Page Title
Content = Search Page Content
Header = Search Raw Page Headers
Taxonomy = Search Taxonomy" + HEADER_KEYS_IGNORED: Header Keys to Ignore + HEADER_KEYS_IGNORED_HELP: The root-level header keys that should be skipped when searching type "Header" + +ro: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Caută …" + SEARCH_RESULTS: "Rezultatele căutării" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Căutarea: %s a găsit un rezultat" + SEARCH_RESULTS_SUMMARY_PLURAL: "Căutarea: %s a găsit %s rezultate" + +de: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Suche …" + SEARCH_RESULTS: "Suchergebnisse" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Suche: %s fand ein Ergebnis" + SEARCH_RESULTS_SUMMARY_PLURAL: "Suche: %s fand %s Ergebnisse" + SEARCH_FIELD_MINIMUM_CHARACTERS: "Bitte geben Sie mindestens %s Zeichen ein" + SEARCH_VALUE: 'Suchen' + +fr: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Recherche …" + SEARCH_RESULTS: "Résultats de la recherche" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Recherche : Un résultat trouvé pour %s" + SEARCH_RESULTS_SUMMARY_PLURAL: "Recherche : %2$s résultats trouvés pour %1$s" + SEARCH_FIELD_MINIMUM_CHARACTERS: "Veuillez ajouter au moins %s caractères" + +it: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Cerca …" + SEARCH_RESULTS: "Risultati della ricerca" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Ricerca: %s. Trovato un risultato" + SEARCH_RESULTS_SUMMARY_PLURAL: "Ricerca: %s. Trovati %s risultati" + SEARCH_FIELD_MINIMUM_CHARACTERS: "Inserisci almeno %s caratteri" + +zh: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "搜索 …" + SEARCH_RESULTS: "搜索结果" + SEARCH_RESULTS_SUMMARY_SINGULAR: "查询: %s 找到 1 个结果" + SEARCH_RESULTS_SUMMARY_PLURAL: "查询: %s 找到 %s 个结果" + +zh-TW: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "搜尋 …" + SEARCH_RESULTS: "搜尋結果" + SEARCH_RESULTS_SUMMARY_SINGULAR: "查詢: %s 找到 1 個結果" + SEARCH_RESULTS_SUMMARY_PLURAL: "查詢: %s 找到 %s 個結果" + SEARCH_FIELD_MINIMUM_CHARACTERS: "請輸入至少 %s 個字元" +nl: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Zoeken …" + SEARCH_RESULTS: "Zoek resultaten" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Query: %s is 1 keer gevonden" + SEARCH_RESULTS_SUMMARY_PLURAL: "Query: %s is %s keer gevonden" + SEARCH_FIELD_MINIMUM_CHARACTERS: "Geef minstens %s tekens in" + +hr: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Traži …" + SEARCH_RESULTS: "Rezultati pretrage" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Upit: %s je pronašao jedan rezultat" + SEARCH_RESULTS_SUMMARY_PLURAL: "Upit: %s je pronašao %s rezultata" + +ru: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: 'Найти …' + SEARCH_RESULTS: 'Результат поиска' + SEARCH_RESULTS_SUMMARY_SINGULAR: 'По запросу: %s найден 1 результат' + SEARCH_RESULTS_SUMMARY_PLURAL: 'По запросу: %s результатов найдено %s' + SEARCH_FIELD_MINIMUM_CHARACTERS: 'Добавьте не менее %s символов' + SEARCH_CONTENT: 'Поиск контента' + RENDERED_CONTENT: 'Обработанный контент (медленнее)' + RAW_CONTENT: 'Необработанный Markdown контент (быстрее)' + BUILTIN_CSS: 'Использовать встроенный CSS' + BUILTIN_CSS_HELP: 'Использовать CSS, предоставленный плагином simplesearch' + BUILTIN_JS: 'Использовать встроенный JavaScript' + BUILTIN_JS_HELP: 'Использовать JavaScript, предоставленный плагином simplesearch' + DISPLAY_SEARCH_BUTTON: 'Показать кнопку поиска' + DISPLAY_SEARCH_BUTTON_HELP: 'Показать кнопку поиска рядом с полем поиска' + IGNORE_ACCENDED_CHARACTERS: 'Игнорировать акцентированные символы' + IGNORE_ACCENDED_CHARACTERS_HELP: 'Если этот параметр включен, поисковые термины будут совпадать с акцентированными символами, независимо от их диакритических знаков, т.е. результаты поиска будут отображаться как "кафе" и "кафе́", независимо от того, как вы их набрали' + MIN_QUERY_LENGTH: 'Минимальная длина запроса' + MIN_QUERY_LENGTH_HELP: 'Минимальное количество символов, необходимое для отправки запроса' + SEARCH_PAGE_ROUTE: 'Маршрут' + SEARCH_PAGE_ROUTE_HELP: 'Маршрут по умолчанию для плагина simplesearch' + SEARCH_PAGE_TEMPLATE: 'Шаблон' + SEARCH_PAGE_TEMPLATE_HELP: 'Имя шаблона для страницы результатов поиска' + CATEGORY_FILTER: 'Фильтр категорий' + CATEGORY_FILTER_HELP: 'Разделенный запятыми список названий категорий. Введите "@none" для поиска на всех страницах' + FILTER_COMBINATOR: 'Фильтр комбинатор' + AND_COMBINATOR: 'И - Булева &&' + OR_COMBINATOR: 'Или - Булева ||' + +uk: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: 'Знайти …' + SEARCH_RESULTS: 'Результат пошуку' + SEARCH_RESULTS_SUMMARY_SINGULAR: 'На запит: %s знайдено 1 результат' + SEARCH_RESULTS_SUMMARY_PLURAL: 'На запит: %s результатів знайдено %s' + SEARCH_FIELD_MINIMUM_CHARACTERS: 'Введіть не менше %s символів' + SEARCH_CONTENT: 'Пошук контенту' + RENDERED_CONTENT: 'Оброблений контент (повільніше)' + RAW_CONTENT: 'Необроблений Markdown контент (швидше)' + BUILTIN_CSS: 'Використовувати вбудований CSS' + BUILTIN_CSS_HELP: 'Використовувати CSS, наданий плагіном simplesearch' + BUILTIN_JS: 'Використовувати вбудований JavaScript' + BUILTIN_JS_HELP: 'Використовувати JavaScript, наданий плагіном simplesearch' + DISPLAY_SEARCH_BUTTON: 'Показати кнопку пошуку' + DISPLAY_SEARCH_BUTTON_HELP: 'Показати кнопку пошуку поряд з полем пошуку' + IGNORE_ACCENDED_CHARACTERS: 'Ігнорувати акцентовані символи' + IGNORE_ACCENDED_CHARACTERS_HELP: 'Якщо цей параметр включений, пошукові терміни будуть збігатися з акцентованими символами, незалежно від їх діакритичних знаків, тобто результати пошуку будуть відображатися як "кафе" і "кафе́", незалежно від того, як ви їх набрали' + MIN_QUERY_LENGTH: 'Мінімальна довжина запиту' + MIN_QUERY_LENGTH_HELP: 'Мінімальна кількість символів, необхідна для відправлення запиту' + SEARCH_PAGE_ROUTE: 'Маршрут' + SEARCH_PAGE_ROUTE_HELP: 'Маршрут за замовчуванням для плагіна simplesearch' + SEARCH_PAGE_TEMPLATE: 'Шаблон' + SEARCH_PAGE_TEMPLATE_HELP: "Ім'я шаблону для сторінки результатів пошуку" + CATEGORY_FILTER: 'Фільтр категорій' + CATEGORY_FILTER_HELP: 'Розділений комами список назв категорій. Введіть "@none" для пошуку на всіх сторінках' + FILTER_COMBINATOR: 'Фільтр комбінатор' + AND_COMBINATOR: 'І - Булева &&' + OR_COMBINATOR: 'Або - Булева ||' + +es: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Buscar …" + SEARCH_RESULTS: "Resultados de la búsqueda" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Consulta: %s se encontró 1 resultado" + SEARCH_RESULTS_SUMMARY_PLURAL: "Consulta: %s se encontraron %s resultados" + +ja: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "検索する …" + SEARCH_RESULTS: "検索結果" + SEARCH_RESULTS_SUMMARY_SINGULAR: "検索 : %s に一つの結果があります。" + SEARCH_RESULTS_SUMMARY_PLURAL: "検索 : %s に %s の結果があります。" + +fa: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "جستجو …" + SEARCH_RESULTS: "نتایج جستجو" + SEARCH_RESULTS_SUMMARY_SINGULAR: "جستار: %s یک نتیجه یافت شد" + SEARCH_RESULTS_SUMMARY_PLURAL: "جستار: %s %s نتیجه یافت شد" + +cs: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Vyhledat …" + SEARCH_RESULTS: "Výsledky hledání" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Hledání výrazu '%s' našlo jeden výsledek" + SEARCH_RESULTS_SUMMARY_PLURAL: "Hledání výrazu '%s' našlo %s výsledků" + +pt: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "O que você procura?" + SEARCH_RESULTS: "Resultados da pesquisa" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Pesquisa: %s. Foram encontrados 1 resultados" + SEARCH_RESULTS_SUMMARY_PLURAL: "Pesquisa: %s. Foram encontrados %s resultados" + +sv: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Sök …" + SEARCH_RESULTS: "Sökresultat" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Sökning: %s hittade ett resultat" + SEARCH_RESULTS_SUMMARY_PLURAL: "Sökning: %s hittade %s resultat" + +da: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Søg …" + SEARCH_RESULTS: "Søgeresultat" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Søgning: %s fandt et resultat" + SEARCH_RESULTS_SUMMARY_PLURAL: "Søgning: %s fandt %s resultater" + +kk: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "іздеу …" + SEARCH_RESULTS: "Іздеу нәтижесі" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Сұраныс бойынша: %s табылды 1" + SEARCH_RESULTS_SUMMARY_PLURAL: "Сұраныс бойынша: %s табылды %s" + SEARCH_FIELD_MINIMUM_CHARACTERS: "Кемінде %s таңба қосу" + +pl: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Szukaj…" + SEARCH_RESULTS: "Wyniki wyszukiwania" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Znaleziono jeden wynik dla frazy %s." + SEARCH_RESULTS_SUMMARY_PLURAL: "Znaleziono %2$s wyników dla frazy %1$s." + SEARCH_FIELD_MINIMUM_CHARACTERS: "Fraza musi składać się z minimum %s znaków." + +gl: + PLUGIN_SIMPLESEARCH: + SEARCH_PLACEHOLDER: "Procurar …" + SEARCH_RESULTS: "Resultados da procura" + SEARCH_RESULTS_SUMMARY_SINGULAR: "Consulta: %s atopouse 1 resultado" + SEARCH_RESULTS_SUMMARY_PLURAL: "Consulta: %s atopáronse %s resultados" \ No newline at end of file diff --git a/user/plugins/simplesearch/pages/simplesearch.md b/user/plugins/simplesearch/pages/simplesearch.md new file mode 100644 index 0000000..2f97959 --- /dev/null +++ b/user/plugins/simplesearch/pages/simplesearch.md @@ -0,0 +1,7 @@ +--- +title: Search Results +order_by: date +order_dir: desc +template: simplesearch_results +--- + diff --git a/user/plugins/simplesearch/simplesearch.php b/user/plugins/simplesearch/simplesearch.php new file mode 100644 index 0000000..8239c32 --- /dev/null +++ b/user/plugins/simplesearch/simplesearch.php @@ -0,0 +1,450 @@ + ['onPluginsInitialized', 0], + 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0], + 'onGetPageTemplates' => ['onGetPageTemplates', 0], + ]; + } + + /** + * Add page template types. (for Admin plugin) + * + * @return void + */ + public function onGetPageTemplates(Event $event) + { + /** @var Types $types */ + $types = $event->types; + $types->scanTemplates('plugins://simplesearch/templates'); + } + + + /** + * Add current directory to twig lookup paths. + * + * @return void + */ + public function onTwigTemplatePaths() + { + $this->grav['twig']->twig_paths[] = __DIR__ . '/templates'; + } + + /** + * Enable search only if url matches to the configuration. + * + * @return void + */ + public function onPluginsInitialized() + { + if ($this->isAdmin()) { + return; + } + + $this->enable([ + 'onPagesInitialized' => ['onPagesInitialized', 0], + 'onTwigSiteVariables' => ['onTwigSiteVariables', 0] + ]); + } + + + /** + * Build search results. + * + * @return void + */ + public function onPagesInitialized() + { + $page = $this->grav['page']; + + $route = null; + if (isset($page->header()->simplesearch['route'])) { + $route = $page->header()->simplesearch['route']; + + // Support `route: '@self'` syntax + if ($route === '@self') { + $route = $page->route(); + $page->header()->simplesearch['route'] = $route; + } + } + + // If a page exists merge the configs + if (isset($page)) { + $this->config->set('plugins.simplesearch', $this->mergeConfig($page)); + } + + /** @var Uri $uri */ + $uri = $this->grav['uri']; + $query = $uri->param('query') ?: $uri->query('query'); + $route = $this->config->get('plugins.simplesearch.route'); + + // performance check for route + if (!($route && $route == $uri->path())) { + return; + } + + // set the template is not set in the page header (the page header setting takes precedence over the plugin config setting) + if (!isset($page->header()->template)) { + $template_override = $this->config->get('plugins.simplesearch.template', 'simplesearch_results'); + $page->template($template_override); + } + + // Explode query into multiple strings. Drop empty values + // @phpstan-ignore-next-line + $this->query = array_filter(array_filter(explode(',', $query), 'trim'), 'strlen'); + + /** @var Taxonomy $taxonomy_map */ + $taxonomy_map = $this->grav['taxonomy']; + $taxonomies = []; + $find_taxonomy = []; + + $filters = (array)$this->config->get('plugins.simplesearch.filters'); + $operator = $this->config->get('plugins.simplesearch.filter_combinator', 'and'); + $new_approach = false; + + // if @none found, skip processing taxonomies + $should_process = true; + if (is_array($filters)) { + $the_filter = reset($filters); + + if (is_array($the_filter)) { + if (in_array(reset($the_filter), ['@none', 'none@'])) { + $should_process = false; + } + } + } + + if (!$should_process || !$filters || $query === false || (count($filters) === 1 && !reset($filters))) { + /** @var Pages $pages */ + $pages = $this->grav['pages']; + $this->collection = $pages->all(); + } else { + + foreach ($filters as $key => $filter) { + // flatten item if it's wrapped in an array + if (is_int($key)) { + if (is_array($filter)) { + $key = key($filter); + $filter = $filter[$key]; + } else { + $key = $filter; + } + } + + // see if the filter uses the new 'items-type' syntax + if ($key === '@self' || $key === 'self@') { + $new_approach = true; + } elseif ($key === '@taxonomy' || $key === 'taxonomy@') { + $taxonomies = $filter === false ? false : array_merge($taxonomies, (array)$filter); + } else { + $find_taxonomy[$key] = $filter; + } + } + + if ($new_approach) { + $params = $page->header()->content; + $params['query'] = $this->config->get('plugins.simplesearch.query'); + $this->collection = $page->collection($params, false); + } else { + $this->collection = new Collection(); + $this->collection->append($taxonomy_map->findTaxonomy($find_taxonomy, $operator)->toArray()); + } + } + + //Drop unpublished pages, but do not drop unroutable pages right now to be able to search modular pages which are unroutable per se + $this->collection->published(); + /** @var Collection $modularPageCollection */ + $modularPageCollection = $this->collection->copy(); + //Get published modular pages + $modularPageCollection->modular(); + foreach ($modularPageCollection as $cpage) { + $parent = $cpage->parent(); + if (!$parent || !$parent->published()) { + $modularPageCollection->remove($cpage); + } + } + //Drop unroutable pages + $this->collection->routable(); + //Add modular pages again + $this->collection->merge($modularPageCollection); + + //Check if user has permission to view page + if ($this->grav['config']->get('plugins.login.enabled')) { + $this->collection = $this->checkForPermissions($this->collection); + } + $extras = []; + + if ($query) { + foreach ($this->collection as $cpage) { + + $header = $cpage->header(); + if (isset($header->simplesearch['process']) && $header->simplesearch['process'] === false) { + $this->collection->remove($cpage); + continue; + } + + foreach ($this->query as $query) { + $query = trim($query); + + if ($this->notFound($query, $cpage, $taxonomies)) { + $this->collection->remove($cpage); + continue; + } + + if ($cpage->modular()) { + $this->collection->remove($cpage); + $parent = $cpage->parent(); + $extras[$parent->path()] = ['slug' => $parent->slug()]; + } + + } + } + } + + if (!empty($extras)) { + $this->collection->append($extras); + } + + // use a configured sorting order if not already done + if (!$new_approach) { + $this->collection = $this->collection->order( + $this->config->get('plugins.simplesearch.order.by'), + $this->config->get('plugins.simplesearch.order.dir') + ); + } + + // Display simplesearch page if no page was found for the current route + $pages = $this->grav['pages']; + $page = $pages->dispatch($this->config->get('plugins.simplesearch.route', '/search'), true); + if (!isset($page)) { + // create the search page + $page = new Page; + $page->init(new \SplFileInfo(__DIR__ . '/pages/simplesearch.md')); + + // override the template is set in the plugin config (the plugin config setting takes precedence over the page header setting) + $template_override = $this->config->get('plugins.simplesearch.template'); + if (isset($template_override)) { + $page->template($template_override); + } + + // fix RuntimeException: Cannot override frozen service "page" issue + unset($this->grav['page']); + + $this->grav['page'] = $page; + } + } + + /** + * Filter the pages, and return only the pages the user has access to. + * Implementation based on Login Plugin authorizePage() function. + * + * @param Collection $collection + * @return Collection + */ + public function checkForPermissions($collection) + { + $user = $this->grav['user']; + $returnCollection = new Collection(); + foreach ($collection as $page) { + + $header = $page->header(); + $rules = isset($header->access) ? (array)$header->access : []; + + if ($this->config->get('plugins.login.parent_acl')) { + // If page has no ACL rules, use its parent's rules + if (!$rules) { + $parent = $page->parent(); + while (!$rules and $parent) { + $header = $parent->header(); + $rules = isset($header->access) ? (array)$header->access : []; + $parent = $parent->parent(); + } + } + } + + // Continue to the page if it has no ACL rules. + if (!$rules) { + $returnCollection[$page->path()] = ['slug' => $page->slug()]; + } else { + // Continue to the page if user is authorized to access the page. + foreach ($rules as $rule => $value) { + if (is_array($value)) { + foreach ($value as $nested_rule => $nested_value) { + if ($user->authorize($rule . '.' . $nested_rule) == $nested_value) { + $returnCollection[$page->path()] = ['slug' => $page->slug()]; + break; + } + } + } else { + if ($user->authorize($rule) == $value) { + $returnCollection[$page->path()] = ['slug' => $page->slug()]; + break; + } + } + } + } + } + return $returnCollection; + } + + /** + * @param string $query + * @param Page $page + * @param array|false $taxonomies + * @return bool + */ + private function notFound($query, $page, $taxonomies) + { + $searchable_types = $search_content = $this->config->get('plugins.simplesearch.searchable_types'); + $results = true; + $search_content = $this->config->get('plugins.simplesearch.search_content'); + + $result = null; + foreach ($searchable_types as $type => $enabled) { + if ($type === 'title' && $enabled) { + $result = $this->matchText(strip_tags($page->title()), $query) === false; + } elseif ($type === 'taxonomy' && $enabled) { + if ($taxonomies === false) { + continue; + } + $page_taxonomies = $page->taxonomy(); + $taxonomy_match = false; + foreach ((array)$page_taxonomies as $taxonomy => $values) { + // if taxonomies filter set, make sure taxonomy filter is valid + if (!is_array($values) || (is_array($taxonomies) && !empty($taxonomies) && !in_array($taxonomy, $taxonomies))) { + continue; + } + + $taxonomy_values = implode('|', $values); + if ($this->matchText($taxonomy_values, $query) !== false) { + $taxonomy_match = true; + break; + } + } + $result = !$taxonomy_match; + } elseif ($type === 'content' && $enabled) { + if ($search_content === 'raw') { + $content = $page->rawMarkdown(); + } else { + $content = $page->content(); + } + $result = $this->matchText(strip_tags($content), $query) === false; + } elseif ($type === 'header' && $enabled) { + $header = (array) $page->header(); + $content = $this->getArrayValues($header); + $result = $this->matchText(strip_tags($content), $query) === false; + } + $results = (bool)$result; + if ($results === false) { + break; + } + } + return $results; + } + + /** + * @param string $haystack + * @param string $needle + * @return false|int + */ + private function matchText($haystack, $needle) + { + if ($this->config->get('plugins.simplesearch.ignore_accented_characters')) { + setlocale(LC_ALL, 'en_US'); + try { + $result = mb_stripos(iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $haystack), iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $needle)); + } catch (\Exception $e) { + $result = mb_stripos($haystack, $needle); + } + setlocale(LC_ALL, ''); + return $result; + } + + return mb_stripos($haystack, $needle); + } + + /** + * Set needed variables to display the search results. + * + * @return void + */ + public function onTwigSiteVariables() + { + $twig = $this->grav['twig']; + + if ($this->query) { + $twig->twig_vars['query'] = implode(', ', $this->query); + $twig->twig_vars['search_results'] = $this->collection; + } + + if ($this->config->get('plugins.simplesearch.built_in_css')) { + $this->grav['assets']->add('plugin://simplesearch/css/simplesearch.css'); + } + + if ($this->config->get('plugins.simplesearch.built_in_js')) { + $this->grav['assets']->addJs('plugin://simplesearch/js/simplesearch.js', ['group' => 'bottom']); + } + } + + /** + * @param array $array + * @param array|null $ignore_keys + * @param int $level + * @return string + */ + protected function getArrayValues($array, $ignore_keys = null, $level = 0) { + $output = ''; + + if (is_null($ignore_keys)) { + $config = $this->config(); + $ignore_keys = $config['header_keys_ignored'] ?? ['title', 'taxonomy','content', 'form', 'forms', 'media_order']; + } + foreach ($array as $key => $child) { + + if ($level === 0 && in_array($key, $ignore_keys, true)) { + continue; + } + + if (is_array($child)) { + $output .= " " . $this->getArrayValues($child, $ignore_keys, $level + 1); + } else { + $output .= " " . $child; + } + + } + return trim($output); + } +} diff --git a/user/plugins/simplesearch/simplesearch.yaml b/user/plugins/simplesearch/simplesearch.yaml new file mode 100644 index 0000000..3a234ce --- /dev/null +++ b/user/plugins/simplesearch/simplesearch.yaml @@ -0,0 +1,21 @@ +enabled: true +built_in_css: true +built_in_js: true +display_button: false +min_query_length: 3 +route: /search +search_content: rendered +template: simplesearch_results +filters: + category: +filter_combinator: and +ignore_accented_characters: false +order: + by: date + dir: desc +searchable_types: + title: true + content: true + taxonomy: true + header: false +header_keys_ignored: [ 'title', 'taxonomy','content', 'form', 'forms', 'media_order' ] \ No newline at end of file diff --git a/user/plugins/simplesearch/templates/partials/simplesearch_base.html.twig b/user/plugins/simplesearch/templates/partials/simplesearch_base.html.twig new file mode 100644 index 0000000..2ee42a2 --- /dev/null +++ b/user/plugins/simplesearch/templates/partials/simplesearch_base.html.twig @@ -0,0 +1 @@ +{% extends 'partials/base.html.twig' %} diff --git a/user/plugins/simplesearch/templates/partials/simplesearch_item.html.twig b/user/plugins/simplesearch/templates/partials/simplesearch_item.html.twig new file mode 100644 index 0000000..feab4a6 --- /dev/null +++ b/user/plugins/simplesearch/templates/partials/simplesearch_item.html.twig @@ -0,0 +1,23 @@ +
+ + {% set banner = page.media.images|first %} + + {% if banner %} + + {% endif %} +
+ + +
+ {{ page.date|date(config.system.pages.dateformat.short) }} +
+ +

{{ page.summary|raw }}

+ +
+
+
diff --git a/user/plugins/simplesearch/templates/partials/simplesearch_searchbox.html.twig b/user/plugins/simplesearch/templates/partials/simplesearch_searchbox.html.twig new file mode 100644 index 0000000..6613d58 --- /dev/null +++ b/user/plugins/simplesearch/templates/partials/simplesearch_searchbox.html.twig @@ -0,0 +1,23 @@ +{% set min_chars = config.get('plugins.simplesearch.min_query_length', 3) %} +
+
+ 0 %} data-min="{{- min_chars -}}" {% endif %} + required + placeholder="{{"PLUGIN_SIMPLESEARCH.SEARCH_PLACEHOLDER"|t}}" + value="{{ query|e }}" + data-search-invalid="{{ "PLUGIN_SIMPLESEARCH.SEARCH_FIELD_MINIMUM_CHARACTERS"|t(min_chars)|raw }}" + data-search-separator="{{ config.system.param_sep }}" + data-search-input="{{ base_url }}{{ config.plugins.simplesearch.route == '@self' ? '' : (config.plugins.simplesearch.route == '/' ? '' : config.plugins.simplesearch.route) }}/query" + /> + {% if config.plugins.simplesearch.display_button %} + + {% endif %} +
+
diff --git a/user/plugins/simplesearch/templates/simplesearch_results.html.twig b/user/plugins/simplesearch/templates/simplesearch_results.html.twig new file mode 100644 index 0000000..539a1d7 --- /dev/null +++ b/user/plugins/simplesearch/templates/simplesearch_results.html.twig @@ -0,0 +1,23 @@ +{% extends 'partials/simplesearch_base.html.twig' %} + +{% block content %} +
+

{{ "PLUGIN_SIMPLESEARCH.SEARCH_RESULTS"|t }}

+
+ {% include 'partials/simplesearch_searchbox.html.twig' %} +
+

+ {% if query %} + {% set count = search_results ? search_results.count : 0 %} + {% if count is same as( 1 ) %} + {{ "PLUGIN_SIMPLESEARCH.SEARCH_RESULTS_SUMMARY_SINGULAR"|t(query|e)|raw }} + {% else %} + {{ "PLUGIN_SIMPLESEARCH.SEARCH_RESULTS_SUMMARY_PLURAL"|t(query|e, count)|raw }} + {% endif %} + {% endif %} +

+ {% for page in search_results %} + {% include 'partials/simplesearch_item.html.twig' with {'page': page} %} + {% endfor %} +
+{% endblock %} diff --git a/user/plugins/simplesearch/templates/simplesearch_results.json.twig b/user/plugins/simplesearch/templates/simplesearch_results.json.twig new file mode 100644 index 0000000..d62f502 --- /dev/null +++ b/user/plugins/simplesearch/templates/simplesearch_results.json.twig @@ -0,0 +1,5 @@ +{"results":[ +{%- for search_result in search_results -%} +{{- search_result.route|json_encode -}}{{ not loop.last ? ',' }} +{%- endfor -%} +]} diff --git a/user/plugins/simplesearch/vendor/autoload.php b/user/plugins/simplesearch/vendor/autoload.php new file mode 100644 index 0000000..3731089 --- /dev/null +++ b/user/plugins/simplesearch/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see http://www.php-fig.org/psr/psr-0/ + * @see http://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/user/plugins/simplesearch/vendor/composer/LICENSE b/user/plugins/simplesearch/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/user/plugins/simplesearch/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/user/plugins/simplesearch/vendor/composer/autoload_classmap.php b/user/plugins/simplesearch/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..d4a32c1 --- /dev/null +++ b/user/plugins/simplesearch/vendor/composer/autoload_classmap.php @@ -0,0 +1,10 @@ + $baseDir . '/simplesearch.php', +); diff --git a/user/plugins/simplesearch/vendor/composer/autoload_namespaces.php b/user/plugins/simplesearch/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..b7fc012 --- /dev/null +++ b/user/plugins/simplesearch/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitca0f44aa14891d4b4dfc9b834c0e0763::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + } + + $loader->register(true); + + return $loader; + } +} diff --git a/user/plugins/simplesearch/vendor/composer/autoload_static.php b/user/plugins/simplesearch/vendor/composer/autoload_static.php new file mode 100644 index 0000000..ee1d84e --- /dev/null +++ b/user/plugins/simplesearch/vendor/composer/autoload_static.php @@ -0,0 +1,20 @@ + __DIR__ . '/../..' . '/simplesearch.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitca0f44aa14891d4b4dfc9b834c0e0763::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/user/plugins/simplesearch/vendor/composer/installed.json b/user/plugins/simplesearch/vendor/composer/installed.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/user/plugins/simplesearch/vendor/composer/installed.json @@ -0,0 +1 @@ +[]