| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 | <?phpnamespace Grav\Plugin;use Grav\Common\Page\Collection;use Grav\Common\Plugin;use Grav\Common\Uri;use Grav\Common\Page\Page;use Grav\Common\Page\Types;use Grav\Common\Taxonomy;use Grav\Common\Utils;use Grav\Common\Data\Data;use Grav\Common\Config\Config;use RocketTheme\Toolbox\Event\Event;class SimplesearchPlugin extends Plugin{    /**     * @var array     */    protected $query;    /**     * @var string     */    protected $query_id;    /**     * @var Collection     */    protected $collection;    /**     * @return array     */    public static function getSubscribedEvents()    {        return [            'onPluginsInitialized' => ['onPluginsInitialized', 0],            'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],            'onGetPageTemplates' => ['onGetPageTemplates', 0],        ];    }    /**     * Add page template types. (for Admin plugin)     */    public function onGetPageTemplates(Event $event)    {        /** @var Types $types */        $types = $event->types;        $types->scanTemplates('plugins://simplesearch/templates');    }    /**     * Add current directory to twig lookup paths.     */    public function onTwigTemplatePaths()    {        $this->grav['twig']->twig_paths[] = __DIR__ . '/templates';    }    /**     * Enable search only if url matches to the configuration.     */    public function onPluginsInitialized()    {        if ($this->isAdmin()) {            return;        }        $this->enable([            'onPagesInitialized' => ['onPagesInitialized', 0],            'onTwigSiteVariables' => ['onTwigSiteVariables', 0]        ]);    }    /**     * Build search results.     */    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 ($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;        }        // Explode query into multiple strings. Drop empty values        $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 \Grav\Common\Page\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 and unroutable pages        $this->collection->published()->routable();        //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) {                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')            );        }        // if page doesn't have settings set, create a page        if (!isset($page->header()->simplesearch)) {            // create the search page            $page = new Page;            $page->init(new \SplFileInfo(__DIR__ . '/pages/simplesearch.md'));            // override the template is set in the config            $template_override = $this->config->get('plugins.simplesearch.template');            if ($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.     */    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;    }    /**     * Set needed variables to display the search results.     */    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');        }        $this->grav['assets']->addJs('plugin://simplesearch/js/simplesearch.js', [ 'group' => 'bottom' ]);    }    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;        } else {            return mb_stripos($haystack, $needle);        }    }    /**     * @param $query     * @param Page $page     * @param $taxonomies     * @return bool     */    private function notFound($query, $page, $taxonomies)    {        $searchable_types = ['title', 'content', 'taxonomy'];        $results = true;        $search_content = $this->config->get('plugins.simplesearch.search_content');        foreach ($searchable_types as $type) {            if ($type === 'title') {                $result = $this->matchText(strip_tags($page->title()), $query) === false;            } elseif ($type === 'taxonomy') {                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($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;            } else {                if ($search_content == 'raw') {                    $content = $page->rawMarkdown();                } else {                    $content = $page->content();                }                $result = $this->matchText(strip_tags($content), $query) === false;            }            $results = $results && $result;            if ($results === false ) {                break;            }        }        return $results;    }}
 |