Fork me on GitHub

Simple-Jekyll-Search

Build Status

A JavaScript library to add search functionality to any Jekyll blog.


idea from this blog post


Promotion: check out Pomodoro.cc

Demo

Getting started

  • Place the following code in a file called search.json in the root of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:
---
---
[
  
    {
      "title"    : "WordPress Rewrites",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/redirect/",
      "date"     : "2019-04-08 15:40:00 +0000"
    } ,
  
    {
      "title"    : "WordPress Network",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wordpress-network/",
      "date"     : "2019-03-20 10:50:00 +0000"
    } ,
  
    {
      "title"    : "WordPress Cron",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wordpress-cron/",
      "date"     : "2019-03-15 13:18:00 +0000"
    } ,
  
    {
      "title"    : "Server-side PDF printing",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/pdf-generation/",
      "date"     : "2019-02-11 15:11:00 +0000"
    } ,
  
    {
      "title"    : "What's new?",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/release-notes/",
      "date"     : "2018-11-12 11:26:00 +0000"
    } ,
  
    {
      "title"    : "Environment variables",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/environment-variables/",
      "date"     : "2018-11-11 21:52:00 +0000"
    } ,
  
    {
      "title"    : "New generation testing system",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/ng-integration-tests/",
      "date"     : "2018-09-21 11:36:22 +0000"
    } ,
  
    {
      "title"    : "Profiling with Tideways",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/tideways/",
      "date"     : "2018-03-14 21:31:00 +0000"
    } ,
  
    {
      "title"    : "Remote testing",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/remote-testing/",
      "date"     : "2018-01-10 23:15:00 +0000"
    } ,
  
    {
      "title"    : "Moving data between instances",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/moving-data/",
      "date"     : "2017-03-26 19:45:00 +0000"
    } ,
  
    {
      "title"    : "Restricting access",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/restricting-access/",
      "date"     : "2017-03-26 19:45:00 +0000"
    } ,
  
    {
      "title"    : "Custom error pages",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/error-pages/",
      "date"     : "2016-08-24 23:11:00 +0000"
    } ,
  
    {
      "title"    : "PHP7 and HHVM",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/php7-hhvm/",
      "date"     : "2016-06-21 19:24:02 +0000"
    } ,
  
    {
      "title"    : "Nginx Web Server",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/nginx/",
      "date"     : "2016-05-17 10:24:02 +0000"
    } ,
  
    {
      "title"    : "Staging instances (Shadows)",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/shadows/",
      "date"     : "2016-05-11 07:37:00 +0000"
    } ,
  
    {
      "title"    : "Updating Vagrant Box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/updating-vagrant-box/",
      "date"     : "2015-10-23 10:53:29 +0000"
    } ,
  
    {
      "title"    : "Seravo plugin - A must-use plugin",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wppalvelu-plugin/",
      "date"     : "2015-10-15 17:51:38 +0000"
    } ,
  
    {
      "title"    : "Default values",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/defaults/",
      "date"     : "2015-10-13 15:11:50 +0000"
    } ,
  
    {
      "title"    : "Typical workflow",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/local-development/",
      "date"     : "2015-10-13 14:26:53 +0000"
    } ,
  
    {
      "title"    : "How to install Vagrant",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/how-to-install/",
      "date"     : "2015-10-13 14:26:53 +0000"
    } ,
  
    {
      "title"    : "List of commands",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/available-commands/",
      "date"     : "2015-10-13 14:25:15 +0000"
    } ,
  
    {
      "title"    : "Configure Vagrant Box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/configure-vagrant-box/",
      "date"     : "2015-10-12 19:12:17 +0000"
    } ,
  
    {
      "title"    : "Deploy using Git",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/deploy-using-git/",
      "date"     : "2015-10-12 18:05:02 +0000"
    } ,
  
    {
      "title"    : "Data locations",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/data-locations/",
      "date"     : "2015-10-12 11:29:39 +0000"
    } ,
  
    {
      "title"    : "Using Git hooks",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/using-git-hooks/",
      "date"     : "2015-10-12 11:24:27 +0000"
    } ,
  
    {
      "title"    : "Composer - Plugins & Themes",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/composer/",
      "date"     : "2015-10-11 03:46:06 +0000"
    } ,
  
    {
      "title"    : "Manage with wp-cli",
      "category" : "management",
      "tags"     : "",
      "url"      : "/docs/management/use-wordpress-with-wpcli/",
      "date"     : "2015-10-11 03:39:48 +0000"
    } ,
  
    {
      "title"    : "Vagrant box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/vagrant-box/",
      "date"     : "2015-10-11 03:32:14 +0000"
    } ,
  
    {
      "title"    : "Compile assets & automate with Gulp",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/gulp/",
      "date"     : "2015-10-11 03:30:52 +0000"
    } ,
  
    {
      "title"    : "Debug mails with MailCatcher",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/mailcatcher/",
      "date"     : "2015-10-11 03:30:42 +0000"
    } ,
  
    {
      "title"    : "Profile runtime with Xdebug & Webgrind",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/xdebug/",
      "date"     : "2015-10-11 03:30:24 +0000"
    } ,
  
    {
      "title"    : "Integration tests using Rspec",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/integration-tests/",
      "date"     : "2015-10-11 03:28:22 +0000"
    } ,
  
    {
      "title"    : "Configure SSH",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/configure-ssh/",
      "date"     : "2015-10-10 22:20:23 +0000"
    } 
  
]
  • configure the library ( options )

Note that the index generated in search.json does not include the posts’ content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts’ content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add

"content"  : ""

to search.json after the "date" line to which you must add a comma (,).

Install with bower

bower install simple-jekyll-search

Setup

You need to place the following code within the layout where you want the search to appear.

For example in _layouts/default.html:

<!-- Html Elements for Search -->
<div id="search-container">
<input type="text" id="search-input" placeholder="search...">
<ul id="results-container"></ul>
</div>

<!-- Script pointing to jekyll-search.js -->
<script src="/docs/bower_components/simple-jekyll-search/dest/jekyll-search.js" type="text/javascript"></script>

Options

Customize SimpleJekyllSearch by passing in your configuration options:

SimpleJekyllSearch({
  searchInput: document.getElementById('search-input'),
  resultsContainer: document.getElementById('results-container'),
  json: '/search.json',
})

The above initialization needs to occur after the inclusion of jekyll-search.js.

searchInput (Element) [required]

The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.

resultsContainer (Element) [required]

The container element in which the search results should be rendered in. Typically an <ul>.

json (String|JSON) [required]

You can either pass in an URL to the search.json file, or the results in form of JSON directly, to save one round trip to get the data.

searchResultTemplate

The template of a single rendered search result.

The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.

E.g.

The template

<li><a href="{url}">{title}</a></li>

will render to the following

<li><a href="/jekyll/update/2014/11/01/welcome-to-jekyll.html">Welcome to Jekyll!</a></li>

If the search.json contains this data

[

    {
      "title"    : "Welcome to Jekyll!",
      "category" : "",
      "tags"     : "",
      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",
      "date"     : "2014-11-01 21:07:22 +0100"
    }

]

noResultsText

The HTML that will be shown if the query didn’t match anything.

limit

You can limit the number of posts rendered on the page.

fuzzy

Enable fuzzy search to allow less restrictive matching.

exclude

Pass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).

Enable full content search of posts and pages

  • Replace ‘search.json’ with the following code:
---
layout: null
---
[
  
    {
      "title"    : "WordPress Rewrites",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/redirect/",
      "date"     : "2019-04-08 15:40:00 +0000",
      "content"  : "  Note! In most cases redirects are better done in WordPress/PHP e.g. the Redirection plugin, using the https://codex.wordpress.org/Rewrite_API or with custom code in e.g. theme functions.php. Use Nginx redirects only if there is no WordPress way to do it, for example when diverting traffic from the index.php to another custom PHP script on the same site.Basic redirects in WordPress/PHPIn PHP code you can express whatever redirection logic you want without being confined to the limited features on Nginx. For example there could be a file mu-plugins/redirects.php that would include:&lt;?php// Redirect any requests for www.esimerkki.fi or esimerkki.fi to example.com/fi/if ( isset($_SERVER['HTTP_HOST']) &amp;&amp; strpos($_SERVER['HTTP_HOST'], 'esimerkki.fi') !== false ) {  header("Location: https://example.com/fi/", true, 301);  die();}Another more elaborate one example would be:switch ($_SERVER['HTTP_HOST']) {  # Enforce no www  # Use 301 to make redirect permanent and cached  # Use 302 for temporary (non-cached) redirects  case "www.example.com":    header("Location: https://example.com/", true, 301);    break;  # Multiple extra domains to same canonical domain  # Note! Many plugins already do this automatically, e.g. Seravo Plugin or Polylang  case "example.org":  case "exmple.net":  case "example.info":    header("Location: https://example.com/", true, 301);    break;  # Localized domain to subfolder  case "example.fi":    header("Location: https://example.com/fi/", true, 301);    break;  # Localized domain to subfolder  case "example.de":    header("Location: https://example.com/de/", true, 301);    break;  default:    header("Location: https://www.happy-or-not.com/en/", true, 301);}Force canonical domainGoogle and other search engines don’t like if the exact same content is served on multiple websites. If a website, say example.com, also has the domain example.net if should not serve the same content on both domains but instead choose which domain is the canonical domain and then redirect all alternative domains to it. The same applies for subdomains. Websites should choose if they are available under www.example.com or example.com and then redirect the other to the one.Normally website developers don’t need to bother with this, since WordPress core automatically redirects visitors to the canonical domain based on the siteurl setting. Also any site with the Seravo Plugin will automatically enforce both the canonical domain and the use of HTTPS to ensure all visits are protected.However, certain plugins (e.g. WPML) that mess up the WordPress Rewrite API settings might break this, and in those rare cases a developer might need to create a custom /data/wordpress/htdocs/wp-content/mu-plugins/redirect.php file with the following contents:&lt;?php// Redirect any requests for www.example.com to example.com (non-www)if ( isset($_SERVER['HTTP_HOST']) &amp;&amp; isset($_SERVER['REQUEST_URI'] &amp;&amp; $_SERVER['HTTP_HOST'] == 'example.com') {  header("Location: https://example.com/" . $_SERVER['REQUEST_URI'], true, 301);  die();}Remember to test the redirect with curl to ensure it works!Basic redirects in NginxRedirects from http://your-site.com/original -&gt; https://example.com/new/url/rewrite ^/original(.*)$ https://example.com/new/url$1 permanent;Rewrite all old *.html files to WordPress pages with pretty URLs:rewrite ^/([0-9a-z-]+)\.html /$1/ permanent;Serve country pages (example.es, example.de etc) from custom PHP fileif ($host ~ "example.es|example.de") {  rewrite ^(/)?$ /country-pages/index.php last;}Please use something like Rexexpal to test your regular expressions and be sure to use curl -IL to test your redirects without having to hassle with the cache of a regular browser.Redirecting domains in NginxIf you have multiple domains for your site and want to only use one of them:if ($host ~ "old-subdomain.your-old-site.com") {  return 301 https://your-site.com$request_uri;}Testing redirectsPlease use curl to test redirects. Using a browser for testing will not work as the browser in most cases will cache the first redirect and after that no changes will be visible when testing with a broser. Using curl with header Pragma:no-cache ensures there is no caching at all and it will print location: headers that show clearly what the redirect is.Example:$ curl -IL -H Pragma:no-cache www.example.orgHTTP/1.1 200 OKX-Cache: BYPASSLocation: https://example.com/Content-Length: 100HTTP/1.1 200 OKX-Cache: BYPASSContent-Length: 1270"
    } ,
  
    {
      "title"    : "WordPress Network",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wordpress-network/",
      "date"     : "2019-03-20 10:50:00 +0000",
      "content"  : "WordPress Network (aka multisite) is a special setup of WordPress, where one single database and set of files are used to serve multiple WordPress sites. This is useful for example:  if there is a need to allow admins to use all sites with a single set of credentials and single login  if the sites are thigtly connected, e.g. use the same theme and plugins and those want to be maintained and released in lockstep  if there is a need to create multiple sites quickly or dynamically that share the baseline code  if there needs to be multiple WordPress sites at the same domain, eg. example.com/a and example.com/bOne very typical use case is that an international company that is centrally managed has one marketing department managing multiple international sites that all share the same user database, theme and most parts of the plugins or site code. There could be a main site example.com and a site for Finnish markets at example.com/fi and one for German markets at example.com/de and so on.In many cases when people think about setting up a Network site they realize after some thinking that multiple separate sites is a better idea based on non-technical reasons, for example that each site has a different marketing person handling it or the separate sites have different life-spans and theme/plugin development cycles.  Don’t try this at home! Setting up WordPress Network (aka multisite) correctly can be a bit tricky at times. Customers should not attempt to do it themselves but ask Seravo to do it for them. The setup of a Network is included in the plan prices that allow Network setups. All sites running a WordPress Network needs to have prior approval from Seravo so that Seravo’s upkeep can be adapted to cover the entire multisite with subsites correctly.Subfolder or subdomainIf it is decided that a Network is really the best fit for a new set of sites, then the next decision needs to be if it will have a subfolder structure (e.g. example.com, example.com/a and example.com/b)  or a subdomain structure (e.g. example.com, a.example.com, and b.example.com). This decision is almost impossible to change afterwards once the network has been set up, so it needs to be planned carefully.Also note that a single WordPress installation can be converted into a Network installation at any time, but once it has it’s first subsite created, reverting back to a single WordPress installation is not possible anymore.Domain mappingIf the Network is intended to have individual domains for each subsite, then the site structure needs to be of subdomain and a separate domain mapper needs to be installed so subsites work both with their structural address and mapped address.Example:  main site example.com  subsite a.example.com, domain alias example.net  subsite b.example.com, domain alias example.orgIf is possible to mix different domains and folder structures and the WordPress Network will initially look like it is working, but later reveal severe bugs like authentication issues and redirect loops. The most robust structure is not to mix different domains and folders, but use a clean setup like the example above.Subfolder installation in subfolderIt is also possible to install a WordPress Network subfolder installation in a subfolder, e.g. example.com/branches, example.com/branches/a, example.com/branches/b and so on, where the example.com is outside of the Network and the Network main site is example.com/branches, and each ‘a’ and ‘b’ are the subsites. This however requires special settings and knowledge to get right, so don’t try it yourself.Database structureIn a WordPress Network all contents is in the same database. There are a few shared tables like:  wp_users  wp_usermeta  wp_blogs  wp_sites  wp_sitemeta  …Each subsite has its own content tables that are prefixed with a number, for example:  wp_posts  wp_2_posts  wp_3_posts  …The wp_posts includes all post contents of the main site, while wp_2_posts includes all post contents of the first subsite.Network of networksIndeed, it is actually technically possible to have a WordPress Super Network that contains multiple Networks. This is the reason the table wp_sites only have normally one entry while the subsites are listed in wp_blogs. In this terminology sites refer to super networks while blogs refer to the sites in a Network.Super adminsOn an individual site the admin is still called admin. However the user accounts that can make changes to the Network itself and manage all other users are calles super admins.WP-CLI and NetworksThe wp-cli has a few extra commands related to Networks. To list all sites one can use wp site list and to list all super-admins one can use wp super-admin list.Note that the command wp plugin list only apply to the main site when executed. To apply for a subsite, the parameter --url needs to be included, e.g wp plugin list --url=https://example.com/b.SSH/SFTP and database accessSince the WordPress Network is just one big installation with one database and one set of files, it means that anybody with direct database access or SSH/SFTP access to the files can do changes that affect any site in the network.Uploads folderAll media files will reside in the folder wp-content/uploads otherwise as usual, but each site will have its own subfolder with the site id number, e.g. wp-content/uploads/2/2019/03/pic.jpg.Themes and pluginsThemesA theme installed in a Network can be marked in the Network wp-admin as available to any site, or available only to selected sites. From the site settings each subsite can then choose what available theme to use.PluginsA plugin installed in a Network can be marked active from the settings of each subsite, or they can be marked network-active from the Network wp-admin. That means a subsite must use it and cannot deactivate it even if the admin of that site wanted.Local developmentThe main site of a Network will work out-of-the-box in local development with Vagrant. If the subsites are of the subfolder structure they will also work just fine. However, if the Network is of the subdomain structure, the subdomains will cause extra hoops and loops for the developer to get converted into a form that is routed to the local Vagrant box.InstallationSetting up a Network requries special settings in the wp-config.php file, potentially the installation of a domain mapper (Seravo prefers Mercator at the moment). In addition the web server needs to have some custom routing (at Seravo we put a nginx/network.conf file). In a subdomain installation also preparations regarding HTTPS certificates are needeed. Seravo’s customers don’t need to worry about these as Seravo takes care of them and it is included in the price of the service anyway.Local development with Vagrant and WordPress NetworkOn the production site the wp-config.php contains the Network address hard coded in DOMAIN_CURRENT_SITE. When doing local development this needs to be something else. A typical way to solve this is by having something like the following in the wp-config.php:if ( 'development' === getenv('WP_ENV') ) {  define('DOMAIN_CURRENT_SITE', 'example.local');} else {  define( 'DOMAIN_CURRENT_SITE', 'example.com' );}When using the Seravo WordPress Vagrant box the hard coded string example.local can be replaced with getenv('DEFAULT_DOMAIN') so the value automatically follows whatever was set in the config.yml as the development site domain address.Also note, that our Vagrant box command wp-pull-production-db only works for the primary domain. There is no automation to scan all the subdomains or any domain mapping used on the Network and automatically search-replace those domains to something else. This is something the site developer needs to take care of with custom scripts that do whatever is suitable for the site in question."
    } ,
  
    {
      "title"    : "WordPress Cron",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wordpress-cron/",
      "date"     : "2019-03-15 13:18:00 +0000",
      "content"  : "  Beware of bad advice! There are a lot of old guides out there that recommend putting something like */1 * * * * curl https://example.com/wp/wp-cron.php in your system cron. Never do that! It is outdated advice and will only do harm to any modern WordPress project.The WordPress cron fully works out-of-the-box and developers do not have to do anything special about it. To register your own scheduled events, please learn the WordPress Cron API carefully and in general do read the excellent WordPress Plugin Developer Handbook so you know how to design your WordPress plugin or theme code correctly from the start.What is scheduledAny plugin or theme can schedule events, so the list of events for each site is individual. The easiest way to check it out is using wp-cli:$ wp cron event list+-----------------------------------------+---------------------+-----------------------+---------------+| hook                                    | next_run_gmt        | next_run_relative     | recurrence    |+-----------------------------------------+---------------------+-----------------------+---------------+| check_plugin_updates-builder-pro-update | 2019-03-15 11:16:12 | now                   | 12 hours      || wp_privacy_delete_old_export_files      | 2019-03-15 11:18:22 | 1 minute 25 seconds   | 1 hour        || wp_update_plugins                       | 2019-03-15 17:16:29 | 5 hours 59 minutes    | 12 hours      || wp_update_themes                        | 2019-03-15 17:16:29 | 5 hours 59 minutes    | 12 hours      || wp_scheduled_delete                     | 2019-03-15 17:17:09 | 6 hours               | 1 day         || wp_version_check                        | 2019-03-15 17:18:01 | 6 hours 1 minute      | 12 hours      || check_plugin_updates-wppb-bdp-add-on    | 2019-03-15 17:36:46 | 6 hours 19 minutes    | 12 hours      || sm_ping_daily                           | 2019-03-15 18:20:52 | 7 hours 3 minutes     | 1 day         || wp_scheduled_auto_draft_delete          | 2019-03-15 18:50:37 | 7 hours 33 minutes    | 1 day         || trigger_live_discussion_notification    | 2019-03-18 07:40:00 | 2 days 20 hours       | Non-repeating || trigger_live_discussion_notification    | 2019-04-08 12:45:00 | 3 weeks 3 days        | Non-repeating |+-----------------------------------------+---------------------+-----------------------+---------------+Note the column next_run_relative. If there are many items marked now and they stay as such, it is a sign that the cron is not running correctly. If a developer does a web search on “WordPress” and “cron” they often find advice about running curl or wget from the system cron. Such advice is plain outdated and should be completely discarded, as it will not fix the issue at all. Never do that. If there is a problem with a scheduled event not running, the code needs to be debugged and fixed.Testing cron jobsThe great wp-cli has a built-in function to test cron. It can look like this:$ wp cron testSuccess: WP-Cron spawning is working as expected.An example of a failing cron:$ wp cron testError: The DISABLE_WP_CRON constant is set to true. WP-Cron spawning is disabled.Alternatively you can try running all due events:$ wp cron event run --due-nowExecuted the cron event 'check_plugin_updates-profile-builder-pro-update' in 0.175s.Success: Executed a total of 1 cron event.One may also trigger an individual event like this:$ wp cron event run check_plugin_updates-profile-builder-pro-updateExecuted the cron event 'check_plugin_updates-profile-builder-pro-update' in 0.17s.Success: Executed a total of 1 cron event.If the function executes and there are no errors immediately visible, or in the /data/log/php-error.log, then the code in that function probably works.Triggering WordPress Cron from system CronThis is not recommended practice, but if you for some reason really want to trigger the WP cron from the system cron, this is the line you would add to crontab:* * * * * /usr/local/bin/wp cron event run --due-now &gt;&gt; /data/log/wp-cron.log 2&gt;&amp;1Time zonesWhen doing time sensitive things, please keep in mind that Seravo’s servers all have their hardware clock set to UTC time, and the system time zone setting is according to the location of the server cluster.Further, keep in mind that WordPress resets all time zone data and inside WordPress the timezone is always UTC. For printing times inside WordPress use the special WordPress time functions the_date() and the_time(). Please consider the examples below:$ date -RFri, 22 Mar 2019 10:00:00 +0200$ php -r "echo date('r');"Fri, 22 Mar 2019 10:00:00 +0200$ wp eval "echo date('r');"Fri, 22 Mar 2019 08:00:00 +0000"
    } ,
  
    {
      "title"    : "Server-side PDF printing",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/pdf-generation/",
      "date"     : "2019-02-11 15:11:00 +0000",
      "content"  : "  Please contribute! This document is a stub. If you have recently implemented a PDF generator that works well with modern WordPress in Seravo’s environment, please contribute to this page via the Github link in top right corner.Traditionally developers have generated PDF files on the server side using one of the tools below. Unfortunately, none of them meet modern standards anymore.  PHP-DOMPDF: Works only with PHP 5. Is not maintained anymore.  PHP-FPDF: Works only with PHP 5 and is not maintained anymore. Anyway needed Apache and suexec to run, so was never a good option.  wkhtmltopdf: Uses webkit for rendering and thus does not render correctly all modern websites. Needs X virtual framebuffer which is inconvenient server-side.For modern WordPress sites there are three recommended options:  Render PDF files client-side using JavaScript and/or print CSS  Render PDF files server-side using the headless Chrome available in Seravo’s environments  Use an custom WordPress plugins for the purpose (e.g. Waterwoo for watermarks) or external service via API requestsAlso note, that since Seravo’s server have NodeJS available, any of the NodeJS server-side JavaScript tools out there are also potential options."
    } ,
  
    {
      "title"    : "What&#39;s new?",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/release-notes/",
      "date"     : "2018-11-12 11:26:00 +0000",
      "content"  : "Vagrant box seravo/wordpress 20190513.0.0  Includes latest versions of PHP and modules, wp-cli and in general all software  Latest version of wp-test using headless Chrome and Codeception while old and deprecated Rspec based testing system version is still available under the name wp-test-legacy.  Many new commands:  Improved wp-pull-production-db that is also capable of doing an automatic rename of the main site in a WordPress Network installation. Subdomain renames still need to be taken care manually by the developer, e.g. using a small custom rename script. Network users will also enjoy the handy wp-network-status which will describe the state of a WordPress Network install and help to quickly identify mismatches in config files and database.  New commands wp-pull-production-plugins and wp-pull-production-themes for automating bootstrapping a local development environment for sites that don’t have a custom composer.json already set up.  New command wp-fix-project to help update and compare base project files to those of the upstream Seravo/WordPress project template.  New command wp-remote-db exposes the MariaDB database to be accessible outside the virtual machine for developers who want to use database management tools on their own machine.  A long list of small bug fixes and minor updates, including getting rid of the annoying extra htdocs/htdocs symlink.Vagrant box seravo/wordpress 20190122.0.0  Introduce PHP 7.3  WP-CLI upgraded to version 2.1, and many other minor version upgrades across the box  WordPress 5.0 full compatibility  Latest version of wp-test-ng using headless Chrome and Codeception  Legacy testing system available both via wp-test-legacy and wp-test. The latter one is planned to start to point to wp-test-ng on March 31st, 2019.  New command wp-fix-languages to automatically install and update language packs in WordPress  Add deprecation warning to wp-makepot if favor of wp i18n  Bugfixes related to Avahi/Bonjour and Xdebug debugger  PHP-Pear has been removed temporarily due to security concerns over potentially backdoored PharsVagrant box seravo/wordpress 20181112.0.0  More robust SSH connection handling. Running vagrant up should fail significantly less on SSH connection issues.  Skip excess SSH key acceptance dialogs  Node.js upgraded to version 8.12.0 LTS with accompanying upgrades to Yarn, NPM and other Node.js tools  More robust startup of DBUS and Avahi  New commands          wp-xdebug-on      wp-xdebug-off      wp-debug-info      wp-php-compatibility-check      wp-theme-security-check      wp-db-info        Updated man pages for all commands  Major updates to wp-test-ng command and Codeception framework  Includes also all the improvements available in Seravo’s production and testing environments in last 4 monthsTest Seravo/WordPress-betaIf you want to test the next Vagrant box version before it is released, modify your Vagrantfile provider to use box seravo/wordpress-beta instead of the official seravo/wordpress box."
    } ,
  
    {
      "title"    : "Environment variables",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/environment-variables/",
      "date"     : "2018-11-11 21:52:00 +0000",
      "content"  : "System environment variables at SeravoIn all Seravo’s environments (production, staging/testing shadow, local development etc) there are many environment variables defined. Examples include:WP_ENVThe environment variable WP_ENV always contains the environment name. When code is running on the live production site it always sees a WP_ENV with the value production. Different testing environments have different names, e.g. staging and development.CONTAINERThis is the name of a particular environment instance. For example example_1ab2c3. This may change over time. Don’t write any logic in your WordPress code based on this.DEFAULT_DOMAINThis is the internal domain name given to a live production site given by Seravo. An example could be example.fi.seravo.com. It is used in cases when the real external domain (e.g. example.com) has not yet been registered, does not point to the site or might have some other issue and does not work.REDIS_HOST and REDIS_PORTThese always contain the Redis server address and port.DB_HOST, DB_NAME, DB_USER, DB_PASSWORDThese always contain the database credentials. Please rely on these and the database connection will always work even when you have different passwords in production and in testing. Avoid from hardcoding any passwords or server addresses in your code.AUTH_KEY, LOGGED_IN_, NONCE_, SECURE_AUTH_*Each of these contain a unique key (e.g. xifablhk84nimz4k6p71b5b2hvuq0js66m9db7vcfthdxa4bvtumfykirwfq342q) which is different from environment to environment, but always the same inside the same environment. Using these constants makes the source code of your WordPress site more secure, since it will be void of any secrets. Below is an example of how the wp-config.php at any site at Seravo looks like:/** * Authentication Unique Keys and Salts * You can find them by running $ wp-list-env */define('AUTH_KEY',         getenv('AUTH_KEY'));define('SECURE_AUTH_KEY',  getenv('SECURE_AUTH_KEY'));define('LOGGED_IN_KEY',    getenv('LOGGED_IN_KEY'));define('NONCE_KEY',        getenv('NONCE_KEY'));define('AUTH_SALT',        getenv('AUTH_SALT'));define('SECURE_AUTH_SALT', getenv('SECURE_AUTH_SALT'));define('LOGGED_IN_SALT',   getenv('LOGGED_IN_SALT'));define('NONCE_SALT',       getenv('NONCE_SALT'));Using DotenvThe wp-config.php file uses Dotenv by default which enables you to create a file called .env in the root of your project to override default environment variables.Example:$ cat .env.development# Run 'ln -s .env.development .env' in project root to activate thisWP_TEST_URL=https://example.devDOMAIN_CURRENT_SITE=example.devNOBLOGREDIRECT=https://example.devCOOKIE_DOMAIN=.example.dev$ ll .envlrwxrwxrwx 1 otto otto .env -&gt; .env.developmentYou can have template files like .env.development tracked in version control, and then make a location-specific symbolic link from .env to the correct file. By default the .env file is and should be ignored by git via gitignore.Note on Dotenv version 2.4.0In the version 2.4.0 the syntax of Dotenv changed. In new instances of wp-config.php you will find lines like:$dotenv = new Dotenv\Dotenv($root_dir);$dotenv-&gt;overload();The older syntax was:Dotenv::makeMutable();Dotenv::load($root_dir);The version of Dotenv on your site (and syntax in your wp-config.php) is what was at the time when the site was created. If you later run composer update and thus update Dotenv to version &gt; 2.4.0, you need to update the lines in your wp-config.php manually, otherwise your WordPress will just display a blank page and your /data/log/php-error.log will have fatal error messages about Dotenv."
    } ,
  
    {
      "title"    : "New generation testing system",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/ng-integration-tests/",
      "date"     : "2018-09-21 11:36:22 +0000",
      "content"  : "Testing ensures WordPress updates don’t break a siteTested updates has always been an integral part of Seravo’s WordPress service. Keeping software up-to-date is important for both functionality and security. Unfortunately all changes in new software releases are not always good and can cause regressions. At Seravo we run tests before and after updates to ensure the site does not break on updates.Tests use the site like a real visitor would doOur tests are so called acceptance tests (or integration tests) because they test the site on a high level (and not single pieces of code like unit tests would do). The goal is to browse the site like a real visitor would do and find problems that could be relevant to real users.Our tests simulate among others the following use cases:  User visits your-site.com/wp-login.php and sees the login form.  When user fills correct password and username he sees the WordPress dashboard including adminbar.Our system will detect if during these simulated visits any of the following problems occur:  The HTTP server fails and emits error code (e.g. HTTP 500)  WordPress/PHP code fails and does not generate a page that a browser could sensibly parse  CSS code fails to load or images or other assets fail to load  JavaScript emits warnings or errors to the JavaScript consoleIf any of the failures above occur, the test will end and emit an error. Sites where the tests don’t pass will not proceed with updates.  Note on security updates: If a security update is considered critical, Seravo will ignore any test results and proceed with installing the security update anyway when necessary.The wp-test command and Codeception tests also make screenshots of the site, which is later used by another step in our testing system to detect visual regressions.Testing is separate from monitoringTesting can be used by developers to verify that their code changes don’t break a site. Seravo uses the tests to check that a site is OK before and after updates. Seravo also does many other kinds of testing to sites, including security testing, PHP version compatibility testing etc.Seravo monitors all sites 24/7 and our staff reacts if we detect that a site has stopped working. The monitoring is based on other tests, not these acceptance tests.New generation testing technology: Codeception and headless ChromeOur new testing system uses a PHP testing framework called Codeception. This will make it easier for WordPress developers to write tests compared to how it was in our previous testing system that used Ruby.The simulated browsing of a site is done using headless Google Chrome with the ChromeDriver. This is as close to the real thing as possible, and a major improvement to our previous system that used PhantomJS.Tests mentioned above in the previous chapter have been already implemented by Seravo and forms the baseline of the tests for each site.Running testsYou can use the command wp-test in any environment (production, testing/staging shadow, and Vagrant development box):$ wp-testI: Starting wp-test...I: Using URL 'https://www.example.com' for pre-flight checks.I: Pre-flight test for https://www.example.com returned HTTP code 200I: Executing ChromeDriver...Starting ChromeDriver 2.41.578700 (2f1ed5f9343c13f73144538f15c00b370eda6706) on port 9515Only local connections are allowed.I: Ensure test user exists...I: Updated permission and password for existing test user...I: Running Codecept test suite 1/1..Codeception PHP Testing Framework v2.5.0Powered by PHPUnit 6.5.13 by Sebastian Bergmann and contributors.Running with seed:Acceptance Tests (2) ------------------------------------------------------------------------------------⏺ Recording ⏺ step-by-step screenshots will be saved to /data/reports/tests/Directory Format: record_5bcf04ca2322c_{testname} ----✔ SeravoCheckWPHomeCest: Try to open home (1.86s)✔ SeravoCheckWPLoginCest: Try to login and access wp admin (10.60s)---------------------------------------------------------------------------------------------------------⏺ Records saved into: file:///data/reports/tests/records.htmlTime: 12.76 seconds, Memory: 12.00MBOK (2 tests, 3 assertions)I: Lower test user privileges as test ended...I: Finished running wp-test  Note: These tests can be run by Seravo or our customer at any time, in any environment. The tests should therefore be safe and not cause any problems even when run against a live production website.Write your own testsTest suite files located in the path /data/wordpress/tests/codeception/ will be executed. Group the tests that test the same features and try to name the files logically to make it easier for your collaborators to debug or extend the tests later.If this directory does not exist on your site (or the git version control of your site), then you can just go ahead and create it. The new generation testing system is much simpler and does not pollute your site git repository with many extra files like our previous system did.There are three types of customs tests supported:  Tests written in procedural PHP code in files at /data/wordpress/tests/codeception/acceptance/*Cept.php  Tests written in object-oriented PHP classes in files at /data/wordpress/tests/codeception/acceptance/*Cest.php  Custom Codeception test suite defined in /data/wordpress/tests/codeception/custom.yml1. Simplest version: *Cept.php filesThis is very straight forward and simple to write and maintain for a small set of tests.Example filename: /data/wordpress/tests/codeception/acceptance/ExampleCept.php&lt;?php$I = new AcceptanceTester($scenario);$I-&gt;amOnPage('/');$I-&gt;checkBrowserConsole();$I-&gt;see('WordPress');See short examples in StackOverflow.2. Elegant version: *Cest.php filesUsing proper PHP classes offer control on what tests output and how they are executed. Note that each file can contain only one PHP class and the class name must match the filename.Example filename: /data/wordpress/tests/codeception/acceptance/ExampleCest.php&lt;?phpclass ExampleCest {  /**   * Open front page (/)   **/  public function openFrontPage(\AcceptanceTester $I) {    $I-&gt;amOnPage('/');    $I-&gt;checkBrowserConsole();    $I-&gt;see('WordPress');  }}Example filename: /data/wordpress/tests/codeception/acceptance/UserCheckWPHomeCest.php&lt;?phpclass UserCheckWPHomeCest{  /**   * Try to do search using form on homepage (/)   **/  public function trySubmittingSearch(\AcceptanceTester $I)  {    // Navigate to homepage (/)    $I-&gt;amOnPage('/');    // Check that the browser console is empty (e.g no JavaScript errors)    $I-&gt;checkBrowserConsole();    // Check that string 'Lorem ipsum' is found    $I-&gt;see('Lorem ipsum');    // Check that we see h1 level header with id 'maintitle'    $I-&gt;seeElement('h1#maintitle');    // Check that we see from with name 'searchform'    $I-&gt;seeElement('form[name=searchform]');    // Fil field with name 'search' with value 'mysearchtermgoeshere'    $I-&gt;fillField('input[name=search]', 'mysearchtermgoeshere');    // Submit the form by clicking element with id 'submitSearchForm'    $I-&gt;click('#submitSearchForm');    // Check that we see string 'Search results:' on the page after clicking submit above ^    $I-&gt;see('Search results:');    // Make screenshot of the result page    $I-&gt;makeScreenshot('searchresults');  }  /**   * Check that hidden element exists on a page   *   * Backend used by our testing environment doesn't "see" elements that are hidden to user,   * but we can check if these elements exists by looking at DOM.   */  public function tryIfNonVisibleElementExists(\AcceptanceTester $I) {    // navigate to page /example    $I-&gt;amOnPage('/example');    // Search element in DOM (by id, yet again)    $I-&gt;seeElementInDOM('#myhiddenelement');    // Navigate to another page    $I-&gt;amOnPage('/example2');    // Make sure we DON'T see unwanted element that should be on this page    $I-&gt;dontSeeElementInDOM('h3#thisshouldnotexist');  }}To try your new test, simply run:wp-test --debug --fail-fast  Note: Tested Wordpress updates are included in all plans at Seravo.com, even in the most affordable one. Writing custom tests, or debugging custom test that somebody else wrote, is not included in the monthly fee. You can however at any time buy at an hourly rate bespoke work for your site, including writing custom tests for it.3. Complete custom Codeception test definition: custom.ymlOur tests also support fully customized Codeception test suites. Put the test suite files in the path /data/wordpress/tests/codeception and ensure there is a definition file called custom.yml that can be run by codecept run.List of Helper functionsWordPress Helper moduleThese tests use the helper module SeravoAcceptanceHelper which is included in the project.// return current environment (production, development, staging, update)$I-&gt;getEnvironment();// return true or false if tests run in specific environment$I-&gt;isProduction();$I-&gt;isDevelopment();// get an array containing the browser console contents$I-&gt;getConsoleLog();List of Codeception functionsSee also Codeception documentation for acceptance tests to learn more.// Navigating$I-&gt;amOnPage('/path/to/navigate?value=1');// Clicking links and buttons$I-&gt;click('#elementId');$I-&gt;click('a.classnamehere');$I-&gt;click('input[name=ok]');// Interacting with forms$I-&gt;fillField('field-identifier', 'value');// Querying$I-&gt;seeInTitle('this should be in &lt;title&gt; tag');$I-&gt;see('My title');$I-&gt;seeElement('//table/tr'); // XPath$I-&gt;seeElement('h1'); // by tag$I-&gt;seeElement('#id'); // by id$I-&gt;seeElement('.classname'); // by class$I-&gt;seeElement('div#main p.intro'); // CSS selectors// Check that browser console is empty$I-&gt;checkBrowserConsole();// Check that browser console is empty ignoring warnings$I-&gt;checkBrowserConsole(true);// Check that browser console is empty ignoring warnings// and ignoring one specified severe message$I-&gt;checkBrowserConsole(true, array(  array(    "level" =&gt; "SEVERE",    "message" =&gt; ".* Uncaught DOMException: play() failed because the user didn't interact with the document first.",    "regex" =&gt; true  ));Howto whitelist specific harmless Chrome console errorsAs a rule of thumb, if the Chrome developer console outputs anything, our testing system considers it an error, regardless of being related to JavaScript, CSS loading, image loading, Mixed content security warnings or whatever.However, sometimes the Chrome console messages can be false positives and not actual errors that need to be addressed. To handle those we offer console message whitelisting.A typical example of a false positive would be this JQuery deprecation warning which most website developers have seen in the wild somewhere:  WARNING: https://example/wp-includes/js/jquery/jquery-migrate.js?ver=1.4.1 44:11 “JQMIGRATE: jQuery.browser is deprecated”To ignore this message site-wide, create a file with the name and path /data/wordpress/tests/codeception/acceptance/console-whitelist.json and add the following contents to it:[  {    "level": "WARNING",    "message": ".* JQMIGRATE: .* is deprecated",    "regex": true  }]The field message is compulsory and it must either be a string to be whitelisted, or a regular expression. If regular expressions are used, then there the field regex must also be set to true. The field level is not compulsory.You can also whitelist console messages per test by passing a custom array as second parameter to the assertion $I-&gt;checkBrowserConsole(). Using the site-wide whitelist has the benefit that it affects all invocations of checkBrowserConsole(), including the baseline tests Seravo runs for your site."
    } ,
  
    {
      "title"    : "Profiling with Tideways",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/tideways/",
      "date"     : "2018-03-14 21:31:00 +0000",
      "content"  : "  Recent PHP version required: To use the Tideways integration, make sure you have PHP 7.2 (or newer) activated (with set $mode php7.2;). You want your site to be as fast as possible, right?Xdebug is not for production useWe have provided a pre-installed Xdebug configuration in our Seravo WordPress Vagrant images for a number of years already, but not in our production or staging environments. The reason for this is that the instrumentation Xdebug does is quite heavy and it can be performed only in a separate development environment. For production environments something more lightweight is needed, a tool that does not slow down the site. Also to complement long running in-production tests one needs a good dashboard and tools to extract useful information from the profiling data collected. Therefore we decided to cooperate with Tideways and integrate their profiling tool into our system.Tideways can be safely used in both production and staging all the timeTideways is a service to collect and analyze information about how the code of your WordPress site performs. It helps developers to:  Visualize changes in the site production code performance over time  Drill down into execution logs and find bottle necks in PHP code  Analyze database queries so their performance can be optimized  Alert about PHP errors and analyze stack traces so the code can be improved to avoid the errors  E-mail, Slack, Github and other integrationsThe Tideways profiling agent has a neglible impact on the performance of the site, so it can be active in the backgroud all the time. In our integration we have configured it to sample 1% of production site PHP executions and 10% of executions in the staging shadows, collecting execution traces and profiling data. The Tideways integration also has the Tideways agent pre-configured to work optimally with WordPress. Data from the production and staging shadows is automatically separated in the logs, so developers can easily tell what data came from where.Tideways offers the same timeline and execution path visualizations as XDebug+Webgrind, but in addition it will also store the PHP profiling traces permanently in an online database, which allows you to compare changes over time. Thanks to the team functionality you can share the information between all the development team members. Tideways even has automatic bottle neck detection and it gives improvement suggestions. Analyzing and improving your application could not be easier. Tideways has a ton of features and we strongly recommend you to try it out.  Tideways.com subscription recommended: Note that the free trial version of Tideways does not include all features. For example no traces from your code running in staging environments on Seravo’s servers will be shown on Tideways unless you have at least the Lite plan on Tideways.com.Steps to activate Tideways  Create an account at Tideways. You can start with a free trial and later upgrade to a paid account.  In the Tideways dashboard, create a new application and get the application API key (e.g. A0A0A0A0A0A0A0A0).  Create a file /data/wordpress/.tideways.key in your project and save the application API key in it (e.g. echo 'A0A0A0A0A0A0A0A0' &gt; .tideways.key).  In your production site environment, run wp-restart-php to active the PHP module and the Tideways daemon with your Tideways key.  Double check that you are running PHP 7.2 or newer.  Go back to your Tideways dashboard to see the data start pouring in!Seravo’s customer support is available to help you with the Tideways key activation if the steps above didn’t work for you as expected.Invite more users to your accountOnce you have a login to Tideways.com, you can setup a organization and invite more users to your account. The more users your account has, the more people have access to the collected profiling data and can potentially find out things to optimize and improve. One could for example share access to the entire development team!The fixed monthly fee of Seravo does not include any site specific performance optimization work, but our expertise on the topic can be utilized on per hour consulting basis. In such a case you could invite the Seravo staff member to your Tideways account to review the collected data."
    } ,
  
    {
      "title"    : "Remote testing",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/remote-testing/",
      "date"     : "2018-01-10 23:15:00 +0000",
      "content"  : "By default the local Vagrant is not easy to access by othersWith the default Vagrant box settings it is not easy for someone other than yourself to see the website that is running locally. The exact limitations depend on what kind of network settings your development laptop has and how the Virtualbox machines running on it are exposed. This documentation cannot cover all of those situations, but on the other hand the setup is not Seravo-specific, so any generic Vagrant documentation on this topic can be useful for more information.Sharing the Vagrant box website via Avahi/BonjourThe Seravo Vagrant box has Avahi support out-of-the-box. If you want to make your website available to your local network, you can simply edit the config.yml in your project and set avahi: true. After restarting the Vagrant box it will register the site name to the Avahi service running in your system, which will make it available to the the local network.Normally typing e.g. https://wordpress.local only works on your own computer as it can look up the IP address from the /etc/hosts file. With Avahi anybody in the same local network can type https://wordpress.local and access the site (assuming there are no additional firewalls or restrictions in place).Sharing the Vagrant box website via PageKiteIn theory you could also allow anybody in the world to access your development website running inside your Vagrant box by using a tunneling system like PageKite or localtunnel. We have not tested this setup and it involves so many details regarding your local settings that it is not possible for us to provide generic instructions here. These kind of setups are however very rare. Most developers use the shadow (staging) environments instead when they want to publish their work on a server that can be accessed anywhere from the Internet."
    } ,
  
    {
      "title"    : "Moving data between instances",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/moving-data/",
      "date"     : "2017-03-26 19:45:00 +0000",
      "content"  : "  Warning: Unlike some other publishing systems, in WordPress there is a lot of valuable content in the database (user accounts, comments, statistics) and among the files (mainly uploads). Bluntly pushing the database or files from staging to production will most likely destroy some valuable data, so please plan your deployments carefully.Pushing code is easyOur WordPress project template (and many other similar ones, like Bedrock) have been designed with easy deployments in mind. It is preferred to use them so that your deployment to staging is a simple git push staging and after testing to production just git push production, all neatly orchestrated from the lead developers laptop.Pushing a full site with database and files is riskyWordPress does not separate clearly between actual site content, like the user or customer database, and layout related settings and other stuff in the database. Developers are sometimes tempted to push the entire database from staging to production, but be aware of the potential consequences. This approach is typically suitable only for a very static site.However, if you are certain that this is a safe thing to do, you can simply push files from the staging (or any other instance) to production via SSH. Alternatively, you can pull files and the database from production from any remote instance with SSH access.Below are the steps how to do this from production, fetching data from staging.Preparations before moving data from staging to productionThe first step is to run wp-backup to make sure your current database and files are backed up in case of an emergency and a need to revert your actions. The latest database backup can be found at /data/backups/data/db/&lt;sitename&gt;.sql and the WordPress files at /data/backups/data/wordpress/.The second step is to test your SSH credentials and the connection. For that you need to know the SSH port number of your staging instance.example@example_48daaa:/data/home/example$ ssh example@example.seravo.com -p 11111The authenticity of host '[example.seravo.com]:11111' can't be established.ECDSA key fingerprint is aa:aa:aa:aa:48:29:40:e7:a5:9f:ff:09:fc:aa:aa:aa.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '[example.seravo.com]:11111' (ECDSA) to the list of known hosts.example@example.seravo.com's password:example@example_37faaa:~$ exitIf you are familiar with SSH keys, you can generate them in the production environment and install them on the staging instance for easier access from production. For a safe and secure workflow, do not automate the access from staging towards production. The other way around is okay, as production is always persistent and trusted. Also feel free to use .ssh/config in the production instance.Copying files with rsync and SSHThe following commands demonstrate how to use rsync via SSH to fetch new files as quickly as possible (rsync only transfers files and parts of files that are different). The flags stand for:  -av saves the file attributes (owner, time stamp) and prints out a verbose list of all files that changed  --delete-after will delete all the files from production that did not exist in staging, in effect clearing the obsolete files away  --exclude=wp-content/uploads will omit the uploads directory from being transferred from staging, and from being replaced in production  In some cases you might want to use exclude=.git too  The other options define the SSH port and hostname of the staging instance and the path to the wordpress/` directoryEnsure that a SSH port of staging environment is used in the following commands!example@production:~$ rsync -av --delete-after --exclude=wp-content/uploads -e 'ssh -p 11111' example@example.seravo.com:/data/wordpress/ /data/wordpressexample@example.seravo.com's password:receiving file list ... doneAfter the operation above we will rsync the uploads folder, but with different options to make sure that all files that exist in the uploads folder in production will remain intact and we will only add any additional files from staging.example@production:~$ rsync -av -e 'ssh -p 11111' example@example.seravo.com:/data/wordpress/htdocs/wp-content/uploads /data/wordpress/htdocs/wp-content/Importing the database with wp-cli and SSHAfter all files have been transferred, the next step is to bring over the database from staging, that is if we really want the production to be a 100% identical to what we have in staging. Do note that this will overwrite all data currently in the production database.First we make a fresh database export in staging directly over SSH and import it with one single command:ssh example@example.seravo.com -p 11111 'wp db export -' | wp db importAfter this step you are done and can proceed to peruse wp-watch-logs and browse the site to make sure everything looks good. If not, repeat the steps above or revert to backups."
    } ,
  
    {
      "title"    : "Restricting access",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/restricting-access/",
      "date"     : "2017-03-26 19:45:00 +0000",
      "content"  : "Restricting access with built-in features of WordPressWordPress offers a multitude of user management and access control related features. We warmly recommend that you use them as your primary means of access management. For instance, you can easily protect individual pages and posts with a password by setting post visibility in WP admin to ‘Password Protected’. You can also activate Maintenance Mode and allow only selected users to access the entire site.For more complex scenarios you can install BuddyPress or use some of the groups plugins.Fiddling with Nginx based access control methods should be your last resort. Using WordPress features and PHP code is always the more flexible, more user and developer friendly option and the preferred method for managing authentication and enforcing access controls.Restricting access with HTTP Basic Authentication  Note: This applies only to static HTML pages. If you want to restrict access to certain WordPress pages or sections, use a WordPress plugin or PHP code to implement it.The HTTP Authentication headers based system is quite old fashioned and not exactly the state-of-the-art in cryptographic security. The list of usernames and passwords needs to be maintained manually using the htpasswd command line utility. First create a file with the -c option and then add more users as you need:htpasswd -bc /data/wordpress/nginx/htpasswd-file-example username1 adminpasswordhtpasswd -b /data/wordpress/nginx/htpasswd-file-example username2 userpasswordOnce you’ve generated a htpasswd file you can activate it for a particular path for example by creating a file /data/wordpress/nginx/htauth.conf like this:location ^~ /restricted-section/ {  auth_basic "Arbitrary realm name";  auth_basic_user_file /data/wordpress/nginx/htpasswd-file-example;}Remember to run wp-restart-nginx to make the new Nginx config file effective.  Warning: Do not activate HTTP Authentication for the entire site. Otherwise you will render the wp-test test unusable, all automatic monitoring of your site will start to fail and Seravo’s admins cannot access your site to check it and do upkeep anymore.Restricting access by IP addressIf you have a section of your site that should only be visible for example to visitors from a certain subnet, typically some sort of intranet or extranet page, you might want to use IP address based access controls. Be warned however that it is really hard to get right. Unlike domain names, IP addresses come and go, and you need to manually keep the IP lists up-to-date. IP addresses should not be used for very sensitive content, as no per-user audit trail whatsoever is formed when using blanket IP access rules.Sometimes your users need to access the page on-the-go, so you should also provide a password authenticated way of access they can resort to. A site should never have hard, IP restrictions that are impossible to bypass.location ^~ /restricted-section/ {  allow 1.2.3.4;  allow 5.6.7.8;  deny all;}  Warning: Do not activate IP address based restrictions for the entire site. Otherwise you will render the wp-test test unusable, all automatic monitoring of your site will start to fail and Seravo’s admins cannot access your site to check it and do upkeep anymore.Use two factor authentication and don’t waste time on implementing IP based restrictionsMany people want IP based restrictions because they are afraid that their users use subpar passwords or they want to increase the barrier for unauthorized access for other reasons. Rather than resort to IP based restrictions, look into WordPress plugins that implement two factor authentication (2FA). The primary plugin to research is the Two Factor plugin, a feature project for WordPress Core. It will most likely become a part of WordPress itself in the future.Restricting access to WordPress admin panel (wp-admin)Occasionally WordPress website developers want to allow users to log in to the site, but not see the WordPress admin area (/wp-admin) or admin bar. This is feature WordPress isn’t officially designed for, but the user experience can be achieved with suitable user roles and some CSS rules to hide the admin area.Avoiding hitting existing restrictionsFor security reasons Seravo deploys an array of restrictions that are designed to never interfere with legitimate usage of any WordPress site. However, in some rare cases certain WordPress plugins that function incorrectly may hit the Seravo’s security limits if the conditions listed below are all true at the same time:  making over 200 HTTP requests per minute  to WordPress/PHP scripts  from a the same IP address.If this limit is hit, the symptom is a HTTP response with code 429 to the browser/bot, warnings about PHP flood restrictions in the log at /data/log/nginx-error.log. If you for whatever reason hit these limits, please investigate the HTTP access logs on what the requests are and why the plugin or other piece of code is bombarding the site with so many requests.There should never be any real need for a legitimate user or WordPress plugin to exceed this limit. Note, that this limitation does not apply to static assets (JS, CSS, images) nor to cached PHP responses."
    } ,
  
    {
      "title"    : "Custom error pages",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/error-pages/",
      "date"     : "2016-08-24 23:11:00 +0000",
      "content"  : "Custom error pagesIn some maintenance scenarios it might be handy to be able to show custom error pages.Flexible maintenance pageIf there is a need to temporarily divert all traffic to a single static page (for example to show an error message or a maintenance message) the best way to accomplish this is by adding to your /data/wordpress/nginx/custom.conf a line like:rewrite ^(.*)$ /maintenance.html break;The target of the rewrite can actually be any URL and the temporary view shown to visitors can also reside on an entirely different server. This is the recommended way to implement a static maintenance page.WordPress core maintenance moduleAlso keep in mind that the built-in WordPress drop-in maintenance.php and .maintenance files can be used in the Seravo environment just like anywhere else. For details see wp_maintenance in the WordPress codex.Plain static maintenance page (not recommended)If a file named index.html is placed in the /data/wordress/htdocs/ folder, it will take precedence over the existing index.php file that normally loads WordPress. A static file will always work even if PHP is broken for some reason. A static page might also be useful during a DDOS attack as static content can be served at a much higher rate than PHP generated content.Custom database connection error pageIf WordPress is unable to connect to the database, the PHP file /data/wordpress/htdocs/wp-content/db-error.php will be displayed instead. This file may be customized to show any static or dynamic content instead of the default template.Custom PHP fatal error and parse error pagesAs a general rule, most errors in PHP should be handled by PHP. WordPress does this pretty well but it can be extended by using custom shutdown handler to catch PHP fatal errors and PHP parse errors. These normally make PHP emit HTTP code 500 and an empty page. The empty page is very unhelpful for visitors, because their browser will either show just a blank page or a very generic error page depending on what is built into the browser.The following code example shows how to modify index.php to show a custom static file 500.html on fatal PHP errors instead of an empty response. This can be used to show for example a sitemap, contact information, maintenance notice or whatever seems fit for the page.&lt;?php/* * WordPress includes code for most error handling * apart from parse errors and fatal errors. * With this function a custom error message will be * shown when those errors are catched. * * Normally PHP would emit HTTP code 500 with no * payload on fatal errors. This function adds * a customized payload to inform users about whatever. */function customErrorHandlerAtShutdown() {  $error = error_get_last();  if ( $error ) {    // Error constants listed at    // http://php.net/manual/en/errorfunc.constants.php    switch ( $error['type'] ) {      case E_ERROR: // PHP Fatal error      case E_PARSE: // PHP Parse error        header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");        header("Cache-Control: post-check=0, pre-check=0", false);        header("Pragma: no-cache");        include '500.html';        break;    }  }}register_shutdown_function('customErrorHandlerAtShutdown');// WordPress view bootstrapperdefine('WP_USE_THEMES', true);require(dirname( __FILE__ ) . '/wordpress/wp-blog-header.php');This code also makes sure that error pages are not cached by any step in the HTTP connection.Custom error pages from NginxThis is not a recommended practice. Almost always error handling should be done in some intelligent fashion in WordPress/PHP code. There should be no need to resort to error_page directives in Nginx.The following directive is however possible to do if one wants Nginx to server error pages instead of PHP. Below is an example of how a file called /data/wordpress/nginx/error-pages.conf could look like:fastcgi_intercept_errors on;error_page 500 501 502 /wp-content/wp-error.php;error_page 499 /wp-content/499.html;This error page is independent of the WordPress stack and will work despite PHP errors in WordPress code. This will however mask all PHP output on errors and thus is almost always a bad idea. Also layers higher up in the caching stack might pick up the error code and replace the response with something else conveying the same error."
    } ,
  
    {
      "title"    : "PHP7 and HHVM",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/php7-hhvm/",
      "date"     : "2016-06-21 19:24:02 +0000",
      "content"  : "Alternative PHP EnginesBy default, Seravo.com uses the PHP7 as its PHP driver because it is widely supported in the WordPress community and the vast majority of plugins will work with it. Alternative engines have however also been available and customers can easily change the PHP version in use.PHP versionsThe PHP version can be modified by making a (or modifying an existing) file in /data/wordpress/nginx/*.conf that contains the line:set $mode php7.3;Currently available options are: php7, php7.3, php7.2, php7.0, php5.6. The version php5 is an alias for php5.6 and php7 is an alias for php7.0.If for some reason an older PHP version is preferred, just add a line to Nginx conguration for example in a file named /data/wordpress/nginx/php.confset $mode php7.1;Remember to reload the Nginx configuration afterwards with wp-restart-nginx.The same instructions work both in our Vagrant box and on the live server. When you make the Nginx configuration part of your development repository, all your development, staging and production environments will use the same PHP back-end across the board.HHVMFor a performance boost we used to offer the option of using the Facebook’s Hip Hop Virtual Machine (HHVM) instead of PHP as the WordPress driver. HHVM was a massive improvement over PHP5 in speed, but sometimes suffered from stability and compatibility issues. Now when PHP7 has been in general availability for over a year, HHVM is not offered for new WordPress instances any more as HHVM has been deprecated in favour of PHP7."
    } ,
  
    {
      "title"    : "Nginx Web Server",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/nginx/",
      "date"     : "2016-05-17 10:24:02 +0000",
      "content"  : "BackgroundTraditionally, most hosting sites have been using Apache bundled with PHP as their web server of choice. However, during the last few years many have migrated from Apache to Nginx due to performance benefits and increased flexibility.Default settingsNginx comes pre-configured to use with WordPress in the Seravo setup. There is no need to peruse any guides online on how to set up Nginx with WordPress.Our default Nginx configuration includes:  Strong HTTPS settings (see example)  A Let’s Encrypt certificate for your public domains  HTTP cache with stale cache configured  Automatic expiration headers for static content  The Pagespeed module  User configurable API  Gzip  And lots more…ConfigurationIn most generic Apache hosting platforms, you have the ability to use .htaccess files to add custom rules to your web server. There are multiple reasons why this is not optimal, one being the inherently slower execution time for all page loads on the site.Instead, we give our users access to their nginx configuration through a directory that contains the .conf filesm which will be read by Nginx on startup. These configuration files can be found in /data/wordpress/nginx/*.conf.  Note: Under the hood, your custom Nginx configuration is included like this:  server {  listen 80 default_server;  server_name your-site.com;  ...  include /data/wordpress/nginx/*.conf;  ...}  How to create your own rulesThe file /data/wordpress/nginx/examples.conf includes a few examples for you to start with, all disabled by comments. We recommend that you do not edit this file directly. Leave it as a reference instead and create your own custom.conf file from scratch.$ cd /data/wordpress/nginx$ nano custom.confTo get started with your configuration you can copy stuff from examples.conf. Remove the comments from the beginning of the line and edit the rules to match the specific needs of your site.Restarting NginxAfter you’ve made changes to your nginx configuration, please reload the configuration by runningwp-restart-nginxIf there should be any errors wp-restart-nginx will warn you about them and refuse to restart before the issue is fixed.ExamplesRedirectsPlease see our separate documentation page on Redirects. In most cases it is recommended to do the redirection and HTTP request rewrite logic in WordPress/PHP, which is much more flexible than Nginx. There are no downsides to doing redirects in WordPress/PHP as it is equally fast as soon as the first redirect has been cached.Forcing HTTPSTo force all your users to connect via https, you may redirect them to the https side with a Nginx configuration.  Note! There are many built-in methods in WordPress to enforce HTTPS and the Seravo Plugin also enforces both https and a canonical domain when it can detect that what that those are available, so most of the time one should not use the Nginx configuration below.The preferred method in Nginx to force HTTPS is with a custom variable.# Force redirect http -&gt; httpsset $force_https 1;Making exceptions to the default X-Frame-Options ruleDue to security reasons, X-Frame-Options is set for all customers of Seravo by default. Customers can set their own X-Frame-Options and thus override the default values, but it cannot be removed altogether.This can as easily be emitted from PHP code as well with a simple header("X-Frame-Options: ALLOWALL");. To have this emitted for a section of the WordPress site, the functions.php could include something like this, for example:if ( strpos($_SERVER['REQUEST_URI'], '/widget/') !== false ) {  header("X-Frame-Options: ALLOWALL");}If a single page or a section of the site without PHP functionality needs to have a more relaxed rule to allow arbitrary access, it can be done with a rule like this:location /widget/ {  add_header X-Frame-Options ALLOWALL;}Note that ALLOWALL is not an official keyword, but most browsers support it anyway. In the future the whole X-Frame-Options header will be superseded by a new system called frame ancestors. If a site emits any frame-ancestors headers, the browser will follow those rules and ignore whatever was set in the X-Frame-Options headers.Serve two different sites from the same domainSometimes it makes sense to have two separate sites served from the same domain, as it will contribute to a better user experience and improve the search engine ranking for the content of the sites, when compared to having the site split to separate domains or subdomains. For example, the majority of the site example.com could be hosted on WordPress, but the section example.com/store might be on Magento.This can be achieved using Nginx proxying. For example, create a file called /data/wordpress/nginx/store-proxy.conf with the contents:location /store/ {  proxy_ssl_server_name on;  proxy_ssl_protocols TLSv1 TLSv1.1 TLSv1.2;  proxy_pass https://store.example.com/;}Protected/authenticated downloadsThe path /wp-content/uploads/woocommerce_uploads/ is pre-configured by Seravo to be protected and accessible only when authentication is done in WooCommerce/WordPress/PHP while the download itself is handled by Nginx via the X-Sendfile header instructions.One may place any file in that path and it the server will not allow a direct download of it. It can be accessed only if the PHP code emits a special X-Accel-Redirect header with the path of a file in that location.This is the correct way to implement such a feature, since the PHP worker will not be reserved for the entire download but can continue to serve other PHP requests while the download itself has been offloaded to Nginx to handle.To make a similar protected folder in a custom location, add the following to e.g. /data/wordpress/nginx/protected-downloads.conf:location /protected-files {  internal;  alias /data/wordpress/protected-files/;}In PHP emit the headers as follows:// Do authentication etc firstif ( current_user_can( 'download_special_files' ) ) {  // Emit header to Nginx which will send the file to requester  header('X-Accel-Redirect: //protected-files/confidential.pdf');} else {  echo 'Access denied.';  die();}Automatically expiring secure download links  Note: Do not use the secure link feature with WooCommerce. Use the built-in digital download features of WooCommerce instead, which works out-of-the-box at Seravo.The Nginx Secure Link module can be used to create links to downloadable items (for example a PDF file) that are valid only for a short time.Example Nginx configuration, e.g. /data/wordpress/nginx/securelink.conf:# Make folder inaccessible publiclylocation /my-restricted-files/ {  internal;  alias /data/wordpress/my-restricted-files/;}# Define secure linklocation ~ /my-restricted-files/(.*) {    # Define values used in generating secure links    secure_link $arg_md5, $arg_expires;    # Define link form. Replace ASDF1234 with an unique secret    secure_link_md5 "$secure_link_expires$uri ASDF1234";    if ($secure_link = "") {        return 403;    }    if ($secure_link = "0") {        return 410;    }}To generate links in PHP use something like:function securelink($path) {  $secret = 'ASDF1234'; // the same secret as in Nginx config  $expires = time() + 86400; // 24h in seconds  $url = md5sum("$expires/my-restricted-files/example.pdf $SECRET")}The PageSpeed modulePageSpeed sniffs through your html documents and rewrites them to be faster. This process is very site specific, so make sure that you test everything carefully before enabling PageSpeed in production. You can find more about all the filters available in Google documentation.Turn pagespeed onpagespeed on;Add some PageSpeed filters# This does a lot so test which of these you really needpagespeed EnableFilters rewrite_css,sprite_images,combine_css,inline_css,flatten_css_imports,inline_javascript,combine_javascript,inline_google_font_css,canonicalize_javascript_libraries,rewrite_images,recompress_images;Note that with the introduction of HTTP/2, many of the techniques used by PageSpeed have become obsolete. Currently very few of our customers use PageSpeed because of the limited benefits it brings."
    } ,
  
    {
      "title"    : "Staging instances (Shadows)",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/shadows/",
      "date"     : "2016-05-11 07:37:00 +0000",
      "content"  : "ShadowsShadows are a unique feature in Seravo that provides you with non-public instances of your website for testing and development purposes. Shadows are throw-away copies of the main site. You can reset the shadow to be a fresh copy of the main site at any time (with wp-shadow-reset), and do whatever testing and experiments in the shadow you wish without risking anything on the real live production site.The shadows appear to have the same address as the real site, so it is as a true copy of the real site as possible. However, thanks to special routing, shadows are not the live site despite the appearance.Shadows are not intended to be permanent. You should use it like a testing instance of the website. If a shadow is unused for en extended time, Seravo may delete it.Getting a ShadowEmail us at help@seravo.com and ask us for a shadow instance. You will have one opened up for you in no time.It’s also possible to have your own domain or subdomain for the staging environment. Please note that this shadows with their own domain is still in development so some functionalities are not yet fully implemented and there are known bugs. For example the command wp-shadow-reset and the Exit Staging button do not currently work with a staging environment using its own domain.In later revisions of this feature, you will be able to create on-demand shadows via the WordPress admin dashboard and the Seravo plugin.Using a ShadowThe Seravo PluginThe Instance Switcher module in the Seravo plugin adds a simple interface to your WP-admin view, that allows you to easily switch between your Shadow and Production instances.SSH/SFTPYou can SSH into your shadows and work on them via SSH/SFTP or git. When a new shadow is created you will receive the SSH/SFTP credentials to use with it.Your WordPress login will remain unchanged, since the site is a copy of your production site when first created.You can tell that you’re in a shadow by looking at the WP_ENV environment variable. If you’re in a shadow, it will be staging or testing, depending on what’s applicable in your situation. Your production instance will have a WP_ENV value of production. Local Vagrant images and the like will have WP_ENV value development.example_456def$ wp-list-env...WP_ENV: stagingReset the Shadow environmentCommand line option (via SSH)You can use the command wp-shadow-reset to copy the current production site over the chosen staging instance. The command has to be run on the production server.user@example_456def:~$ wp-shadow-resetNo shadow specified, please run "wp-shadow-reset &lt;shadow&gt;" with one of the shadows below:- example_123abc- example_456defThe command requires you to choose the shadow environment you want to reset. The system will then ask if it’s okay for you that the database and all files in /data/wordpress will be replaced with data from the production site.After choosing yes, the shadow will be reset.Graphical option (via wp-admin)In the WordPress admin area you can open the page Tools &gt; Shadows to view a list of the shadows that currenlty exist for a site. Resetting them to be a fresh copy of the production site can be done with a click of a button.How does it work?Under the hood, shadows work exactly the same as your production instance does, the only difference being that they have no public domain mapped to them.Shadows have their own full Linux environment and their own database, completely separate from your production instance. You can use SSH to work on your shadow instances in precisely the same way as you can with your production instance.The Shadow CookieTo view your shadow in the browser, a special cookie called seravo_shadow must be set.The value of the seravo_shadow cookie determines which shadow instance will be served to your browser.Example:example_123abc (production)example_456def (staging)Setting a cookie seravo_shadow=456def;path=/ will return the staging instance to your browser.The Seravo plugin handles these cookies easily for you.You can also use GET parameters to set these cookies as described below.Direct link to ShadowYou can set the Shadow cookie easily via a GET parameter in the url.Example: https://example.seravo.com/?seravo_shadow=456defThe link above would set the cookie seravo_shadow=456def;path=/ in your current browser session.Curl to shadowWith curl you can fetch a file from the shadow in the following way:curl -iLs https://example.com/test/?seravo_shadow=456def""
    } ,
  
    {
      "title"    : "Updating Vagrant Box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/updating-vagrant-box/",
      "date"     : "2015-10-23 10:53:29 +0000",
      "content"  : "When to update the Vagrant BoxWhen you’re using Vagrant, it will automatically check for updates. If an update is available you’ll get a message like this:$ vagrant upBringing machine 'default' up with 'virtualbox' provider...==&gt; default: Checking if box 'seravo/wordpress' is up to date...==&gt; default: A newer version of the box 'seravo/wordpress' is available! You currently==&gt; default: have version '20151016.13.2529'. The latest is version '20151022.21.3040'. Run==&gt; default: `vagrant box update` to update....This is an indicator that you don’t have the newest version of the box available locally.Download new box imageRun the following command in your project folder so that you’ll get new version of the box seravo/wordpress:$ vagrant box update  Note: This might take a while. The image is  ~900mb large because it’s designed to have everything included for easier usage.Replace the old box with a newer oneRun the following commands in your project folder:# First make sure that the original box is online for the database dumps to work$ vagrant up# Then just destroy the box# This triggers a database dump in the box which is used when a new box comes online.$ vagrant destroy --force# Then just start it again. This will use newer box image and use the earlier database and ssl certicates$ vagrant up"
    } ,
  
    {
      "title"    : "Seravo plugin - A must-use plugin",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/wppalvelu-plugin/",
      "date"     : "2015-10-15 17:51:38 +0000",
      "content"  : "How to downloadThe plugin is available on Github, so all users can see its open development and even do pull requests for features.You can download the plugin here for testing purposes: Download plugin from GithubList of featuresVersion 1.9.3Sortable metaboxesFor improved usability and a unified WordPress user experience, our plugin now uses primarily metaboxes that behave similarily to the WordPress Dashboard.Shadow reset-buttonAdds the feature for resetting a staging environment from admin pages.Enforce the use of httpsIf the site and home addresses of the WordPress site include the https-prefix, it is enforced by redirections.Disable weak passwordsStrong passwords are enforced by not allowing the user to update their profile with a weak password.Find and remove cruft files, themes and pluginsSeravo-plugin lists files, themes and plugins that are considered unnecessary, obsolete or outright bad in the admin page with the option for removing them..Technical contact email settingYou can set your technical contact email from the admin view.Database statistics-viewStatus of the database is shown in the admin view.Moving between shadow/productionInstance switcher is not loaded when in a development environment.Optimize-images -pageYou can opt in to optimize your websites images and adjust the parameters.Run tests on WordPress core functionalitiesYou can run $ wp-test as seen in commands.Logs pageAdds an admin page where you can view all your website logs.Notifications from SeravoThese are used only for informing about service outages.Appends all logins to a log file and returns a 401 (unauthorized) http status code after a failed loginAll logins are logged to /data/log/wp-login.log. This enables more relevant logging.Hides “New updates available” naggingAll updates are handled by Seravo so your clients don’t need to worry about them. However,Seravo updates can be manually disabled from the WordPress admin Tools-&gt;Updates page.Uses nocache headers if the site is in development modeMakes development with clients so much easier.Imagine a situation where your client sees the page in development and then doesn’t know how to empty the browser cache later on.Adds a Purge Cache button to the admin barThis feature simplifies emptying your cached pages.Purging the cache is also achievable via command line: $ wp-purge-cache as seen in commands.Makes URLs content relativeThis makes migration and switching the site URL easier when publishing your site.  Note: relative URLs are automatically changed into absolute URLs when using feeds (rss, atom, etc…)Enables login to wp-admin with a secure SSL client certificateThis helps the admins and clients who have multiple sites in Seravo. If you have multiple sites in Seravo, you can request a personal SSL-certificate from our admins.List all domains associated with your accountAdds an admin page where you can view all your domains. You can also inspect your DNS-records for each domain.Checks that WordPress is configured over SSLNotifies you if you have forgotten to enable SSL in the WordPress siteurl or homeurl. Seravo provides free SSL for its customers and encourages them to ensure the safety of their site.Configuration by using filtersOur plugin contains multiple filters which can be used to turn off the features mentioned above.Add any of the filters below to the functions.php file of your theme:&lt;?php/* * This is a master switch to disable all modules. */add_filter('seravo_disable_modules', '__return_false');/* * Disable: Helpers for hiding useless notifications and small fixes in logging */add_filter('seravo_use_helpers', '__return_false');/* * Manage which users are able to see and use the purge button in WP-adminbar. * Default value is same as below: users need to have the 'edit_posts' capability. */add_filter('seravo_purge_cache_capability', function () {  return 'edit_posts';});/* * Remove instance switcher from WP admin bar */add_filter('seravo_show_instance_switcher', '__return_false');/* * Disable: relative URLs in post content */add_filter('seravo_use_relative_urls', '__return_false');/* * Prevent hiding the domain alias from search engines */add_filter('seravo_hide_domain_alias', '__return_false');/* * Disable: Wordpress login log */add_filter('seravo_use_login_log', '__return_false');/* * Disable: Check that HTTPS is enabled in siteurl and homeurl */add_filter('seravo_check_https', '__return_false');/* * Hide admin menu pages, where * {PAGE} is one of: reports, backups, updates or domains */add_filter('seravo_show_{PAGE}_page', '__return_false');/* * Disable: Check that user has changed email address no-reply@seravo */add_filter('seravo_check_default_email', '__return_false');"
    } ,
  
    {
      "title"    : "Default values",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/defaults/",
      "date"     : "2015-10-13 15:11:50 +0000",
      "content"  : "VagrantDefault credentialsWordPress:user:     vagrantpassword: vagrantMariaDB (MySQL):# For rootuser:     rootpassword: root# For WordPress DBuser:     vagrantpassword: vagrantDefault Addresses for the boxThe default address can be changed by editing config.ymlWordPresshttp://wordpress.localAdminerhttp://adminer.wordpress.localMailcatcherhttp://mailcatcher.wordpress.localWebgrindhttp://webgrind.wordpress.localBrowsersync consolehttp://browsersync.wordpress.local"
    } ,
  
    {
      "title"    : "Typical workflow",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/local-development/",
      "date"     : "2015-10-13 14:26:53 +0000",
      "content"  : "Using gitGit is available on our platform, but there is no git repository by default. This is because most users want to create their own projects, and having a git repository initialized by default might create confusion. An even bigger source of confusion would be a git repository with hundreds of untracked or modified by uncommitted files, a situation we want to avoid. From the server administration point of view the fact that there is a git repository somewhere is a signal that it is there intentionally and any uncommitted changes are real anomalies that need to be addressed.Start a fresh repo from the server contentsLog in to the server via SSH and initialize the project. You can use git add . to add all current files to the project as the default .gitignore file will omit everything that does not belong to be tracked by version control.Example:ssh my-site.seravo.com -p &lt;port&gt;cd /data/wordpress/git config --global user.name "&lt;Full Name&gt;"git config --global user.email &lt;email&gt;git initgit add .git commit -am "Initial commit"Now you can simply clone the remote git repository to your machine and start working.$ git clone ssh://$SSH_USER@$SITE.seravo.com:$SSH_PORT/data/wordpress ~/Projects/$SITE --origin productionAlternative: Start by using our project template on GitHub  Note: Do not do this if you have existing site. Pushing to production host will overwrite your current content, and might lead to data loss.The method above gives you a fresh new project with no prior history. You may however want to consider using our template as a starting point and have a shared history, which makes it easier to later merge updated versions of our project template to your project. To do that run$ git clone https://github.com/Seravo/wordpress ~/Projects/$SITE$ cd ~/Projects/$SITE$ git remote add production ssh://$SSH_USER@$SITE.seravo.com:[$SSH_PORT]/data/wordpress$ git push -f production masterTip: you can have multiple git remotes:$ git remote add github git@github.com:ottok/example-site.git$ git remote -vgithub	git@github.com:ottok/example-site.git (fetch)github	git@github.com:ottok/example-site.git (push)production ssh://example@example.seravo.com:12345/data/wordpress (fetch)production ssh://example@example.seravo.com:12345/data/wordpress (fetch)upstream	https://github.com/Seravo/wordpress (fetch)upstream	https://github.com/Seravo/wordpress (push)Start up your local copyOnce you have the project on your own machine, starting it using Vagrant is as easy as running vagrant up:# Start vagrant and follow the questions from the installer# It's safe to just push enter to all of them$ vagrant up# You can connect into vagrant$ vagrant ssh# You can pull the production database (not required on new sites)$ wp-pull-production-db# You can also pull the production plugins (not required on new sites)$ wp-pull-production-pluginsNow you can open http://wordpress.local/ in a browser and edit the files in you project and see the result immediately.When you think your code is good to go, commit it and push to production with:$ git push production masterThe .git/hooks/post-receive will run on the receiving end and run Composer and Gulp if configured to do so. Note that if you created the git repository yourself, there will be no post-receive hook until you have copied it from /usr/share/seravo/git/hooks/post-receive.When you are done, you can shut down Vagrant with halt. If you completely want to destroy the virtual image (for example to save disk space) execute destroy. Note that even after destroy you will have files under .vagrant and all the Composer installed modules etc under your project. Use git clean to get rid of all traces of vagrant up.vagrant haltvagrant destroygit clean -fdx &amp;&amp; git reset --hardInclude default site database contents in the git repositoryTo provide a seamless vagrant up experience for anybody who starts to develop the site using the git repository as their sole starting point, you should include a file named vagrant-base.sql in the repository that contains a suitable minimal database with some example settings and contents.You can easily create such a database dump file by running inside Vagrant the commandscd /data/wordpresswp db export vagrant-base.sql --path=/data/wordpress/htdocs/wordpress --skip-extended-insert --allow-root --single-transactionCustomize the ‘vagrant up’ runIf Vagrant detects that a file named vagrant-up-customizer.sh is present, it will automatically be run every time vagrant up is invoked. (Feature available in Seravo/WordPress since Jan 29th, 2017)."
    } ,
  
    {
      "title"    : "How to install Vagrant",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/how-to-install/",
      "date"     : "2015-10-13 14:26:53 +0000",
      "content"  : "  Vagrant 2.1.0 changes vagrant-triggers: Currently our Seravo WordPress Vagrant box does not work with the latest version of Vagrant due to non-backwards compatible changes in vagrant-triggers. The work-around is to install Vagrant 2.0.4 instead of the latest version for now.InstallationLinux (Ubuntu/Debian)To use Virtualbox make sure you have vt-x enabled in your BIOS.sudo apt-get install -y vagrant virtualbox virtualbox-dkmsgit clone https://github.com/Seravo/wordpress ~/wordpress-devcd ~/wordpress-devvagrant plugin install vagrant-hostsupdater vagrant-triggers vagrant-bindfsvagrant up  Optional: If you want to have PHP Composer locally installed run:  $ sudo apt-add-repository -y ppa:duggan/composer$ sudo apt-get update$ sudo apt-get install php5-composer  Ubuntu 16.04 and later need ruby-devIf you see this error message on Ubuntu 16.04 or later:$ vagrant upmkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.hIt means you need to install separately the Ruby development files:sudo apt-get install ruby-devUbuntu 17.04 and laterVirtualBox sets up the vboxnet0 virtual interface routing using the legacy ifconfig and routecommands, instead of the modern ip command. For networking to work properly, you need to runapt install net-tools.Linux (Fedora)Add RPMFusion repositories. See  RpmFusion. Repository isneeded for Virtualbox.Clone the WordPress Git repo and run following commands:sudo yum install vagrant virtualboxsudo yum install ruby-devel # Needed to build native ruby extensionssudo gem update bundlersudo gem install hittimes -v '1.2.2'vagrant plugin install vagrant-hostsupdater vagrant-triggers vagrant-bindfs# Needed to load the kernel module for virtualbox, you may want to load it automatically on boot...sudo modprobe vboxdrvvagrant upLinux (General)If you get errors related to creating host-only network adapters during vagrant up, run sudo vboxreload.It seems that sometimes the virtualbox kernel modules are not working correctly after the machine wakes up from sleep.MacOS X  Install Xcode: xcode-select --install  Install Vagrant (version 2.0.4, or any other release before 2.1.0!)  Install Virtualbox (version 5.2 or older)  Clone this repo: git clone https://github.com/Seravo/wordpress ~/wordpress-dev  Run the installation in Terminal:    cd ~/wordpress-devvagrant plugin install vagrant-hostsupdater vagrant-triggers vagrant-bindfsvagrant up              Optional: Vagrant Manager for OS X can help you manage multiple Vagrant boxes.      Windows (Cygwin)To use Virtualbox make sure you have vt-x enabled in your BIOS.You might need to disable hyper-v in order to use Virtualbox.On Windows 10 you need to run Cygwin as an administrator so vagrant-hostsupdater can write the necessary entries to /system32/drivers/etc/hosts. Otherwise you need to add the vagrant-hostsupdater entries manually.Note that in some cases you can’t modify the hosts file without administrative access. In that case you need to ask the administrator to give you access to the file.  Install Cygwin and via Cygwin openssh and git          Note: Cygwin doesn’t include a package manager, so in order to install extra packages like openssh and git, you have to select them during the Cygwin setup. To add packages to an existing Cygwin installation, you can just re-run the setup binary (i.e. setup-x86_64.exe).        Install Vagrant (version 2.0.4, or any other release before 2.1.0!)  Install Virtualbox (version 5.2 or older)  Clone this repo: git clone https://github.com/Seravo/wordpress ~/wordpress-dev  Run the installation in terminal:    cd ~/wordpress-devvagrant plugin install vagrant-hostsupdater vagrant-triggers vagrant-bindfsvagrant up        Cygwin will give you the following message and the necessary entries you need to add to the hosts file, if you try to vagrant up without administrative access.  Note that this is just an example. Your message will be different.[vagrant-hostsupdater] Writing the following entries to (system32/drivers/etc/hosts)[vagrant-hostsupdater] exampleIP-address examplehostname[vagrant-hostsupdater] This operation requires administrative access.You may skip it by manually adding equivalent entries to the hosts file.On some versions of Windows (Windows 8) you might get a “Vt-x is not available” error. You’ll need to disable Hyper-V in bios to proceed with the installation.Most bios setups have the option under “Security”.In theory, Seravo WordPress should work even without Cygwin installed, but we strongly recommend using Cygwin for doing WordPress development on Windows machines.Seravo WordPress installation with PowerShell:Note that PowerShell also needs to be run in administrator mode.  Install Git  Install Vagrant (version 2.0.4, or any other release before 2.1.0!)  Install Virtualbox  (version 5.2 or older)  Clone this repo with PowerShell: git clone https://github.com/Seravo/wordpress wordpress-dev  Run the installation in terminal:    cd wordpress-devvagrant plugin install vagrant-hostsupdater vagrant-triggers vagrant-bindfsvagrant up        Optional: Vagrant Manager for Windows can help you manage multiple Vagrant boxes."
    } ,
  
    {
      "title"    : "List of commands",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/available-commands/",
      "date"     : "2015-10-13 14:25:15 +0000",
      "content"  : "  Admin helpers          wp-backup      wp-backup-list-changes      wp-backup-status      wp-fix-checksums      wp-flush-cache      wp-last-ssh-logins      wp-list-files-mtime      wp-speed-test      wp-load-test      wp-mail-test      wp-optimize-images      wp-purge-cache      wp-reset-all-passwords      wp-reset-all-sessions      wp-reset-ssh-password      wp-seravo-plugin-update        Developer helpers          wp-list-env      wp-makepot      wp-pomo-compile      wp-restart-nginx      wp-restart-php      wp-shadow-pull      wp-shadow-reset      wp-test      wp-watch-logs        Database helpers          wp-db-optimize      wp-db-cleanup      wp-db-info      wp-db-dump      wp-db-load      wp-db-update      wp-db-admin        Vagrant commands          wp-xdebug-on/off      wp-ssh-production      wp-pull-production-db      wp-pull-production-plugins        Vagrant internal commands          wp-activate-git-hooks      wp-generate-ssl      wp-use-asset-proxy      wp-vagrant-activation      wp-vagrant-dump-db      wp-vagrant-import-db        Vagrant box contains plenty of helpers for developing your site and migrating data to/from production. Production contains most of these and also a command for purging cache.  For more information about any of the commands, please run man &lt;command&gt; or command --help.  Admin helpers  wp-backup  wp-backup - Dump the WordPress database and backup the /data directory into /data/backups/data using rdiff-backup.  wp-backup-list-changes  wp-backup-list-changes - List all files known by rdiff-backup sorted by date when backed up. Use this to find out what files really changed in the system, as the file attribute mtime is not a reliable source of information. Use rdiff-backup --exclude /data/backups --compare-at-time now /data /data/backs/data/ to find out how the current data differs from latest backup.  wp-backup-status  wp-backup-status - List all backup increments known by rdiff-backup by date. Use this to find out what backups exists. This is an alias of rdiff-backup --list-increment-sizes /data/backups/data.  wp-fix-checksums  wp-fix-checksums - Automatically fix typical situations where wp core verify-checksums has detected an error (core files changed/tampered). Does not attempt to fix any warnings returned.  wp-flush-cache  wp-flush-cache - Wrapper for wp-purge-cache with flush cache terminology in line with what wp-cli uses. See wp-purge-cache.  wp-last-ssh-logins  wp-last-ssh-logins - List last logins according to system status history and failed logins based on wtmp and btmp logs.  wp-list-files-mtime  wp-list-files-mtime - List all recently changed filed based on modification time (mtime attribute). Files modified during the last 30 days are listed. This is less reliable than wp-backup-list-changes as files can have their mtime attribute set to anything.  wp-speed-test  wp-speed-test - Measure the load time of WordPress (PHP) page loads. Tests site front page by default, but other URLs can be given as an argument. Supports parameter --cache which will test how fast the sites load from front proxy (not PHP) if the tested URL supports HTTP level caching.  wp-load-test  wp-load-test - A simple command line tool to measure how many consecutive PHP requests the site can handle in a minute. Accepts same parameters as wp-speed-test.  wp-mail-test  wp-mail-test - A simple command line tool to test that that PHP mail() works as expected. Combine with mail-tester.com for most comprehensive results.  wp-optimize-images  wp-optimize-images - Optimize images on site. Can be given a path as a parameter but scans /data/wordpress/htdocs/wp-content/uploads/\ but default. Runs only if the seravo-enable-optimize-images value in the database is set to true. Reduces the resolution of all JPEG files according to maximum width and height saved in the database table wp_optionsasseravo-image-max-resolution-widthandseravo-image-max-resolution-width. Maximum image quality for JPEG is set to 90. Image quality for PNG files is set to 7. Prints the output to terminal and /data/log/wp-optimize-images.log.  wp-purge-cache  wp-purge-cache - Purge all server caches: Nginx proxy cache, WordPress object cache, WordPress rewrite cache and PageSpeed cache.  wp-reset-all-passwords  wp-reset-all-passwords - Reset passwords for all registered WordPress users. Automatically also resets sessions.  wp-reset-all-sessions  wp-reset-all-sessions - Reset sessions for all users, after which each user needs to login again.  wp-reset-ssh-password  wp-reset-ssh-password - Reset the SSH passwords. This is the only way to change the SSH password for a site at Seravo.  wp-seravo-plugin-update  wp-seravo-plugin-update - Update the must-use Seravo Plugin to the latest version. It also cleans up all legacy remnants of the wp-palvelu-plugin. Use --dev parameter to pull latest git master instead of the latest stable release.  Developer helpers  wp-list-env  wp-list-env - Print a list of defined environment variables. Both the Vagrant image and the production server contain ENVs which define ports and credentials for WordPress. With this command you can debug the settings that you have.  wp-makepot  wp-makepot - is a wrapper for php /opt/wordpress/i18n/tools/makepot.php. For more information on internationalization read the corresponding page on WordPress Codex.  wp-pomo-compile  wp-pomo-compile - Find .po files, process each with msgfmt and generate binary .mo files. Can be given a target path as an argument, otherwise the process is done on /data/wordpress/htdocs/wp-content/ and all subdirectories.  wp-restart-nginx  wp-restart-nginx - Restart Nginx. Does not require root permissions. Applies all configuration at /data/wordpress/nginx/*.conf.  wp-restart-php  wp-restart-php - Restart all PHP processes.  wp-shadow-pull  wp-shadow-pull - Replace files and database contents in your production environment with your chosen shadow environment. Use with caution, as it may break your site. Makes a backup for you automatically.  wp-shadow-reset  wp-shadow-reset - Replace files and database in a shadow environment with data from the production environment. Will delete and replace all files in the /data/wordpress/ directory of a shadow with a clone from production. Use with caution, as data from your shadow can not be recovered after this process.  wp-test  wp-test - Run the Codeception integration tests.  wp-watch-logs  wp-watch-logs - Print all new linest written to any of the logs under /data/log/. Press Ctrl+C to exit.  Database helpers  wp-db-optimize  wp-db-optimize - Run the CHECK and OPTIMIZE tasks for the WordPress database.  wp-db-cleanup  wp-db-cleanup - Remove older than current year post revisions from database. Use with caution, this is destructive operation. Operation is cancelable within 10 seconds by typing Ctrl+C.  wp-db-info  wp-db-info - Display database table size in bytes and wp_option record lenghts.  wp-db-dump  wp-db-dump - Dump current database into /data/db/. This file will normally be the daily backup database dump.  wp-db-load  wp-db-load - Replace current database with existing dump from /data/db/  wp-db-update  wp-db-update - Apply all pending wordpress database schema updates, if any  wp-db-admin  wp-db-admin - Access the MariaDB Proxy admin console  Vagrant commands      Note: These are only available inside the Vagrant box.    wp-xdebug-on/off  wp-xdebug-on and wp-xdebug-off - Activate and deactivates the Xdebug profiler. Run wp-xdebug-off if the Vagrant box seems sluggish to speed it up, as Xdebug can sometimes be quite heavy on the virtual machine load.  wp-ssh-production  wp-ssh-production - Log into production with SSH. Requires the config.yml to have the production section defined.  wp-pull-production-db  wp-pull-production-db - Copy the production database into local Vagrant box. Also replaces all production siteurls in the database with the local development siteurl based on the contents of config.yml.  wp-pull-production-plugins  wp-pull-production-plugins - Pull the plugins used in production. Uses rsync to make sure local wp-content/plugins is equal to the one in prodction.  Vagrant internal commands      Note: These commands are used by the Vagrantfile. These are not intended for manual invocation by any WordPress site developer.    wp-activate-git-hooks  wp-activate-git-hooks - Adds files to local .git/hooks for test automation on every local git commit.  wp-generate-ssl  wp-generate-ssl - Generate SSL self-signed certificates for domains defined in config.yml.  wp-use-asset-proxy  wp-use-asset-proxy - Set an asset proxy for the development environment based on config.yml.  wp-vagrant-activation  wp-vagrant-activation - Restart Nginx and the Avahi daemon, and generates domains mappings in /etc/hosts and configure production details to .ssh/config.  wp-vagrant-dump-db  wp-vagrant-dump-db - This is run everytime you halt or destroy the Vagrant box so that the database can be re-imported on the next vagrant up run. Database dump is stored in the local .vagrant directory.  wp-vagrant-import-db  wp-vagrant-import-db - This is run everytime you up the Vagrant box. It tries to import the dump file generated by wp-vagrant-dump-db so you can continue development where you left off."
    } ,
  
    {
      "title"    : "Configure Vagrant Box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/configure-vagrant-box/",
      "date"     : "2015-10-12 19:12:17 +0000",
      "content"  : "Example configuration of config.yml#### Configuration for Vagrant#### This is used as the hostname of the Vagrant boxname: wordpress# These are used for migrating database and uploads back and forth with production# Comment these out if you don't want this integrationproduction:  domain: example.seravo.com  ssh_port: 12345staging:  domain: example.seravo.com  ssh_port: 23456# Domains are automatically mapped to Vagrant with /etc/hosts modificationsdevelopment:  domains:    - wordpress.local    - example.dev    - www.example.dev  # If you want other sin your local network (e.g. office) to be able to access  # the site running on your laptop, activate Avahi / Bonjour / Zeroconf that  # will advertice *.local domains on the network.  avahi: trueChanging config.ymlnameChange name in config.yml to change your site name. This is used in quite a few places in the development environment.For example, with the above config.yml mailcatcher is set up in the address: mailcatcher.example.local.productionAdd domain and ssh_port to sync with your production instance.stagingOptional: Add domain and ssh_port to sync with your staging (testing shadow) instance.developmentAdd new domains under domains before you run vagrant up to use extra domains.See config-sample.yml for more."
    } ,
  
    {
      "title"    : "Deploy using Git",
      "category" : "deployment",
      "tags"     : "",
      "url"      : "/docs/deployment/deploy-using-git/",
      "date"     : "2015-10-12 18:05:02 +0000",
      "content"  : "Requirements  You need to setup your ssh settings first.  You need to have a git repository initialized on the server and a local copy of it as described in Local development.  You need to be in your project directory: cd ~/Projects/your-site/  You need to have production set as git remote:# Here we are using custom alias 'your-site' in ~/.ssh/config$ git remote -vproduction  your-site:/data/wordpress/.git (fetch)production  your-site:/data/wordpress/.git (push)# This is the output if project was cloned without ssh alias$ git remote -vproduction  ssh://your-site@your-site.seravo.com:12345/data/wordpress (fetch)production  ssh://your-site@your-site.seravo.com:12345/data/wordpress (push)Deploy using gitNote: This doesn’t deploy the database or the contents of uploads. We recommend that you don’t include these in git.$ git push production masterCounting objects: 3, done.Delta compression using up to 8 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 317 bytes | 0 bytes/s, done.Total 3 (delta 2), reused 0 (delta 0)remote: Seravo: composer.json was updated, installing...remote: Loading composer repositories with package informationremote: Installing dependencies from lock fileremote: Nothing to install or updateremote: Generating autoload filesremote: &gt; Wordpress/Installer::symlinkWPContentremote: Seravo: Nginx configs were changed, reloading nginx...remote: testing nginx configuration...remote: nginx: the configuration file /etc/nginx/nginx.conf syntax is okremote: nginx: configuration file /etc/nginx/nginx.conf test is successfulremote: restarting nginx...remote: nginx restarted!To your-site:/data/wordpress/.git   01b9b80..9b3d006  master -&gt; masterTutorialsIf you have a working site in the Vagrant box and you want to deploy itStep 1 - Getting credentialsFirst you need to order a site from seravo.com in order to get ssh credentials, which are described in the Configuring SSH section.Step 2 - Setting credentials# Go to your project folder$ cd Projects/your-site# Add new remote to git$ git remote add production ssh://your-site@your-site.seravo.com:12345/data/wordpress  Note: This is just an example. Use the real credentials for your site.Step 3 - Push into production# First push doesn't share anything with the fresh site so you need to force push it$ git push production master --forceFor deploying the database contents or the contents of the uploads folder you will need to roll your own solution which is safe enough to not overwrite any data created in production.  Note on wp-push- commands:In Seravo Vagrant images created before October 5th, 2016, there used to be the commands wp-push-production-db (deploy database to production) and wp-push-production-uploads (deploy wp-content/uploads into production) but they were deemed as too risky and removed to protect customers from accidentally making too much damage to their site."
    } ,
  
    {
      "title"    : "Data locations",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/data-locations/",
      "date"     : "2015-10-12 11:29:39 +0000",
      "content"  : "Base installationSeravo uses https://github.com/Seravo/wordpress as a base installation for all sites. If you have any problems with the template please submit an issue to Github.Data locationsAll of your data is located under the path /data/. During the updates of your site we will wipe away all unnecessary files and only preserve things in the /data/ directory. No need to worry though, because we have moved your home folder into /data/home/$USER/, for instance, and made correct symlinks so you won’t even notice it’s located somewhere else.Web root (htdocs)Your content is served from:/data/wordpress/htdocs/WordPress installation pathWordPress is installed in: /data/wordpress/htdocs/wordpress/WP-contentWP-content is moved outside of the wordpress directory into: /data/wordpress/htdocs/wp-content/WordPress uploadsIt is not recommended to store anything in wp-content/uploads in git. If you have images in your plugin or theme, store those files inside the plugin or theme directories. The uploads folder is intended only for user uploaded files (in production). The real production media files or database should not be tracked in git.When a site is developed in our Seravo Vagrant box, the uploads will automatically be visible via our special uploads asset proxy. It will fetch any media file from the production site not present in the development environment on-the-fly. It requires the Vagrant box to have a working Internet connection and the production server address to be defined in the project config.yml. To use the production database while developing with the Seravo Vagrant box, see the command wp-pull-production-db.Log filesAll logs are saved in: /data/log/Project StructureSeravo uses a custom directory layout which is derived from Bedrock. Bedrock is a WordPress layout which uses Composer for package management. It is not advisable to modify any WP core files, so usually your application consists only of what’s included in the wp-content -directory. When we use version control, it is much better to have your content separated to a folder which is separated from the core installation. Our custom template also includes tests, composer.json, custom Nginx rules and files for local development (Vagrantfile). Let’s take a closer look at the Project directory:/data/wordpress├── config.yml # See about Configuration above├── composer.json # Use composer for package handling├── composer.lock├── gulpfile.js # Example for using gulp├── Vagrantfile # Advanced vagrant environment and scripts packaged in Vagrantfile│├── tests # Here you can include tests for your WordPress instance│   └── codeception│       └── acceptance│           ├── console-whitelist.json # Your custom whitelists│           └── MyTestCept.php         # Your custom tests|├── nginx # Here you can have your custom modifications to nginx which are also used in production│   └── examples.conf # Some examples to get started│   └── anything.conf # Your own config files can be named anything *.conf.│├── scripts│   ├── hooks # Git hooks for your project│   │   ├── pre-commit # This is run after every commit│   │   └──│   ││   ├── wordpress│   │   └── Installer.php #Additional composer scripts│   ││   └── run-tests # Bash-script as an interface for your tests in Seravo Production and Dev environment│├── vendor # Composer packages go here└── htdocs # This is the web root of your site    ├── wp-content # wp-content is moved out of core    │   ├── mu-plugins    │   ├── plugins    │   ├── themes    │   └── languages    ├── wp-config.php    ├── index.php    └── wordpress # WordPress Core installed by composer        ├── wp-admin        ├── index.php        ├── wp-load.php        └── ...Migrating from BedrockPlain Bedrock has the following directory structure:/data/wordpress├── config|   ├── environments|   │   ├── production.php|   │   └── ...|   └── application.php├── vendor├── web|   ├── app|   │   ├── mu-plugins|   │   ├── plugins|   │   ├── themes|   │   └── uploads|   ├── index.php|   └── wp-config.php├── composer.json└── ...For this to continue to work, you need to add the following symlinks:ln -s web htdocsln -s app htdocs/wp-contentThe server expects to find the web root in /data/wordpress/htdocs with wp-contents under it containing the site-specific files and wordpress containing the WordPress core files. Alternatively, you can simply modify your composer.json to use the same paths as defined in the Seravo WordPress project template composer.json.Directory layout with Capistrano or other deploy toolsIf you use a deploy tool that deploys multiple versions of the files on the server, and then activates one of them by repointing a symbolic link, you could use a directory layout like this:/data/wordpress├── current -&gt; releases/20170323143417├── htdocs -&gt; releases/20170323143417/web├── nginx├── releases│   ├── 20170322075317│   ├── 20170322112722│   └── 20170323143417│      ├── config│       │   ├── deploy│       │   └── environments│       ├── public -&gt; web│       ├── vendor│       │   ├── composer│       │   └── vlucas│       └── web│           ├── app│           │   ├── languages -&gt; /data/wordpress/shared/web/app/languages│           │   ├── mu-plugins -&gt; /data/wordpress/shared/web/app/mu-plugins│           │   ├── plugins -&gt; /data/wordpress/shared/web/app/plugins│           │   ├── themes│           │   └── uploads -&gt; /data/wordpress/shared/web/app/uploads│           ├── wordpress -&gt; /data/wordpress/htdocs/wp│           ├── wp -&gt; /data/wordpress/shared/web/wp│           └── wp-content -&gt; /data/wordpress/htdocs/app└── shared    ├── config -&gt; releases/20170323143417/config    ├── vendor -&gt; releases/20170323143417/vendor    └── web        ├── app        │   ├── languages        │   ├── mu-plugins        │   ├── plugins        │   └── uploads        └── wp            ├── wp-admin            ├── wp-content            └── wp-includesGit repository locationsIt is intended that customers initialize a git repository in the /data/wordpress directory. For details, see Local development.If we detect that a git repository exists while doing upkeep and editing any project files, we will commit any changes so that it’s less likely that the changes would be lost or overwritten when the customer later does a redeploy."
    } ,
  
    {
      "title"    : "Using Git hooks",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/using-git-hooks/",
      "date"     : "2015-10-12 11:24:27 +0000",
      "content"  : "Git hooksBasically they are scripts which allow you to run custom actions automatically on every git commit.They exists in your repository .git/hooks/ and are not copied when pulling/pushing.Git hooks are explained really well in git documentation. Example hooks can be found on a Linux system at the path /usr/share/git-core/templates/hooks/.Basically the hooks are just shell scripts and it should be fairly easy for any developer to customize them.During development: pre-commit hookThe pre-commit hook script is run by git on every commit attempt. If the hook returns any exit code whatsoever, the commit is aborted and the developer alerted that they need to fix their code before a commit can be accepted.The purpose of this hooks is to help with quality control, preventing developers from committing code that has clear mistakes in them.Our WordPress project template contains one custom git hook example you can find in scripts/hooks/pre-commit.By default our example pre-commit hooks script runs php -l to check if there are any syntax errors in the modified PHP files, and then it runs the Codeception tests to check that the integration tests pass.How to temporarily skip the pre-commit hookYou can use -n flag to skip hooks in case you are sure you want to make a commit and specifically want to ignore the result of any quality assurance and testing done by the git pre-commit hook.$ git commit -n -m "commit message"During deployment: post-receive hookWhen a developer runs git push production or similar to push the code to the remote server, the remote server will trigger the hook .git/hooks/post-receive. At Seravo this hooks is preinstalled and contains the following:#!/bin/bash### This is run after successful push to this repo# Useful for automating grunt work using local development##### This script can be called from anywhere so it's good to be in the correct location# This can also be called in .git/hooks dir and we need to get into project root##cd "$( dirname "${BASH_SOURCE[0]}" )"/../..# Hack: Somehow this script won't understand that git is in current directoryGIT_DIR="$(pwd)/.git"# Loop through all changed fileschanged_files=$(git diff --name-only --diff-filter=ACM HEAD HEAD^)# Check files which have triggerswhile read -r line; do    if [ "$line" = "composer.json" ] || [ "$line" = "composer.lock" ]; then      COMPOSER_CHANGED=true    elif [ "$line" = "nginx/*.conf" ]; then      NGINX_CHANGED=true    fidone &lt;&lt;&lt; "$changed_files"# Do stuff with the triggersif $COMPOSER_CHANGED; then  echo "Seravo: composer.json was updated, installing..."  composer install --no-devfiif $NGINX_CHANGED; then  echo "Seravo: Nginx configs were changed, reloading nginx..."  wp-restart-nginxfiThis script will see if the composer.json or any files in nginx/*.conf were modified in the commit, and if that was the case it will run composer install or reload Nginx.Note that while there is a post-receive hook on the server, it’s not included in the project template as it’s useless (and potentially even harmful) to have in your local copy. For details see Deploy using git.Testing git hooksSince the git hooks are just regular scripts, you can easily test them by simply running them e.g. like this:$ /data/wordpress/.git/hooks/post-receiveSeravo: composer.json was updated, installing...Loading composer repositories with package informationInstalling dependencies from lock fileNothing to install or updateGenerating autoload files&gt; WordPress\Installer::symlinkWPContentSeravo: Nginx configs were changed, reloading nginx...testing nginx configuration...nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successfulNginx restarted!One common reason for scripts to fail is the lack of the executable bit. That is easily fixed by chmod +x /data/wordpress/.git/hooks/post-receive."
    } ,
  
    {
      "title"    : "Composer - Plugins &amp; Themes",
      "category" : "configuration",
      "tags"     : "",
      "url"      : "/docs/configuration/composer/",
      "date"     : "2015-10-11 03:46:06 +0000",
      "content"  : "What is it?  Composer is a dependency manager for PHP that has been gaining popularity lately. Your first question is most likely “what is a dependency manager and why do I need one?”. Almost any code you write probably ends up depending on 3rd party libraries. All of these libraries (projects, frameworks, files, etc) become dependencies of your project. Composer lets you declare the dependencies for a project and it will install and manage them.  Source: roots.ioHow to use Composer with WordPressExampleLet’s look at simplified version of our composer.json as an example:{  "repositories": [    {      "type": "composer",      "url": "http://wpackagist.org"    }  ],  "require": {    "php": "&gt;=5.3.2",    "johnpbloch/wordpress": "*",    "wpackagist-plugin/wordpress-seo": "*",    "wpackagist-theme/twentyfifteen": "*"  },  "extra": {    "installer-paths": {      "htdocs/wp-content/plugins/{$name}/": ["type:wordpress-plugin"],      "htdocs/wp-content/mu-plugins/{$name}/": ["type:wordpress-muplugin"],      "htdocs/wp-content/themes/{$name}": ["type:wordpress-theme"]    },    "wordpress-install-dir": "htdocs/wordpress"  }}ExplanationRepositories section:{  "repositories": [    {      "type": "composer",      "url": "http://wpackagist.org"    }  ]}Repositories section tells Composer where to look for your packages. In order for Composer to find your beloved WordPress plugins and themes you’ll need to point it to wpackagist.org, which is a mirror of all the plugins and themes that you can download from wordpress.org. Here you can add any custom repositories you may have, but for WordPress you’ll need to use wpackagist.Require section:{  "require": {    "php": "&gt;=5.3.2",    "johnpbloch/wordpress": "*",    "wpackagist-plugin/wordpress-seo": "*",    "wpackagist-theme/twentyfifteen": "*"  }}Require section tells Composer the minimum PHP version needed and all the packages to install."johnpbloch/wordpress": "*" means newest available version of WordPress."wpackagist-plugin/wordpress-seo": "*" means that we need plugin WordPress Seo"wpackagist-theme/twentyfifteen": "*" means that we need theme TwentyfifteenExtra section:{  "extra": {    "installer-paths": {      "htdocs/wp-content/plugins/{$name}/": ["type:wordpress-plugin"],      "htdocs/wp-content/mu-plugins/{$name}/": ["type:wordpress-muplugin"],      "htdocs/wp-content/themes/{$name}": ["type:wordpress-theme"]    },    "wordpress-install-dir": "htdocs/wordpress"  }}By default Composer installs everything to vendor directory in the root of your project. Luckily for us wpackagist uses a clever plugin composer/installers that allows us to have custom installation paths. In Extra section we can define installation paths for different types of packages.Adding your own plugins and themesThe best way to develop custom plugins and themes is to add them into their own repositories and install them by Composer.You can do this by adding a composer.json file for into your plugin/theme repo:{  "name": "your-name/custom-plugin",  "type": "wordpress-plugin",  "license": "GPLv3",  "description": "Plugin description",  "homepage": "https://github.com/your-name/custom-plugin"}…and then requiring it in your project like this:{  "repositories": [    {        "type": "vcs",        "url": "https://github.com/your-name/custom-plugin.git"    }  ],  "require": {      "your-name/custom-plugin": "*"  }}This way you can use plugins and themes from Github or Bitbucket.Paid WordPress plugins and ComposerBy default Composer only fetches modules that are publicly available on Github. For paid WordPress plugins that are not freely available, there are basically two alternative approaches. Either you can include them in our project repository and distribute them together with the project code, or you can get a paid account from Packagist.com that includes the ability to have private Composer packages.More informationYou can find more documentation on getcomposer.org"
    } ,
  
    {
      "title"    : "Manage with wp-cli",
      "category" : "management",
      "tags"     : "",
      "url"      : "/docs/management/use-wordpress-with-wpcli/",
      "date"     : "2015-10-11 03:39:48 +0000",
      "content"  : "BasicsYou can use wp-cli in Vagrant box and Production. Just run this to see all commands:$ wp --help  Note: You can call wp command anywhere without --path parameter because the environment handles paths for you.More information about wp-cli can be found in wp-cli.orgUseful commandsDatabase export/import (dumping db)# Dumps the database to a provided filename$ wp db export your-dump-filename.sql# Imports the dump file from file$ wp db import your-dump-filename.sqlCreate userThis creates a new administrator user ‘admin’ with password ‘admin’:$ wp user create admin admin@wordpress.dev --user_role=administrator --user_pass=adminSearch-Replace database contentsAfter you have pulled database from production it’s useful to regex replace urls:$ wp search-replace http://original.com http://original.dev"
    } ,
  
    {
      "title"    : "Vagrant box",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/vagrant-box/",
      "date"     : "2015-10-11 03:32:14 +0000",
      "content"  : "Develop locally with VagrantYou can do development for your site locally in your own computer. This is done using Vagrant.All of our sites are pre-configured with a Vagrantfile so you can just clone your site to your computer and start developing locally. We have mimicked the production environment as much as possible and bundled many development tools into the Vagrant image.The virtual machine image is based on Ubuntu and it can be downloaded from Hashicorp Atlas.Contributing to the Vagrant imageOur project template can be found on Github: Seravo/WordPress.Please file an issue or make a pull request if you have suggestions how to make our Vagrant box and development environment even better.Requirements  Knowledge of using terminal  Vagrant 1.7.4 or later installed  Virtualbox 4.2 or later installedTested up to  Vagrant 2.0  Virtualbox 5.2Debugging Vagrant issuesUnfortunately Vagrant is somewhat brittle and many people experience issues with it from time to time. Here are some general tips on how to debug if Vagrant isn’t working for you:If you experience slownessRun vagrant global-status and check that you don’t have too many Vagrant images running in parallel. You can also fire up the VirtualBox main window to get an overview what VirtualBox machines are active on your system.Inside the Vagrant box you may also want to try turning of the Xdebug profiler by running wp-xdebug-off. Xdebug can sometimes be quite heavy even when running in the background.  Note: The Vagrant box will never be as fast as the live production site. This is intentional and helps WordPress site developers detect performance issues before they go into production. Both our Vagrant box and testing shadows have less available resources than the real production site, so that even a single user (the developer testing his/her code) might notice performance issues if the code has severe bugs. If the development environments are too powerful, badly performing code will go undetected into production.If you have startup issuesIf during startup there are issues, you can simply try again a few times running vagrant up. If your Vagrant box is already running and you run vagrant up again it does no harm.You can also see more verbose output during the startup if you run with vagrant up --debug.If you experience SSH connection errorsTry deleting the previous Vagrant box and try fresh again. It is fully OK, since the Vagrant boxes are intended to be throw-away development and testing boxes. All of your valuable data is stored in your project folder anyway. First the Vagrant box with vagrant destroy, then delete the all settings related to it rm -rf .vagrant. With git status --ignored you can see what else lies in your project directory and evaluate if you want to remove it as well or not. You should also check that the /etc/hosts file does not have any remnants pointing domain names to VirtualBox machines that no longer exists.Once you are sure you have cleaned away all of the old box, then start fresh with vagrant up.Compare to vanilla versionTo rule out if a problem is related to the Seravo Vagrant box in general, or just a particular instance, you can try firing up a vanilla instance and see if the error reproduces with it as well:git clone https://github.com/Seravo/wordpress.git testcd testvagrant upCompate to another releaseTo make sure you have the latest version of our Vagrant box, please run vagrant box update. Hopefully any bugs we have had are fixed in the latest version. You can also test our next release in advance by replacing in the Vagrantfile the line config.vm.box = 'seravo/wordpress' with config.vm.box = 'seravo/wordpress-beta'.Compare to VVVTo rule out if a problem is related to the Vagrant in general, or just a the Seravo Vagrant box, you can try firing up a vanilla instance of VVV and see if the error reproduces with it as well:git clone https://github.com/Varying-Vagrant-Vagrants/VVV.git VVVcd VVVvagrant upThe VVV project also has some good docs on typical Vagrant problems and how to troubleshoot them.Cleaning away excess VirtualBox boxes/files and local network interfacesVirtualBox seems to create a new network interface on every invocation of vagrant up which is not removed on vagrant destroy, and thus lots of VBoxnet interfaces keep accumulating. You can clean up VirtualBox cruft with these steps:  Run vagrant destroy in all your project folders to stop and delete all Vagrant boxes. It is safe, since you can later restart boxes with identical content with vagrant up thanks to the automatic WP database exports and imports.  Run vagrant global-status --prune to ensure no Vagrant boxes exist anymore.  Clean up outdated and exess boxes from your system with vagrant box prune and vagrant box outdated --global. You can always download new ones from the Vagrant cloud, and you most likely will do that anyway for all images that were anyway already outdated on your local computer.  You can review the list of boxes with vagrant box list and manually remove more boxes you are sure you don’t need anymore (or are willing to re-download later). Check the disk space consumption of ~/.vagrant.d/ to see which images are biggest.  Remove all temporary files with rm -rf ~/.vagrant.d/tmp/*.  Fire up the VirtualBox UI and check that the list of virtual machines is empty.  Delete all virtual network interfaces with this one-liner: for x in {1..99}; do VBoxManage hostonlyif remove vboxnet$x; done"
    } ,
  
    {
      "title"    : "Compile assets &amp; automate with Gulp",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/gulp/",
      "date"     : "2015-10-11 03:30:52 +0000",
      "content"  : "Using GulpYou can use Gulp to automatically minify and compile your assets. This includes turning SASS to CSS or CoffeeScript to JavaScript. Gulp can also optimize your images and do a multitude of other things. With Browsersync it can even reload your browser windows across multiple devices automatically.  Known Browsersync failure in latest release: Due to architectural changes in Seravo’s Vagrant box, Browsersync does currently not work out-of-the-box. Everything else in the gulp watchfile however works.Default Gulp settings require an adaptation to watch for your themeThe Seravo template contains example gulpfile.js. Our example compiles sass files from twentyfifteen theme. It also has Browsersync already configured for the theme twentyfifteen so you can use it as base for your own projects.First, install the dependencies specified in /data/wordpress/package.json:$ cd /data/wordpress/ &amp;&amp; npm installAfter this, Browsersync can be started by running:$ cd /data/wordpress/ &amp;&amp; gulp watchWhen it’s started the internal web server (Nginx) automatically reroutes all traffic through Browsersync so that you can have a nice live reload effect while developing.Example file/* * This is default barebone for gulp, add your own modifications here * It also serves as an example of how to use browser-sync in this environment */var gulp        = require('gulp');var browserSync = require('browser-sync');var sass        = require('gulp-sass');var reload      = browserSync.reload;/* * src array contains the files which the gulp will be watching * Choose your theme over here (twentyfifteen is provided as an example) */var src = {    scss: 'htdocs/wp-content/themes/twentyfifteen/scss/*.scss',    css:  'htdocs/wp-content/themes/twentyfifteen/css/',    php: [        'htdocs/wp-content/themes/*/*.php',        'htdocs/wp-content/plugins/*/*.php',        'htdocs/wp-content/mu-plugins/*/*.php'    ]};// Serve all files through browser-syncgulp.task('serve', ['sass'], function() {    // Initialize browsersync    // Nginx is configured to use any service in port 1337    // as middleware to WordPress in vagrant environment    browserSync.init({        // browsersync with a PHP server        proxy: "http://localhost:8080",        port: 1337,        ui: {            port: 1338        },        notify: true    });    // Watch sass files and compile them    gulp.watch(src.scss, ['sass']);    // Update the page automatically if php files change    gulp.watch(src.php).on('change', reload);});// Give another name for servegulp.task('watch',['serve'], function() {});// Compile sass into CSSgulp.task('sass', function() {    return gulp.src(src.scss)        .pipe(sass())        .pipe(gulp.dest(src.css))        .pipe(reload({stream: true}));});// Default task just compiles everything// This is run when site is deployed!gulp.task('default', ['sass'], function() {});"
    } ,
  
    {
      "title"    : "Debug mails with MailCatcher",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/mailcatcher/",
      "date"     : "2015-10-11 03:30:42 +0000",
      "content"  : "MailCatcherMailCatcher is a web app which can be used to mimic a real-life mail server. It’s used in Vagrant to prevent WP from sending mail out of the development environment to actual recipients. It simply tells WordPress that mail was sent successfully and saves the contents for the developer to see.You can find MailCatcher at the address: mailcatcher.{sitename}.localBy default it is: mailcatcher.wordpress.local"
    } ,
  
    {
      "title"    : "Profile runtime with Xdebug &amp; Webgrind",
      "category" : "development",
      "tags"     : "",
      "url"      : "/docs/development/xdebug/",
      "date"     : "2015-10-11 03:30:24 +0000",
      "content"  : "  Note: Xdebug is only for development sites. Use Tideways on live production websites.What is Xdebug?Xdebug is a debugger and profiler for PHP. We mainly use it as profiler and it’s installed in the Vagrant box by default.Profiling pages with Xdebug and WebgrindYou can profile any page in Vagrant by visiting them and using the ?XDEBUG_PROFILE paramater in the url.This will generate a new dbkg dump which you can the analyze in your browser using webgrind.Example: Profile WP admin dashboard  Enter the Vagrant box with vagrant ssh and inside it run wp-xdebug-on to ensure Xdebug is active.  Visit: http://wordpress.local/wp-admin/?XDEBUG_PROFILE  Visit http://webgrind.wordpress.local/  Click update button and wait for webgrind to analyze the dump.  You can see the profiling of the admin page and look up slow functions which you can then optimize.Using the Xdebug Helper Chrome extensionProfiling and traces can easily also be triggered using the Xdebug Helper Chrome extension. For optimal operation use the settings:  IDE key: vagrant  Trace Trigger Value: XDEBUG_TRACE  Profile Trigger Value: XDEBUG_PROFILEUsing remote debugging with XdebugXdebug can also be used for remote live debugging with breakpoints and all. Please refer to your IDE for more information. A good example is the Atom php-debug plugin documentation on the topic."
    } ,
  
    {
      "title"    : "Integration tests using Rspec",
      "category" : "tests",
      "tags"     : "",
      "url"      : "/docs/tests/integration-tests/",
      "date"     : "2015-10-11 03:28:22 +0000",
      "content"  : "  Deprecated: The legacy Rspec-based test system described on this page has been replaced by our a new testing system based on headless Chrome and Codeception. This page is kept purely for historical reference and will be deleted later in 2019..  For more information see new testing system announcement at Seravo.com.What are integration tests?Integration tests make sure that certain features of your site work as they should.For example if we have the following use case:  User visits your-site.com/wp-login.php and sees the login form.  When user fills correct password and username he sees the WordPress dashboard including adminbar.When the project is functional we will have the feature described above. Integration tests can be used to make sure that the feature works as described in the use case.Testing with Rspec &amp; CapybaraOur integration tests use a Ruby testing framework called Rspec with the extension Capybara.We use the headless browser PhantomJS with the Ruby driver Poltergeist.Using Ruby for testing a PHP application like WordPress may sound overwhelming but in our opinion it’s quite fun and effective. Our latest WordPress baseline template can be found in Github. It can be used as an example for your own unique tests.  Note: These tests are used in your production system as well (if available).  This way we can figure out if the site is still working after updates so that we can alarm you when something breaks and hand the updating process to be manually by the owner.How to run these testsYou can use this command in Production and Vagrant box:# Runs all tests in /data/wordpress/tests/rspec/*.rb$ wp-test-legacyExample testsThe following test suite consists of two describe blocks.The first one tests that the front page is loaded correctly and has CSS styles. Then it clicks a link in the front page and expects the following page to contain text Archives.The second one tests the use case we talked about in the beginning of this page.# Use preconfigured Poltergeist/PhantomJS rules and load WP classrequire_relative 'lib/config.rb'### Begin tests ###describe "WordPress: #{WP.host} - ", :type =&gt; :request, :js =&gt; true do  subject { page }  describe "frontpage" do    before do      visit WP.siteurl('/')    end    it "Healthy status code 200" do      expect(page).to have_status_of [200]    end    it "Page includes stylesheets" do      expect(page).to have_css    end    it "After user clicks archive link, User should see archives" do      click_link('October 2015')      expect(page).to have_content 'Archives'    end  end  describe "admin-panel" do    before do      visit WP.siteurl('/wp-login.php')    end    it "There's a login form" do      expect(page).to have_id "wp-submit"    end    # Only try logging in if we could create a test user    if WP.user?      it "Logged in to WordPress Dashboard" do        within("#loginform") do          fill_in 'log', :with =&gt; WP.user.username          fill_in 'pwd', :with =&gt; WP.user.password        end        click_button 'wp-submit'        # Should obtain cookies and be able to visit /wp-admin        expect(page).to have_id "wpadminbar"      end    end  endendHow to extend the testsAll files located in the path tests/rspec/*.rb will be executed. Instead of editing the existing baseline tests, we recommend creating new files for your own tests. Group the tests that test the same features and try to name the files logically to make it easier for your collaborators to debug or extend the tests later.To try your new test, run it individually with verbose output:rspec -f d new-test.rbRspec also has a profiling option available if you want to measure how long the test takes and potentially detect some execution time anomalies:rspec -f d -p 10 new-test.rbList of Helper functionsWP Helper moduleThese tests use the helper module WP which is included in the project.# Returns url to your site for the following {path}# @return string - url to your siteWP.siteurl(path)WP.url(path)# Returns the hostname/domain which is defined for WordPress# @return string - hostname of WordPressWP.hostname()WP.host()# Check if a testuser was created successfully# @return boolWP.user?# Returns the user object# @return User objectWP.user# User has following attributes:WP.user.username    # Username for the WordPressWP.user.password    # Password for the WordPressWP.user.firstname   # Test user firstname - by default: TestWP.user.lastname    # Test user lastname - by default: SeravoWP.user.email       # Test user email - by default: testbotuser@{your-site}List of Capybara functions# Navigatingvisit('/projects')visit(post_comments_path(post))# Clicking links and buttonsclick_link('id-of-link')click_link('Link Text')click_button('Save')click('Link Text') # Click either a link or a buttonclick('Button Value')# Interacting with formsfill_in('First Name', :with =&gt; 'John')fill_in('Password', :with =&gt; 'Seekrit')fill_in('Description', :with =&gt; 'Really Long Text…')choose('A Radio Button')check('A Checkbox')uncheck('A Checkbox')attach_file('Image', '/path/to/image.jpg')select('Option', :from =&gt; 'Select Box')# Scopingwithin("//li[@id='employee']") do  fill_in 'Name', :with =&gt; 'Jimmy'endwithin(:css, "li#employee") do  fill_in 'Name', :with =&gt; 'Jimmy'endwithin_fieldset('Employee') do  fill_in 'Name', :with =&gt; 'Jimmy'endwithin_table('Employee') do  fill_in 'Name', :with =&gt; 'Jimmy'end# Queryingpage.has_xpath?('//table/tr')page.has_css?('table tr.foo')page.has_content?('foo')page.should have_xpath('//table/tr')page.should have_css('table tr.foo')page.should have_content('foo')page.should have_no_content('foo')find_field('First Name').valuefind_link('Hello').visible?find_button('Send').clickfind('//table/tr').clicklocate("//*[@id='overlay'").find("//h1").clickall('a').each { |a| a[:href] }# Scriptingresult = page.evaluate_script('4 + 4');# Asynchronous JavaScriptclick_link('foo')click_link('bar')page.should have_content('baz')page.should_not have_xpath('//a')page.should have_no_xpath('//a')# XPath and CSSwithin(:css, 'ul li') { ... }find(:css, 'ul li').textlocate(:css, 'input#name').valueCapybara.default_selector = :csswithin('ul li') { ... }find('ul li').textlocate('input#name').value# Source: https://gist.github.com/zhengjia/428105."
    } ,
  
    {
      "title"    : "Configure SSH",
      "category" : "get-started",
      "tags"     : "",
      "url"      : "/docs/get-started/configure-ssh/",
      "date"     : "2015-10-10 22:20:23 +0000",
      "content"  : "Obtaining your credentialsAfter ordering your instance from Seravo.com you’ll get an email which includes your credentials. It looks something like this:SSH/SFTP:Host: example-site.seravo.comPort: 12345User: example-sitePass: 123456abcdefgh  Important: Use your own credentials instead of these. These are just an example and won’t work!You can use these credentials to log into your website server using a console connection program that supports SSH or a file transfer program that supports SFTP. Note that Seravo only allows the use of encrypted connections and for example the insecure legacy FTP method is forbidden. As a WordPress site developer you should be familiar with using SSH and SFTP and the basics about them is not covered in our documentation.Using the above credentials with OpenSSH on the command line would look like this:my-laptop$ ssh -p 12345 example-site@example-site.seravo.comThe authenticity of host '[example-site.seravo.com]:12345 ([185.26.50.24]:12345)' cant be established.RSA key fingerprint is xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy:xy.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '[example-site.seravo.com]:12345' (RSA) to the list of known hosts.|----------------------------------------------------------------------------|| Welcome to Seravo.com!                                                     || Unauthorized use is forbidden. All connections are monitored and recorded. ||----------------------------------------------------------------------------|example-site@example-site.seravo.com's password:Last login: Mon Oct 29 09:58:59 2018 from 172.17.42.1Documentation for WordPress developers at: https://seravo.com/docs/example-site@example-site_a1b2c3:~$Note that the first time you connect to a new SSH server it will ask you to verify the fingerprint of the server. On the first connection it is OK to accept whatever key it presents. On any consecutive connections it will use the same fingerprint. However, if the fingerprint suddenly changes, it can be a sign that there is a man-in-the-middle attack going on.Using SSH key authenticationWhen frequently using SSH it can be annoying to constantly have to enter the password. To make your workflow significantly smoother as a developer, please use SSH keys to authenticate instead of the password.Generate a SSH key pairSSH keys use public key cryptography, which means that there is one key to encrypt, and one to decrypt. The other key is secret, and the other public. With OpenSSH, the keys are generated using the command ssh-keygen -t ed25519. Running it will generate a pair of keys which with the default settings are named id_ed25519 (private) and id_ed25519.pub (public) and placed the directory .ssh in your home directory. If you have an old version of SSH your keys might be named id_rsa and id_rsa.pub. Modern elliptic key cryptography is recommended. Check out Seravo’s Linux blog article on how to create good SSH keys.You need to make sure you never loose or leak your private key! The public key is the one you install on remote servers you want to access using keys. The private key never leaves your own computer and is used only to decrypt challenges sent by the remote server, proving to the remote server that you are the holder of the private key.See also Github’s article on generating ssh keysInstall your public key on the remote serverTo install your public key on a remote server, the easiest way using the credentials above would be to run:my-laptop$ ssh-copy-id -p 12345 example-site@example-site.seravo.comNumber of key(s) added: 1Now try logging into the machine, with:   "ssh example-site.seravo.com"and check to make sure that only the key(s) you wanted were added.When you are done installing your SSH key, test that it works and you actually can SSH into the server without being prompted for a password.  OS X users: You can install ssh-copy-id with Homebrew:  $ brew install ssh-copy-id    Windows users: Install your ssh key through Putty instead. Cygwin is also a great choice for all your terminal tools (ssh, rsync, git..) under Windows.If ssh-copy-id does not work for you, it is also possible to manually install them simply by editing the .ssh/authorized_keys file on the remote server, and add there your public key as a new line in the file.my-laptop$ cat ~/.ssh/id_ed25519.pubasdfasdfasdfasdfasdfasdfasdfmy-laptop$ ssh ssh -p 12345 example-site@example-site.seravo.comexample-site@example-site_a1b2c3:~$ nano ~/.ssh/authorized_keys # add the line 'asdfasdfasdfasdfasdfasdfasdf' at the end of the file # then press Ctrl+X to save and exitexample-site@example-site_a1b2c3:~$ exit # terminate connectionIf you do this manually, also make sure the .ssh folder has permission attributes drwx------ and that the authorized_keys has permission attributes -rw-------.Configuring SSHAnother typical thing developers do to make their workflow more efficient is to store their SSH remote server settings in the configuration file at ~/.ssh/config. With will remove the need to remember and enter manually port numbers, and it will also make the ssh command support tab auto-completion so there is less need to even remember the server name itself.Add following lines to ~/.ssh/config:Host example-site  HostName example-site.seravo.com  User example-site  Port 12345  ForwardAgent yes  Optional: ForwardAgent yes - allows you to use your ssh credentials in the production machine to access private code from Github or Bitbucket.Using Vagrant SSHKeep in mind that when using use the vagrant ssh command to enter your Vagrant box, the SSH ForwardAgent is automatically enabled and your personal keys are used to access the production environment. These are used for wp-ssh-production or wp-pull-production-db. The Vagrant image itself does not contain any private SSH keys."
    } 
  
  ,
  
   {
     
        "title"    : "404 - Not found",
        "category" : "",
        "tags"     : "",
        "url"      : "/docs/404/",
        "date"     : "",
        "content"  : "Sorry, the page you tried to open does not exist =(Try using the Search."
     
   } ,
  
   {
     
        "title"    : "WordPress Developer Documentation",
        "category" : "",
        "tags"     : "",
        "url"      : "/docs/",
        "date"     : "",
        "content"  : "  Note: This documentation is primarily intended for Seravo.com customers. You can however use our project structure &amp; Vagrant box for any WordPress development no matter where you decide to deploy, host or maintain it. As stated in LICENSE (GPL), the provided software and documentation is open source and can be used freely anywhere, including also commercial use.This contains guides and tips for developers who might want to enhance their workflow and use the best modern development tools. Feel free to pick the parts that fit your style of development best!All Seravo.com sites are preconfigured for optimal development workflow, but the workflow in general or any part of it is not mandatory. You don’t have to use Git, Composer or the others if you don’t want. Or you can use them, but follow your own workflow. All sites have SFTP/SSH access so all sites support even the most traditional FTP-your-stuff-to-the-server-workflow. But if you like the idea of a really optimized workflow, please keep on reading!This documentation is maintained in a public git repository. If you find any errors or you want to extend the documentation, feel free to contribute!Fast-track guide for developing your site:  Hint: Read the other topics to have better understanding what’s happening over here. The git repository must be initialized as described in Local development.# Clone your site to your computer and name the git remote as 'production'$ git clone ssh://$SSH_USER@$SITE.seravo.com:[$SSH_PORT]/data/wordpress ~/Projects/$SITE --origin production# (Alternatively clone github.com/Seravo/wordpress and use it as your project template)# Start developing your site with Vagrant$ cd ~/Projects/$SITE$ vagrant up# Follow the vagrant installer...# Make changes to your site...# Save your work into git$ git commit -am "Made some superb changes"# See if commit triggered tests are succesful...# Push your changes to production$ git push production masterCounting objects: 3, done.Delta compression using up to 8 threads.Compressing objects: 100% (3/3), done.Writing objects: 100% (3/3), 317 bytes | 0 bytes/s, done.Total 3 (delta 2), reused 0 (delta 0)remote: Seravo: composer.json was updated, installing...remote: Loading composer repositories with package informationremote: Installing dependencies from lock fileremote: Nothing to install or updateremote: Generating autoload filesremote: &gt; Wordpress\Installer::symlinkWPContentremote: Seravo: Nginx configs were changed, reloading nginx...remote: testing nginx configuration...remote: nginx: the configuration file /etc/nginx/nginx.conf syntax is okremote: nginx: configuration file /etc/nginx/nginx.conf test is successfulremote: restarting nginx...remote: nginx restarted!To ssh://example@example.seravo.com:12345/data/wordpress/.git   01b9b80..9b3d006  master -&gt; master"
     
   } ,
  
   {
     
   } ,
  
   {
     
   } ,
  
   {
     
   } ,
  
   {
     
   } ,
  
   {
     
        "title"    : "Simple-Jekyll-Search",
        "category" : "",
        "tags"     : "",
        "url"      : "/docs/bower_components/simple-jekyll-search/",
        "date"     : "",
        "content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts' content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts' content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```"content"  : "{{ post.content | strip_html | strip_newlines }}"```to `search.json` after the `"date"` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById('search-input'),  resultsContainer: document.getElementById('results-container'),  json: '/search.json',})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      "title"    : "Welcome to Jekyll!",      "category" : "",      "tags"     : "",      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",      "date"     : "2014-11-01 21:07:22 +0100"    }]```### noResultsTextThe HTML that will be shown if the query didn't match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace 'search.json' with the following code:```---layout: null---[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}",      "content"  : "{{ post.content | strip_html | strip_newlines }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        "title"    : "{{ page.title | escape }}",        "category" : "{{ page.category }}",        "tags"     : "{{ page.tags | join: ', ' }}",        "url"      : "{{ site.baseurl }}{{ page.url }}",        "date"     : "{{ page.date }}",        "content"  : "{{ page.content | strip_html | strip_newlines }}"     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn't working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```"content"  : "{{ page.content | strip_html | strip_newlines }}"```with```"content"  : "{{ page.content | strip_html | strip_newlines | remove_chars | escape }}"```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission 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."
     
   } ,
  
   {
     
   } ,
  
   {
     
   } 
  
]

If search isn’t working due to invalid JSON

  • There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use remove_chars as a filter.

For example: in search.json, replace

"content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts' content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts' content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```"content"  : "{{ post.content | strip_html | strip_newlines }}"```to `search.json` after the `"date"` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById('search-input'),  resultsContainer: document.getElementById('results-container'),  json: '/search.json',})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      "title"    : "Welcome to Jekyll!",      "category" : "",      "tags"     : "",      "url"      : "/jekyll/update/2014/11/01/welcome-to-jekyll.html",      "date"     : "2014-11-01 21:07:22 +0100"    }]```### noResultsTextThe HTML that will be shown if the query didn't match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace 'search.json' with the following code:```---layout: null---[  {% for post in site.posts %}    {      "title"    : "{{ post.title | escape }}",      "category" : "{{ post.category }}",      "tags"     : "{{ post.tags | join: ', ' }}",      "url"      : "{{ site.baseurl }}{{ post.url }}",      "date"     : "{{ post.date }}",      "content"  : "{{ post.content | strip_html | strip_newlines }}"    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        "title"    : "{{ page.title | escape }}",        "category" : "{{ page.category }}",        "tags"     : "{{ page.tags | join: ', ' }}",        "url"      : "{{ site.baseurl }}{{ page.url }}",        "date"     : "{{ page.date }}",        "content"  : "{{ page.content | strip_html | strip_newlines }}"     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn't working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```"content"  : "{{ page.content | strip_html | strip_newlines }}"```with```"content"  : "{{ page.content | strip_html | strip_newlines | remove_chars | escape }}"```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission 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."

with

"content"  : "Simple-Jekyll-Search====================[![Build Status](https://travis-ci.org/christian-fei/Simple-Jekyll-Search.svg?branch=master)](https://travis-ci.org/christian-fei/Simple-Jekyll-Search)A JavaScript library to add search functionality to any Jekyll blog.---idea from this [blog post](https://alexpearce.me/2012/04/simple-jekyll-searching/#disqus_thread)---### Promotion: check out [Pomodoro.cc](https://pomodoro.cc/)# [Demo](http://christian-fei.github.io/Simple-Jekyll-Search/)# Getting started- Place the following code in a file called `search.json` in the **root** of your Jekyll blog. This file will be used as a small data source to perform the searches on the client side:```------[  {% for post in site.posts %}    {      &quot;title&quot;    : &quot;{{ post.title | escape }}&quot;,      &quot;category&quot; : &quot;{{ post.category }}&quot;,      &quot;tags&quot;     : &quot;{{ post.tags | join: &#39;, &#39; }}&quot;,      &quot;url&quot;      : &quot;{{ site.baseurl }}{{ post.url }}&quot;,      &quot;date&quot;     : &quot;{{ post.date }}&quot;    } {% unless forloop.last %},{% endunless %}  {% endfor %}]```- configure the library ( [options](#options) )### Enabling full-text searchNote that the index generated in `search.json` does not include the posts&#39; content since you may not want to load the whole content of your blog in each single page. However, if some of you want to enable full-text search, you can still add the posts&#39; content to the index, either to the normal search, or on an additional search page with a dedicated second index file. To do this, simply add```&quot;content&quot;  : &quot;{{ post.content | strip_html | strip_newlines }}&quot;```to `search.json` after the `&quot;date&quot;` line to which you must add a comma (`,`).# Install with bower```bower install simple-jekyll-search```# SetupYou need to place the following code within the layout where you want the search to appear.For example in  **_layouts/default.html**:``````# OptionsCustomize SimpleJekyllSearch by passing in your configuration options:```SimpleJekyllSearch({  searchInput: document.getElementById(&#39;search-input&#39;),  resultsContainer: document.getElementById(&#39;results-container&#39;),  json: &#39;/search.json&#39;,})```The above initialization needs to occur after the inclusion of `jekyll-search.js`.### searchInput (Element) [required]The input element on which the plugin should listen for keyboard event and trigger the searching and rendering for articles.### resultsContainer (Element) [required]The container element in which the search results should be rendered in. Typically an ``.### json (String|JSON) [required]You can either pass in an URL to the `search.json` file, or the results in form of JSON directly, to save one round trip to get the data.### searchResultTemplateThe template of a single rendered search result.The templating syntax is very simple: You just enclose the properties you want to replace with curly braces.E.g.The template```{title}```will render to the following```Welcome to Jekyll!```If the `search.json` contains this data```[    {      &quot;title&quot;    : &quot;Welcome to Jekyll!&quot;,      &quot;category&quot; : &quot;&quot;,      &quot;tags&quot;     : &quot;&quot;,      &quot;url&quot;      : &quot;/jekyll/update/2014/11/01/welcome-to-jekyll.html&quot;,      &quot;date&quot;     : &quot;2014-11-01 21:07:22 +0100&quot;    }]```### noResultsTextThe HTML that will be shown if the query didn&#39;t match anything.### limitYou can limit the number of posts rendered on the page.### fuzzyEnable fuzzy search to allow less restrictive matching.### excludePass in a list of terms you want to exclude (terms will be matched against a regex, so urls, words are allowed).## Enable full content search of posts and pages- Replace &#39;search.json&#39; with the following code:```---layout: null---[  {% for post in site.posts %}    {      &quot;title&quot;    : &quot;{{ post.title | escape }}&quot;,      &quot;category&quot; : &quot;{{ post.category }}&quot;,      &quot;tags&quot;     : &quot;{{ post.tags | join: &#39;, &#39; }}&quot;,      &quot;url&quot;      : &quot;{{ site.baseurl }}{{ post.url }}&quot;,      &quot;date&quot;     : &quot;{{ post.date }}&quot;,      &quot;content&quot;  : &quot;{{ post.content | strip_html | strip_newlines }}&quot;    } {% unless forloop.last %},{% endunless %}  {% endfor %}  ,  {% for page in site.pages %}   {     {% if page.title != nil %}        &quot;title&quot;    : &quot;{{ page.title | escape }}&quot;,        &quot;category&quot; : &quot;{{ page.category }}&quot;,        &quot;tags&quot;     : &quot;{{ page.tags | join: &#39;, &#39; }}&quot;,        &quot;url&quot;      : &quot;{{ site.baseurl }}{{ page.url }}&quot;,        &quot;date&quot;     : &quot;{{ page.date }}&quot;,        &quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines }}&quot;     {% endif %}   } {% unless forloop.last %},{% endunless %}  {% endfor %}]```### If search isn&#39;t working due to invalid JSON- There is a filter plugin in the _plugins folder which should remove most characters that cause invalid JSON. To use it, add the simple_search_filter.rb file to your _plugins folder, and use `remove_chars` as a filter.For example: in search.json, replace```&quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines }}&quot;```with```&quot;content&quot;  : &quot;{{ page.content | strip_html | strip_newlines | remove_chars | escape }}&quot;```##Browser supportBrowser support should be about IE6+ with this `addEventListener` [shim](https://gist.github.com/eirikbacker/2864711#file-addeventlistener-polyfill-js)# Dev setup- `npm install` the dependencies.- `gulp watch` during development- `npm test` or `npm run test-watch` to run the unit tests#License##MIT licensedPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &#39;Software&#39;), 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 &#39;AS IS&#39;, 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."

##Browser support

Browser support should be about IE6+ with this addEventListener shim

Dev setup

  • npm install the dependencies.

  • gulp watch during development

  • npm test or npm run test-watch to run the unit tests

#License ##MIT licensed 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.