# SiteSearch Plugin for OctoberCMS This plugin adds global search capabilities to OctoberCMS. ## Available languages * English * German * Czech * Russian * Persian (Farsi) * Portuguese You can translate all contents into your own language. ## Currently supported content types * [RainLab.Pages](https://octobercms.com/plugin/rainlab-pages) * [RainLab.Blog](https://octobercms.com/plugin/rainlab-blog) * [Indikator.News](https://github.com/gergo85/oc-news) * [Feegleweb.Octoshop](https://octobercms.com/plugin/feegleweb-octoshop) * [Jiri.JKShop](http://octobercms.com/plugin/jiri-jkshop) * [RadiantWeb.ProBlog](https://octobercms.com/plugin/radiantweb-problog) * [Arrizalamin.Portfolio](https://octobercms.com/plugin/arrizalamin-portfolio) * [Responsiv.Showcase](https://octobercms.com/plugin/responsiv-showcase) * [VojtaSvoboda.Brands](https://octobercms.com/plugin/vojtasvoboda-brands) * [Graker.PhotoAlbums](https://octobercms.com/plugin/graker-photoalbums) * Native CMS pages (experimental) **Multilingual contents via RainLab.Translate are supported.** Support for more plugins is added upon request. **You can easily extend this plugin to search your custom plugin's contents as well. See the documentation for further information.** ### Get native support for your plugin If you are a plugin developer and wish to have native support for your contents in SiteSearch please submit a pull request for your search provider or send us a copy of you plugin so we can create the provider for you. We cannot add support for every plugin but will add any plugin that has a notable project count on the October Marketplace. ## Components ### searchResults Place this component on your page to display search results. #### Usage example Create a search form that sends a query to your search page: ##### Search form ```html
``` **Important**: Use the `q` parameter to send the user's query. Alternatively you can also use the `searchInput` component described below to generate this form for you. ##### Search results Create a page to display your search results. Add the `searchResults` component to it. Use the `searchResults.query` parameter to display the user's search query. ```html title = "Search results" url = "/search" layout = "default" [searchResults] resultsPerPage = 10 showProviderBadge = 1 noResultsMessage = "Your search did not return any results." visitPageMessage = "Visit page" ==

Search results for {{ searchResults.query }}

{% component 'searchResults' %} ``` ##### Example css to style the component ```css .ss-result { margin-bottom: 2em; } .ss-result__aside { float: right; margin-left: .5em; } .ss-result__title { font-weight: bold; margin-bottom: .5em; } .ss-result__badge { font-size: .7em; padding: .2em .5em; border-radius: 4px; margin-left: .75em; background: #eee; display: inline-block; } .ss-result__text { margin-bottom: .5em; } .ss-result__url { } ``` #### Modify the query before searching If you want to modify the user's search query before the search is executed you can call the `forceQuery` method on the `searchResults` component from your page's `onStart` method. ```php [searchResults] resultsPerPage = 10 showProviderBadge = 1 noResultsMessage = "Your search returned no results." visitPageMessage = "Visit page" == function onStart() { $query = Request::get('q'); $query = str_replace('ё', 'e', $query); $this->page->components['searchResults']->forceQuery($query); } == {% component 'searchResults' %} ``` #### Change the results collection before displaying You can listen for the `offline.sitesearch.results` event and modify the query as you wish. This is useful to remove certain results or change the sort order. ```php [searchResults] resultsPerPage = 10 showProviderBadge = 1 noResultsMessage = "Your search returned no results." visitPageMessage = "Visit page" == function onInit() { \Event::listen('offline.sitesearch.results', function ($results) { // return $results->filter(...); return $results->sortByDesc('model.custom_attribute'); }); } == {% component 'searchResults' %} ``` #### Properties The following properties are available to change the component's behaviour. ##### resultsPerPage How many results to display on one page. ##### showProviderBadge The search works by querying multiple providers (Pages, Blog, or other). If this option is enabled each search result is marked with a badge to show which provider returned the result. This is useful if your site has many different entities (ex. teams, employees, pages, blog entries). ##### noResultsMessage This message is shown if there are no results returned. ##### visitPageMessage A link is placed below each search result. Use this property to change that link's text. ### searchInput Place this component anywhere you want to display a simple search input with "search as you type" capabilities. #### Usage example Add the `searchInput` component to any layout, partial or page. ```html title = "Home" url = "/" ... [searchInput] useAutoComplete = 1 autoCompleteResultCount = 5 showProviderBadge = 1 searchPage = "search.htm" == {% component 'searchInput' %} ``` ##### Example css to style the component ```css .ss-search-form { position: relative; } .ss-search-form__results { display: none; position: absolute; left: 0; top: 35px; width: 100%; background: #fff; padding: 1em; box-shadow: 0 2px 4px rgba(0, 0, 0, .1); } .ss-search-form__results--visible { display: block; } ``` #### Properties The following properties are available to change the component's behaviour. ##### useAutoComplete If this property is enabled, a search query will be executed as soon as the user begins to type. ##### autoCompleteResultCount This many results will be displayed to the user below the input field. There will be a "Show all results" link the user can click that takes her to a full search results page if one has been specified via the `searchPage` property. ##### showProviderBadge The search works by querying multiple providers (Pages, Blog, or other). If this option is enabled each search result is marked with a badge to show which provider returned the result. This is useful if your site has many different entities (ex. teams, employees, pages, blog entries). ##### searchPage The filename of the page where you have placed a `searchResults` component. If a user clicks on the "Show all results" link it will take him to this page where a full search is run using the `searchResults` component. ## Add support for custom plugin contents ### Simple method To return search results for you own custom plugin, register an event listener for the `offline.sitesearch.query` event in your plugin's boot method. Return an array containing a `provider` string and `results` array. Each result must provide at least a `title` key. #### Example to search for custom `documents` ```php public function boot() { \Event::listen('offline.sitesearch.query', function ($query) { // The controller is used to generate page URLs. $controller = \Cms\Classes\Controller::getController() ?? new \Cms\Classes\Controller(); // Search your plugin's contents $items = YourCustomDocumentModel ::where('title', 'like', "%${query}%") ->orWhere('content', 'like', "%${query}%") ->get(); // Now build a results array $results = $items->map(function ($item) use ($query, $controller) { // If the query is found in the title, set a relevance of 2 $relevance = mb_stripos($item->title, $query) !== false ? 2 : 1; // Optional: Add an age penalty to older results. This makes sure that // newer results are listed first. // if ($relevance > 1 && $item->created_at) { // $ageInDays = $item->created_at->diffInDays(\Illuminate\Support\Carbon::now()); // $relevance -= \OFFLINE\SiteSearch\Classes\Providers\ResultsProvider::agePenaltyForDays($ageInDays); // } return [ 'title' => $item->title, 'text' => $item->content, 'url' => $controller->pageUrl('cms-page-file-name', ['slug' => $item->slug]), 'thumb' => optional($item->images)->first(), // Instance of System\Models\File 'relevance' => $relevance, // higher relevance results in a higher // position in the results listing // 'meta' => 'data', // optional, any other information you want // to associate with this result // 'model' => $item, // optional, pass along the original model ]; }); return [ 'provider' => 'Document', // The badge to display for this result 'results' => $results, ]; }); } ``` That's it! ### Advanced method If you need a bit more flexibility you can also create your own `ResultsProvider` class. Simply extend SiteSearch's `ResultProvider` and implement the needed methods. Have a look at the existing providers shipped by this plugin to get an idea of all the possibilities. When your own `ResultsProvider` class is ready, register an event listener for the `offline.sitesearch.extend` event in your plugin's boot method. There you can return one `ResultsProvider` (or multiple in an array) which will be included every time a user runs a search on your website. #### Advanced example to search for custom `documents` ```php public function boot() { Event::listen('offline.sitesearch.extend', function () { return new DocumentsSearchProvider(); // or // return [new DocumentsSearchProvider(), new FilesSearchProvider()]; }); } ``` ```php query}%") ->orWhere('content', 'like', "%{$this->query}%") ->get(); // Create a new Result for every match foreach ($matching as $match) { $result = $this->newResult(); $result->relevance = 1; $result->title = $match->title; $result->text = $match->description; $result->url = $match->url; $result->thumb = $match->image; $result->model = $match; $result->meta = [ 'some_data' => $match->some_other_property, ]; // Add the results to the results collection $this->addResult($result); } return $this; } public function displayName() { return 'My Result'; } public function identifier() { return 'VendorName.PluginName'; } } ``` ## Settings You can manage all of this plugin's settings in the October CMS backend. ### Rainlab.Pages No special configuration is required. ### Rainlab.Blog Make sure you select your CMS page with the `blogPost` component as the `blog post page` in the backend settings. You can access a post's published_at date in your search results via `{{ result.meta }}`. ### Feegleweb.Octoshop Make sure you set the `Url of product detail page` setting to point to the right url. Only specify the fixed part of the URL: `/product`. If your products are located under `/product/:slug` the default value is okay. ### Jiri.JKShop Make sure you set the `Url of product detail page` setting to point to the right url. Only specify the fixed part of the URL: `/product`. If your products are located under `/product/:slug` the default value is okay. You can access an article's price in your search results via `{{ result.meta }}`. ### Indikator.News Make sure you set the `News post page` setting to point to the right url. Only specify the fixed part of the URL: `/news/post`. If your products are located under `/news/post/:slug` the default value is okay. ### RadiantWeb.ProBlog Make sure you set the `Url of blog post page` setting to point to the right url. Only specify the fixed part of the URL: `/blog`. If your posts are located under `/blog/:category/:slug` the default value is okay. ### ArrizalAmin.Portfolio Make sure you set the `Url of portfolio detail page` setting to point to the right url. Only specify the fixed part of the URL: `/portfolio/project`. If your detail page is located under `/portfolio/project/:slug` the default value is okay. ### VojtaSvoboda.Brands Make sure you set the `Url of brand detail page` setting to point to the right URL. Only specify the fixed part of the URL: `/brand`. If your brand detail page is located under `/brand/:slug` then insert only `/brand` without the slug parameter. ### CMS pages (experimental) If you want to provide search results for CMS pages change the `enabled` setting to `On`. You have to specifically add the component `siteSearchInclude` to every CMS page you want to be searched. Pages **without** this component will **not** be searched. This feature works best with simple pages that include components, but don't rely on url parametres or other variables (like a page number). CMS pages with dynamic URLs (like `/page/:slug`) won't be linked correctly from the search results listing. If you have CMS pages with more complex dynamic contents consider writing your own search provider (see `Add support for custom plugin contents`) ## Overwrite default markup To overwrite the default markup copy all files from `plugins/offline/sitesearch/components/searchresults` to `themes/your-theme/partials/searchResults` and modify them as needed. If you gave an alias to the `searchResults` component make sure to put the markup in the appropriate partials directory `themes/your-theme/partials/your-given-alias`.