]> BookStack Code Mirror - website/commitdiff
Merge pull request #121 from blogmotion/master
authorDan Brown <redacted>
Tue, 24 May 2022 12:41:51 +0000 (13:41 +0100)
committerGitHub <redacted>
Tue, 24 May 2022 12:41:51 +0000 (13:41 +0100)
French guide upgraded to RHEL based

274 files changed:
.github/FUNDING.yml [new file with mode: 0644]
.gitignore
LICENSE
config.toml
content/about/confluence-alternative.md [new file with mode: 0644]
content/about/open-source-documentation-software.md [new file with mode: 0644]
content/blog/1-year-of-bookstack.md
content/blog/100-stars-on-github.md
content/blog/5-years-of-bookstack.md [new file with mode: 0644]
content/blog/6-years-of-bookstack.md [new file with mode: 0644]
content/blog/9000-stars-and-the-effects-of-hacker-news.md [new file with mode: 0644]
content/blog/beta-bugfix-release-v0-11-1.md
content/blog/beta-bugfix-release-v0-12-1.md
content/blog/beta-bugfix-release-v0-12-2.md
content/blog/beta-bugfix-release-v0-13-1.md
content/blog/beta-release-v0-10-0.md
content/blog/beta-release-v0-11-0.md
content/blog/beta-release-v0-12-0.md
content/blog/beta-release-v0-13-0.md
content/blog/beta-release-v0-25-5.md
content/blog/beta-release-v0-28-0.md
content/blog/beta-release-v0-28-3.md
content/blog/beta-release-v0-29-0.md
content/blog/beta-release-v0-29-1.md
content/blog/beta-release-v0-30-0.md [new file with mode: 0644]
content/blog/beta-release-v0-31-0.md [new file with mode: 0644]
content/blog/beta-release-v0-7-6.md
content/blog/beta-release-v0-8-0.md
content/blog/beta-release-v0-8-2.md
content/blog/beta-security-release-v0-30-4.md [new file with mode: 0644]
content/blog/beta-security-release-v0-30-5.md [new file with mode: 0644]
content/blog/beta-security-release-v0-30-6.md [new file with mode: 0644]
content/blog/beta-security-release-v0-30-7.md [new file with mode: 0644]
content/blog/beta-security-release-v0-31-4.md [new file with mode: 0644]
content/blog/beta-security-release-v0-31-5.md [new file with mode: 0644]
content/blog/bookstack-in-2021.md [new file with mode: 0644]
content/blog/bookstack-release-v21-04.md [new file with mode: 0644]
content/blog/bookstack-release-v21-05.md [new file with mode: 0644]
content/blog/bookstack-release-v21-08.md [new file with mode: 0644]
content/blog/bookstack-release-v21-10.md [new file with mode: 0644]
content/blog/bookstack-release-v21-11.md [new file with mode: 0644]
content/blog/bookstack-release-v21-12.md [new file with mode: 0644]
content/blog/bookstack-release-v22-02.md [new file with mode: 0644]
content/blog/bookstack-release-v22-03.md [new file with mode: 0644]
content/blog/bookstack-release-v22-04.md [new file with mode: 0644]
content/blog/bookstack-support-services.md [new file with mode: 0644]
content/blog/contributing-to-open-source.md [new file with mode: 0644]
content/blog/replacing-ga-and-mailchimp.md [new file with mode: 0644]
content/blog/security-release-v21-08-2.md [new file with mode: 0644]
content/blog/security-release-v21-08-5.md [new file with mode: 0644]
content/blog/security-release-v21-10-1.md [new file with mode: 0644]
content/blog/security-release-v21-10-2.md [new file with mode: 0644]
content/blog/security-release-v21-10-3.md [new file with mode: 0644]
content/blog/security-release-v21-11-2.md [new file with mode: 0644]
content/blog/security-release-v21-11-3.md [new file with mode: 0644]
content/blog/security-release-v21-12-1.md [new file with mode: 0644]
content/blog/security-release-v22-02-3.md [new file with mode: 0644]
content/blog/services-we-use.md [new file with mode: 0644]
content/blog/ubuntu-2204-script.md [new file with mode: 0644]
content/docs/admin/backup-restore.md
content/docs/admin/commands.md
content/docs/admin/debugging.md
content/docs/admin/email-config.md [deleted file]
content/docs/admin/email-webhooks.md [new file with mode: 0644]
content/docs/admin/hacking-bookstack.md
content/docs/admin/installation.md
content/docs/admin/language-config.md
content/docs/admin/ldap-auth.md
content/docs/admin/multi-instance.md
content/docs/admin/oidc-auth.md [new file with mode: 0644]
content/docs/admin/other-config.md
content/docs/admin/pdf-rendering.md
content/docs/admin/saml2-auth.md
content/docs/admin/security.md
content/docs/admin/subdirectory-setup.md
content/docs/admin/third-party-auth.md
content/docs/admin/updates.md
content/docs/admin/upload-config.md
content/docs/admin/visual-customisation.md
content/docs/user/content-overview.md
content/docs/user/markdown-editor.md
content/docs/user/organising-content.md
content/docs/user/page-permalinks.md [new file with mode: 0644]
content/docs/user/reusing-content.md
content/docs/user/roles-and-permissions.md [new file with mode: 0644]
content/docs/user/searching.md
content/docs/user/wysiwyg-editor.md
content/support/_index.html [new file with mode: 0644]
gulpfile.js [deleted file]
package-lock.json
package.json
readme.md
static/BingSiteAuth.xml [new file with mode: 0644]
static/images/2017/01/bookstack-includes-popover.webm
static/images/2020/07/bookstack_website_2016_2020.png [new file with mode: 0644]
static/images/2020/09/api_chapters.png [new file with mode: 0644]
static/images/2020/09/attachment_link_insert.png [new file with mode: 0644]
static/images/2020/09/bookstack_audit_log.png [new file with mode: 0644]
static/images/2020/09/code_block_session_saving.png [new file with mode: 0644]
static/images/2021/01/audit_log_updates.png [new file with mode: 0644]
static/images/2021/01/bookstack-permalink.mp4 [new file with mode: 0644]
static/images/2021/01/bookstack-permalink.webm [new file with mode: 0644]
static/images/2021/01/changes_view.png [new file with mode: 0644]
static/images/2021/01/mailbag.png [new file with mode: 0644]
static/images/2021/01/mailchimp_tracked_links.png [new file with mode: 0644]
static/images/2021/01/ownership_change.png [new file with mode: 0644]
static/images/2021/01/pages_api.png [new file with mode: 0644]
static/images/2021/01/plausible_bookstack_site_analytics.png [new file with mode: 0644]
static/images/2021/01/recycle_bin.png [new file with mode: 0644]
static/images/2021/01/recycle_bin_maintenance.png [new file with mode: 0644]
static/images/2021/01/user_list_activity.png [new file with mode: 0644]
static/images/2021/04/audit_log_filter.png [new file with mode: 0644]
static/images/2021/04/footer_display.png [new file with mode: 0644]
static/images/2021/04/footer_settings.png [new file with mode: 0644]
static/images/2021/04/search_owned_by.png [new file with mode: 0644]
static/images/2021/04/shelf_sort.png [new file with mode: 0644]
static/images/2021/05/dark-mode-header-2104.png [new file with mode: 0644]
static/images/2021/05/dark-mode-header-2105.png [new file with mode: 0644]
static/images/2021/05/favourites-action.png [new file with mode: 0644]
static/images/2021/05/favourites-home-list.png [new file with mode: 0644]
static/images/2021/05/favourites-profile-menu.png [new file with mode: 0644]
static/images/2021/05/header-a11y.png [new file with mode: 0644]
static/images/2021/05/next-prev-nav.png [new file with mode: 0644]
static/images/2021/05/tags-in-search.png [new file with mode: 0644]
static/images/2021/08/export-options-with-markdown.png [new file with mode: 0644]
static/images/2021/08/mfa-role-permission.png [new file with mode: 0644]
static/images/2021/08/mfa-verify-view.png [new file with mode: 0644]
static/images/2021/08/non-download-attachment-link.png [new file with mode: 0644]
static/images/2021/08/skip-to-content-link.png [new file with mode: 0644]
static/images/2021/10/attachment-api-endpoints.png [new file with mode: 0644]
static/images/2021/10/audit-log-ip.png [new file with mode: 0644]
static/images/2021/10/code-climate.png [new file with mode: 0644]
static/images/2021/10/crowdin.png [new file with mode: 0644]
static/images/2021/10/debug-view.png [new file with mode: 0644]
static/images/2021/10/discord.png [new file with mode: 0644]
static/images/2021/10/docsearch.png [new file with mode: 0644]
static/images/2021/10/editor-conflicts.png [new file with mode: 0644]
static/images/2021/10/github.png [new file with mode: 0644]
static/images/2021/10/gohugo.png [new file with mode: 0644]
static/images/2021/10/mailbag.png [new file with mode: 0644]
static/images/2021/10/oidc.png [new file with mode: 0644]
static/images/2021/10/ovh.png [new file with mode: 0644]
static/images/2021/10/plausible.png [new file with mode: 0644]
static/images/2021/10/styleci.png [new file with mode: 0644]
static/images/2021/10/totp-url.png [new file with mode: 0644]
static/images/2021/11/search-api.png [new file with mode: 0644]
static/images/2021/11/search-results.png [new file with mode: 0644]
static/images/2021/11/tags-page.png [new file with mode: 0644]
static/images/2021/12/2021_development_rate.png [new file with mode: 0644]
static/images/2021/12/audit_log_search.png [new file with mode: 0644]
static/images/2021/12/company_sponsors.png [new file with mode: 0644]
static/images/2021/12/copy_book.png [new file with mode: 0644]
static/images/2021/12/copy_role.png [new file with mode: 0644]
static/images/2021/12/site_stats.png [new file with mode: 0644]
static/images/2021/12/sponsor_progress.png [new file with mode: 0644]
static/images/2021/12/webhook_form.png [new file with mode: 0644]
static/images/2021/12/webhooks_list.png [new file with mode: 0644]
static/images/2022/02/api-user-endpoints.png [new file with mode: 0644]
static/images/2022/02/collapse-blocks-qa.png [new file with mode: 0644]
static/images/2022/02/collapsible-blocks.mp4 [new file with mode: 0644]
static/images/2022/02/community-videos.png [new file with mode: 0644]
static/images/2022/02/crowdin.png [new file with mode: 0644]
static/images/2022/02/editor-toolbar.png [new file with mode: 0644]
static/images/2022/02/editor-translations.png [new file with mode: 0644]
static/images/2022/02/github-sponsors.png [new file with mode: 0644]
static/images/2022/02/huntr-dev.png [new file with mode: 0644]
static/images/2022/02/recently-updated-enhancements.png [new file with mode: 0644]
static/images/2022/02/user-create-language.png [new file with mode: 0644]
static/images/2022/02/webhook-debug-help.png [new file with mode: 0644]
static/images/2022/03/link_selector.png [new file with mode: 0644]
static/images/2022/03/link_toolbar.png [new file with mode: 0644]
static/images/2022/03/settings_view.png [new file with mode: 0644]
static/images/2022/03/task_lists.png [new file with mode: 0644]
static/images/2022/04/bookstack_editor_switch.mp4 [new file with mode: 0644]
static/images/2022/04/bookstack_traffic_hn_6m.png [new file with mode: 0644]
static/images/2022/04/bookstack_traffic_hn_week.png [new file with mode: 0644]
static/images/2022/04/default_editor_option.png [new file with mode: 0644]
static/images/2022/04/edit_switch_warning.png [new file with mode: 0644]
static/images/2022/04/editor_switch_dropdown.png [new file with mode: 0644]
static/images/2022/04/recycle_bin_endpoints.png [new file with mode: 0644]
static/images/2022/04/star_growth.png [new file with mode: 0644]
static/images/2022/04/stars_per_month.png [new file with mode: 0644]
static/images/about/books-view.png [new file with mode: 0644]
static/images/blog-cover-images/autumn-road-sebastian_unrau.jpg [new file with mode: 0644]
static/images/blog-cover-images/blossom-tegethoff.jpg [new file with mode: 0644]
static/images/blog-cover-images/book-collection-fredmarriage.jpg [new file with mode: 0644]
static/images/blog-cover-images/cat-pounce-dorothea-oldani.jpg [new file with mode: 0644]
static/images/blog-cover-images/dog-woods-jamie-street.jpg [new file with mode: 0644]
static/images/blog-cover-images/door-lock-lucas-santos.jpg [new file with mode: 0644]
static/images/blog-cover-images/fence-birds-yudi-m.jpg [new file with mode: 0644]
static/images/blog-cover-images/gate-benofthenorth.jpg [new file with mode: 0644]
static/images/blog-cover-images/gate-masaaki-komori.jpg [new file with mode: 0644]
static/images/blog-cover-images/jellyfish-ruthlaine-tan.jpg [new file with mode: 0644]
static/images/blog-cover-images/library-priscilla-du-preez.jpg [new file with mode: 0644]
static/images/blog-cover-images/lighthouse-dimitry_b.jpg [new file with mode: 0644]
static/images/blog-cover-images/lions-joel-herzog.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-aubrey-odom.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-calina.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-chepe-nicoli.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-door-waldemar-brandt.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-gina-neri.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-hudsoncrafted.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-jon-moore.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-jornada-produtora.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-markus-spiske.jpg [new file with mode: 0644]
static/images/blog-cover-images/lock-muhammad-zaqy-al-fattah.jpg [new file with mode: 0644]
static/images/blog-cover-images/locks-marcos-mayer.jpg [new file with mode: 0644]
static/images/blog-cover-images/mountain-stars-martin-jernberg.jpg [new file with mode: 0644]
static/images/blog-cover-images/mountains-jerry-zhang.jpg [new file with mode: 0644]
static/images/blog-cover-images/mountains-jonatan-pie.jpg [new file with mode: 0644]
static/images/blog-cover-images/path-ugne-vasyliute.jpg [new file with mode: 0644]
static/images/blog-cover-images/penguin-steffen-triekel.jpg [new file with mode: 0644]
static/images/blog-cover-images/reindeer-joe-green.jpg [new file with mode: 0644]
static/images/blog-cover-images/robin-alfred-kenneally.jpg [new file with mode: 0644]
static/images/blog-cover-images/spring-arno-smit.jpg [new file with mode: 0644]
static/images/blog-cover-images/spring-bird-brian-breeden.jpg [new file with mode: 0644]
static/images/blog-cover-images/winter-fox-birger-strahl.jpg [new file with mode: 0644]
static/images/blog-cover-images/zebra-herd-the_bracketeer.jpg [new file with mode: 0644]
static/images/dan.jpg
static/images/docs/book-sort.png [new file with mode: 0644]
static/images/docs/page-move.png [new file with mode: 0644]
static/images/docs/user/page-permissions.png [new file with mode: 0644]
static/images/docs/user/permissions-active-indicator.png [new file with mode: 0644]
static/images/docs/user/shelf-book-organising.png [new file with mode: 0644]
static/images/sponsors/cloudabove.svg [new file with mode: 0644]
static/images/sponsors/diagramsnet.png [new file with mode: 0644]
static/images/sponsors/stellarhosted.png [new file with mode: 0644]
themes/bookstack/archetypes/default.md
themes/bookstack/layouts/_default/single.html
themes/bookstack/layouts/about/list.html [new file with mode: 0644]
themes/bookstack/layouts/about/single.html [new file with mode: 0644]
themes/bookstack/layouts/blog/single.html
themes/bookstack/layouts/index.html
themes/bookstack/layouts/partials/footer.html
themes/bookstack/layouts/partials/header.html
themes/bookstack/layouts/partials/icon/code.svg
themes/bookstack/layouts/partials/icon/dark-mode.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/demo.svg [moved from themes/bookstack/layouts/partials/icon/touch_app.svg with 100% similarity]
themes/bookstack/layouts/partials/icon/drawing.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/email.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/features.svg [moved from themes/bookstack/layouts/partials/icon/star.svg with 100% similarity]
themes/bookstack/layouts/partials/icon/logo.svg
themes/bookstack/layouts/partials/icon/mfa.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/reddit.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/support.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/twitter.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/icon/youtube.svg [new file with mode: 0644]
themes/bookstack/layouts/partials/mailchimp.html [deleted file]
themes/bookstack/layouts/partials/menu_admin_docs.html
themes/bookstack/layouts/partials/menu_user_docs.html
themes/bookstack/layouts/partials/signup.html [new file with mode: 0644]
themes/bookstack/layouts/partials/twitter_card.html
themes/bookstack/layouts/section/blog.html
themes/bookstack/layouts/section/docs.html
themes/bookstack/sass/_blocks.scss
themes/bookstack/sass/_blog.scss
themes/bookstack/sass/_buttons.scss
themes/bookstack/sass/_code.scss [new file with mode: 0644]
themes/bookstack/sass/_codemirror.scss
themes/bookstack/sass/_grid.scss
themes/bookstack/sass/_header.scss
themes/bookstack/sass/_html.scss
themes/bookstack/sass/_text.scss
themes/bookstack/sass/_variables.scss
themes/bookstack/sass/styles.scss
themes/bookstack/static/images/bookstack-hero-screenshot.jpg [deleted file]
themes/bookstack/static/images/bookstack-hero-screenshot.webp [new file with mode: 0644]
themes/bookstack/static/images/docs/book-sort.png [deleted file]
themes/bookstack/static/images/docs/page-move-menu.png [deleted file]
themes/bookstack/static/js/script.js
themes/bookstack/static/libs/codemirror/modes.js
themes/bookstack/static/libs/docs-searchbar.min.css [new file with mode: 0644]
themes/bookstack/static/libs/docs-searchbar.min.js [new file with mode: 0644]
themes/bookstack/static/libs/photoswipe.min.js

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..d40879a
--- /dev/null
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: ssddanbrown
index 233cf8f2686fbaae2e5131c0dd924fa2e42f9678..9e675e626944ab71215c56925d5aaee98d87a16d 100644 (file)
@@ -1,2 +1,3 @@
 node_modules
-/public
\ No newline at end of file
+/public
+/resources
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index d2395ec7faf7a5cf85f7044ba110e784372fad29..ddbe4c907b46a2e77e80ae1ebe329ba9a742dbac 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2017 Dan Brown
+Copyright (c) 2020 Dan Brown
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
index ec398298cfd47b4d0eb43eaf12459c88b35becd0..ba052e28b8816675943395798dc6c967bf623d08 100644 (file)
@@ -1,8 +1,11 @@
 languageCode = "en-gb"
 title = "BookStack"
-baseurl = "https://bookstackapp.com/"
+baseurl = "https://www.bookstackapp.com/"
 theme="bookstack"
 enableGitInfo=true
+paginate=50
+summarylength=20
+assetDir = "static"
 
 [markup]
   defaultMarkdownHandler = "goldmark"
@@ -10,10 +13,24 @@ enableGitInfo=true
     [markup.goldmark.renderer]
       unsafe = true
       xHTML = true
+  [markup.highlight]
+    anchorLineNos = true
+    codeFences = true
+    guessSyntax = true
+    hl_Lines = ""
+    lineAnchors = ""
+    lineNoStart = 1
+    lineNos = false
+    lineNumbersInTable = false
+    noClasses = false
+    style = "vs"
+    tabWidth = 4
 
 [permalinks]
   post = "/blog/:slug/"
 
 [params]
   blogCover = "/images/cover.jpg"
-  googleAnalyticsUserID = "UA-61486258-4"
+  description = "BookStack is a simple, open-source, self-hosted, easy-to-use platform for organising and storing information."
+  [params.social]
+    twitter = "bookstack_app"
diff --git a/content/about/confluence-alternative.md b/content/about/confluence-alternative.md
new file mode 100644 (file)
index 0000000..8bff1db
--- /dev/null
@@ -0,0 +1,57 @@
+---
+title: "BookStack: An Open Source Alternative to Confluence"
+---
+
+
+### What BookStack Offers
+
+##### Free and Open Source
+
+BookStack is 100% free to use under the MIT license. The code is fully open; Use and modify BookStack any way you like, just provide attribution if you distribute the code.
+
+##### Self-Host However You Please
+
+There's no push to the cloud here unless that's your preference. Whether you install on a Raspberry Pi, or stick it in AWS; It's fully up to you. You can ensure your documentation stays behind your firewall.
+
+##### Lightweight and Speedy
+
+Tired of seeing placeholder content while your pages load? Tired of managing bloated Java processes? BookStack is built in modern PHP and can run on lightweight systems.
+
+##### WYSIWYG Editing Experience
+
+BookStack uses a WYSWIYG editor at it's core, with a range of essential formatting options and features. No scary mark-up language. Ideal for mixed-skill environments.
+
+##### Standard Content Storage Format
+
+BookStack primarily retains content in a relatively focused and flat layer of HTML. One of our ideals is to keep the format relatively standardised for if you ever need to migrate away from BookStack.
+
+##### System API
+
+Our growing REST API allows the programming of automation where desired, and provides a mechanism for import where needed.
+
+##### An Open Community
+
+Whether it's via GitHub, Discord or our subreddit, you can discuss with and seek help from the BookStack developers & the wider community.
+
+##### Diagrams.net Integration, Built In
+
+Want to create diagrams in your documentation? You can jump directly into an embedded [diagrams.net](https://www.diagrams.net/) editing session which will save directly into BookStack.
+
+##### Authentication Options
+
+A range of authentication options and controls are built-in including LDAP, SAML2 and a range of OAuth clients. Additionally MFA with role-based enforcement can be used with any of these.
+
+---
+
+[GitHub](https://github.com/BookStackApp/BookStack)   |   [Demo Instance](https://demo.bookstackapp.com)   |   [Installation Instructions](/docs/admin/installation)
+
+[Read more and see screenshots on our homepage »](/)
+
+---
+
+## Migrating From Confluence to BookStack
+
+We don't yet have an automated way to migrate your content directly from Confluence to BookStack.  
+We do have a growing REST API that can be used to automate some of the process where possible.  
+In the future we'd like to have some tools to assist with a migration but the opportunity  
+to create such tools has not yet presented itself.
diff --git a/content/about/open-source-documentation-software.md b/content/about/open-source-documentation-software.md
new file mode 100644 (file)
index 0000000..203cc49
--- /dev/null
@@ -0,0 +1,73 @@
+---
+title: "Open Source Documentation Software"
+---
+
+
+BookStack is proud to be an open source & free alternative to many existing closed-source & paid offerings.
+On this page we'll go though the reasons why we think using an open source documentation option 
+can be a benefit to you or your business.
+
+### Control Your Own Data
+
+BookStack is self-hosted open source software, meaning that we supply the code for you
+to run on your own systems within your own environment, or in a location where you trust.
+You don't need to put your faith in a third party to keep your information secure and 
+you don't need to keep track of what other parties they may be sending your data to.
+
+Hosted closed-source services often sneak in the use of various tools, to help meet
+business requirements, such as usage analytics and marketing tracking.
+Even in the best intentions of such uses, they can sometimes leak more data and present
+another entity that needs to be trusted. This can especially complicate implementation
+when you are storing details of customers, where they may have their own security
+requirements. With BookStack, we don't need to include such systems since we get
+feedback direct from the community. If you don't trust us the source code is freely
+available for self-review, and you can sandbox/limit your own instance within your
+own environment as much as necessary. 
+
+### Free in Price, Free in Limits
+
+BookStack is a free documentation platform, meaning you can start up an instance without
+spending any money on the software, with direct access to the code needed. No sign-up
+form where you have to provide your email, no salesperson chasing you down while assess
+the product. 
+
+Many paid offerings charge per user per month/year. Having to get finance involved can 
+be a hinderance to wider adoption, which can be vital for the success of a documentation
+platform. For your central information, concern of payment should not be a point of contention
+when trying to onboard a new department. We believe access to a shared information store should
+be as frictionless as possible.
+
+Even if you can afford a paid offering now, that may not be the case down the line once prices
+are raised or when profits are limited. You don't want to be in position of deciding who
+loses access in an effort to reduce costs. Nor would you want to promote insecure practices
+(such as user account sharing) to meet user limits.
+
+Speaking of insecure practices, we don't charge extra for advanced authentication features
+like many paid services do. Once something is added to the core code-base, its available
+to all. Features like SAML2, LDAP and multi-factor-auth are baked into the platform.
+
+### Customize to Requirements
+
+Since BookStack is open source under the MIT license, you can take the source code and make
+whatever changes are needed to make it fit your usage. This isn't an ideal route for most users
+on a day-to-day basis, since you'll need to maintain any such changes as BookStack evolves, but it
+can be extremely useful in a pinch if there's an ability/feature/tweak you absolutely need which has not
+been implemented in the core project. By using in-house development skills, or by finding a developer
+familiar with the popular frameworks we use, you can achieve anything that's possible since you have
+free access to the code. Conversely, when using a hosted closed source offering, you have little option
+other than to hope the service provider listens to, and implements, your request.
+
+### Open Development
+
+Even if not looking to hack around your own instance, our work is done in the open and we're
+available via fairly direct means on GitHub, Discord or Reddit. Rather than providing feedback
+via a non-responsive support portal, you can get involved and join development discussions 
+directly and even help out with the project if desired. 
+By being part of an open project you can help in building a platform that can then be used 
+by anyone else, no matter their location or financial capabilities. Even if core development
+work stops, the project is under a license which means it can be forked by anyone else to continue
+development. That same cannot be said for a closed system.
+
+---
+
+[Read more about the features of BookStack on our homepage &raquo;](/#features)
\ No newline at end of file
index dd564c153aefb3f669f14789bc54003a74ae0030..0d6e1c99f185088495cb9108b142415926fba19a 100644 (file)
@@ -11,7 +11,7 @@ date = 2016-07-11T15:37:51Z
 
 +++
 
-BookStack is now 1 year old! The first commit can be found on [GitHub here](https://github.com/ssddanbrown/BookStack/commit/eaa1765c7a68cd671bcb37a666203210bf05d217). BookStack also now has over 200 stars GitHub stars! It's absolutely awesome to see BookStack grow into something worked on and shaped by a community.
+BookStack is now 1 year old! The first commit can be found on [GitHub here](https://github.com/BookStackApp/BookStack/commit/eaa1765c7a68cd671bcb37a666203210bf05d217). BookStack also now has over 200 stars GitHub stars! It's absolutely awesome to see BookStack grow into something worked on and shaped by a community.
 
 ## Back in Time
 
@@ -64,4 +64,4 @@ Traffic to the website has steadily grown since the [100 stars](https://www.book
 
 ## Moving Forwards
 
-I'd like to re-focus on getting through more of the outstanding issues on GitHub soon since they are starting to build up. I'd also like to slowly prepare the project for becoming multilingual to reach more people. The official docs, https://www.bookstackapp.com/docs/, are still fairly weak (Especial for a documentation platform) so I'd like to focus on getting them up to standard and I'm still eager to make a video to introduce newcomers to the project.
\ No newline at end of file
+I'd like to re-focus on getting through more of the outstanding issues on GitHub soon since they are starting to build up. I'd also like to slowly prepare the project for becoming multilingual to reach more people. The official docs, https://www.bookstackapp.com/docs/, are still fairly weak (Especial for a documentation platform) so I'd like to focus on getting them up to standard and I'm still eager to make a video to introduce newcomers to the project.
index 0d2693f3e1c474270afbb90aa5acd22abbaa0164..60247545a7f6df3fec2197da7024265083726196 100644 (file)
@@ -10,7 +10,7 @@ slug = "100-stars-on-github"
 
 +++
 
-BookStack now has [over 100 stars on GitHub](https://github.com/ssddanbrown/BookStack/stargazers)! It's a minor milestone but I'm very happy about it. Since July of last year I've put a fair amount of effort into the project and it's awesome to see other people find value in BookStack. 
+BookStack now has [over 100 stars on GitHub](https://github.com/BookStackApp/BookStack/stargazers)! It's a minor milestone but I'm very happy about it. Since July of last year I've put a fair amount of effort into the project and it's awesome to see other people find value in BookStack. 
 
 Most of those stars have come from my [initial posting on the Reddit](https://www.reddit.com/r/selfhosted/comments/3z06rb/bookstack_a_free_wikilike_information_store/) and a tweet about the project. These events can be seen in the site analytics:
 
@@ -25,4 +25,4 @@ The initial small spike is my Reddit post and the large middle spike is the twit
 
 I've recently added BookStack to [alternativeto.net](https://alternativeto.net/software/bookstack/) so people can find it when looking at similar systems like Confluence.
 
-I would really like to improve the BookStack documentation soon as it's very weak with the readme being the only source of information. Ideally I'd like the docs to be written in markdown in a git repo so I'm looking to get some kind of markdown import going into BookStack to accommodate this. Also, I'm looking to create some videos to give an overview of what BookStack is and the features it has.
\ No newline at end of file
+I would really like to improve the BookStack documentation soon as it's very weak with the readme being the only source of information. Ideally I'd like the docs to be written in markdown in a git repo so I'm looking to get some kind of markdown import going into BookStack to accommodate this. Also, I'm looking to create some videos to give an overview of what BookStack is and the features it has.
diff --git a/content/blog/5-years-of-bookstack.md b/content/blog/5-years-of-bookstack.md
new file mode 100644 (file)
index 0000000..75c6a8a
--- /dev/null
@@ -0,0 +1,109 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "Five Years of BookStack"
+image = "/images/blog-cover-images/mountains-jerry-zhang.jpg"
+author = "Dan Brown"
+slug = "5-years-of-bookstack"
+draft = false
+date = 2020-07-28T23:00:00Z
+description = "At five years old we take a look at the numbers we've hit"
++++
+
+With a [first commit](https://github.com/BookStackApp/BookStack/commit/eaa1765c7a68cd671bcb37a666203210bf05d217) dated Sunday the 12th of July 2015, BookStack is now over 5 years old. Looking back, those 5 years have appeared to fly by but within that time there's been a lot of growth, both for me as a maintainer and in regards to the project itself. 
+
+For the 5th year anniversary I thought it would be interesting to collate some numbers to quantify the scale of the project while also sharing my experience of maintaining & managing the project.
+
+### BookStack, In Numbers
+
+At the time of writing *(28th July)*:
+
+#### GitHub Figures
+
+- [5,050 GitHub stars](https://github.com/BookStackApp/BookStack/stargazers)
+- [817 forks on GitHub](https://github.com/BookStackApp/BookStack/network/members)
+- 2,202 GitHub issues and PRs opened
+- 1,296 GitHub issues closed, 508 open
+- [90 releases published](https://github.com/BookStackApp/BookStack/releases)
+- 1,322 git clones in the last 14 days, 560 unique
+
+#### Code Repository Stats
+
+- [2,200 commits](https://github.com/BookStackApp/BookStack/commits/development)
+- 127 direct git contributors
+- 51,814 lines of code across 1112 files
+    - PHP 82.9%
+    - HTML 8.1%
+    - JavaScript 5.9%
+    - CSS 3.1%
+
+#### Social & Docker
+
+- [667 Discord members](https://discord.gg/ztkBqR2)
+- [144 Subreddit members](https://www.reddit.com/r/BookStack/)
+- [> 45,000,000 docker hub pulls](https://hub.docker.com/search?q=bookstack&type=image)
+
+#### Website Analytics
+
+Main bookstackapp.com site only, Averaged over last 90 days:
+
+- 431 unique users per day
+- 1,565 page views per day
+- Operating system breakdown:
+    - 49.17% Windows
+    - 20.55% Mac
+    - 12.31% Android
+    - 9.78%  Linux
+    - 7.72%  iOS
+
+Growth since January 2016:
+
+![BookStack site users over time](/images/2020/07/bookstack_website_2016_2020.png)
+
+#### CrowdIn (Project Translations) Numbers
+
+- 26 languages
+- 4243 words to translate
+- 64 project members
+
+#### Thoughts
+
+I've always kept an eye on GitHub stars, so its satisfying to hit the 5k mark at the same time as hitting 5 years. The GitHub stars have been slowly accelerating. The [first 1k](https://www.bookstackapp.com/blog/1k-stars-and-v0-19-0/) took about 2 years & 3 months to accrue, whereas we hit 4k only in February this year.
+
+For me, The two most surprising figures are the docker pulls and the discord member count. 45 million is just a staggering number of docker pulls to imagine. I know there's a massive amount of automation in that area leading to such an inflated figure but I still double take when seeing it.
+
+The discord user count gives me a warm fuzzy feeling; It's incredible that so many people have reached a level of admiration towards the project that they'd subscribe themselves, and remain in, a chat room purposed for it. I was nervous at first about needing to monitor an instant messaging social channel but it's definitely grown on me as an ideal place for informal project chat.
+
+
+### My Experience of the Last 5 Years
+
+Within the last 5 years I've bought my own house, adopted a cat, become a Tech Lead at work and discovered I have, and learnt to live with, a chronic arthritis-based disease. During this time BookStack has been a large part of my life, with many weekends and evenings dedicated to the project.
+
+For the most part, I'm really happy with the project and its growth. I absolutely love open source software and being part of something in that area is wonderful. Working on the project has forced me into many new areas of development & technology including LDAP, SAML, Web Accessibility and multi-language support (Including supporting right-to-left content). Accessibility has been my favorite topic so far since it was something I thought I knew but in reality was a subject I had little depth in. Learning how to make the platform more inclusive to those with disabilities has not only been mindfully rewarding but it has also been rewarding in skills and fundamental understandings I can utilize in my other work.
+
+The social aspect of managing something like BookStack has been quite a new experience for me. I'm not too much of a social person by default, so working out how to deal with the thoughts and opinions of a growing amount of people has been a challenge. The anonymisation and distance inferred by the web introduces a lot of unknowns to each issue & pull request that is addressed; In many cases you have no idea of the skill, experience, history or the intentions of the other party. Their idea of what the project is, or should be, may be completely different to my own. As time goes on I feel I'm getting better at knowing where to draw the scope for the project and how to be assertive in that scope. The hard part comes in the reasoning when you have to disappoint, It can be a tricky & time consuming task to apply the thought and write out the reasoning from the maintainer perspective, especially when an idea or request is popular so that perspective is very much the minority. A good example of this can be seen with [my thoughts for SQLite/PostgreSQL support here](https://github.com/BookStackApp/BookStack/issues/76#issuecomment-494956958).
+
+GitHub issues do seem to have a mental burden. I take a lax approach to closing off issues, since I prefer to leave things open to feedback & opinion even if I do not intend to include, which may not help but seeing that counter increase can yet remain worrisome. I think it's likely due knowing that each of those could potentially take a lot of time, some may take mere minutes but some can consume an evening or even weekend with little reward in return. I am very thankful to those that help out and to those that spend the time detailing or investigate the issue when posting. 
+
+There are two main principles I've learnt when it comes to GitHub issues & pull requests:
+
+1. Always be polite & thankful where possible. I always remind myself that these are people that are taking their own time to report an issue, or are passionate enough about the project to share their own opinion. In many cases it'll be someone's first interaction with GitHub or open source; The extra effort to make that a positive experience is likely well worth it.
+2. If my initial reaction to an idea or request is negative, Don't respond right away but come back to it in a day or two. With time often comes additional perspective and empathy which may change your alignment on the matter, or at least allow for a more constructive response.
+
+One thing I still find particularly tricky are pull requests. Once someone has put the time in to create something it becomes more awkward to reject or request fundamental changes. In addition, merge of a pull request can often force my focus to less interesting busy-work like documenting, implementation clean-up and test writing. That's not to say I'm not appreciative of people's efforts though, it's amazing that individuals are interested enough to contribute, it's just that I need to learn how to manage that process better.
+
+Over the next couple of years I'd like to attend a few open source events & conferences. It would be great to speak to others with similar projects to get some first-hand advice and learn to build up a community in a manageable way.
+
+### BookStack on YouTube
+
+As a positive addendum to this five year post, I've been seeing some great videos emerge on YouTube that cover how to install or configure BookStack in various cases. Here's a couple of examples:
+
+<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/NhPw1DvxYZc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/_13K1DeZwhk" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+There are many other videos out there; I'm keeping track of any I find, that feature or mention the project, in this playlist: https://www.youtube.com/playlist?list=PLMI5XWgNpCyVjdIqx9oPoHl2y42Rph3Co
+
+---
+
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: &nbsp; <span>Photo by <a href="https://unsplash.com/@z734923105?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jerry Zhang</a> on <a href="https://unsplash.com/s/photos/mountain?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/6-years-of-bookstack.md b/content/blog/6-years-of-bookstack.md
new file mode 100644 (file)
index 0000000..cfa5c90
--- /dev/null
@@ -0,0 +1,87 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "Six Years of BookStack"
+image = "/images/blog-cover-images/mountains-jonatan-pie.jpg"
+author = "Dan Brown"
+slug = "6-years-of-bookstack"
+draft = false
+date = 2021-07-13T19:00:00Z
++++
+
+Well there goes another year, A year of worldwide lock-downs and uncertainty but BookStack development has pushed on and now we're at 6 years since the [original commit](https://github.com/BookStackApp/BookStack/commit/eaa1765c7a68cd671bcb37a666203210bf05d217) on the 12th of July 2015. To mark the milestone we'll look at the figures, go into some upcoming plans and distribute some thanks.
+
+### An Upcoming Experimental Change of Pace
+
+At the end of September I'll be leaving my current job, where I've been working for the last 7 years. The details of this can be found on [my personal blog here](https://danb.me/blog/posts/leaving-my-job-to-focus-on-open-source/) but to summarise: From October my current plan is to focus on BookStack for the proceeding 6 months before looking for a new role. I don't have any specific targets or goals for this period but my current idea is to just accelerate along the current path of development to see where we end up afterwards. This should be an exciting time and I'm looking forward to being able to put some real focus on the project for a while.
+
+### Shoutout to Reddit
+
+I want to take this post as an opportunity to shout out to the technical community on Reddit, and the /r/selfhosted subreddit in particular. The [response to the "5 years of BookStack" post](https://www.reddit.com/r/selfhosted/comments/i14dnq/5_years_of_bookstack/) last year was incredibly kind and there have been continued positive & constructive comments from that community since [my original posting](https://www.reddit.com/r/selfhosted/comments/3z06rb/bookstack_a_free_wikilike_information_store/) of the project there in 2016. 
+
+I use [F5bot](https://f5bot.com/) to keep tabs of BookStack mentions on Reddit and I see almost daily mentions across the [/r/selfhosted](https://www.reddit.com/r/selfhosted/), [/r/homelab](https://www.reddit.com/r/homelab/) and [/r/sysadmin](https://www.reddit.com/r/sysadmin/) subreddits, most of which are recommendations which warms my heart every time and adds some mental fuel to the project.
+
+### BookStack, In Numbers, A Year Later
+
+For the ["Five years of BookStack"](/blog/5-years-of-bookstack/) post I decided to list out a bunch of metrics related to the project.
+I thought it'd be fun to do the same for 6 years, but show the relative <strong style="color: green;">increases</strong>/<strong style="color: red;">decreases</strong>.
+
+At the time of writing *(13th July)*:
+
+#### GitHub Figures
+
+- [6,854 GitHub stars](https://github.com/BookStackApp/BookStack/stargazers) <strong style="color: green;">+1,804</strong>
+- [1043 forks on GitHub](https://github.com/BookStackApp/BookStack/network/members) <strong style="color: green;">+226</strong>
+- 2,844 GitHub issues and PRs opened <strong style="color: green;">+642</strong>
+- 1,872 GitHub issues closed (<strong style="color: green;">+576</strong>), 450 open (<strong style="color: red;">-58</strong>)
+- [118 releases published](https://github.com/BookStackApp/BookStack/releases) <strong style="color: green;">+28</strong>
+- 1,558 (<strong style="color: green;">+236</strong>) git clones in the last 14 days, 594 (<strong style="color: green;">+34</strong>) unique
+
+#### Code Repository Stats
+
+- [2,658 commits](https://github.com/BookStackApp/BookStack/commits/development) <strong style="color: green;">+458</strong>
+- 136 direct git contributors <strong style="color: green;">+9</strong>
+
+#### Social & Docker
+
+- [1,575 Discord members](https://discord.gg/ztkBqR2) <strong style="color: green;">+908</strong>
+- [323 Subreddit members](https://www.reddit.com/r/BookStack/) <strong style="color: green;">+179</strong>
+- [> 76,000,000 docker hub pulls](https://hub.docker.com/search?q=bookstack&type=image) <strong style="color: green;">+31m</strong>
+
+#### Website Analytics
+
+Main bookstackapp.com site only, Averaged over last 90 days:
+
+- 754 unique users per day <strong style="color: green;">+323</strong> 
+  - ([Analytics system change may distort this](https://www.bookstackapp.com/blog/replacing-ga-and-mailchimp/))
+- 1,982 page views per day  <strong style="color: green;">+417</strong>
+- Operating system breakdown:
+    - 52% Windows <strong style="color: green;">+3%</strong>
+    - 19% Mac <strong style="color: red;">-1%</strong>
+    - 10% Android <strong style="color: red;">-2%</strong>
+    - 10% Linux
+    - 7% iOS
+
+[Our full website analytics since the start of the year can be found here.](https://analytics.bookstackapp.com/bookstackapp.com)
+
+#### CrowdIn (Project Translations) Numbers
+
+- 34 languages <strong style="color: green;">+8</strong>
+- 4764 words to translate <strong style="color: green;">+521</strong>
+- 122 project members <strong style="color: green;">+58</strong>
+
+#### Thoughts on the Numbers
+
+We've had a nice continued stable growth of the project over the last year which is positive and keeps things manageable. Last year I was particularly suprised by the discord membership count and now I'm surprised again by the relative change in the last year, Over 1.5k people are now part of that chat which I think is still darn surprising for a documentation platform. While on the topic of the discord chat, I've noticed over the last year some wonderful people have been consistently supporting others in the chat. Shout-out to Corporat, Khroners, Morten (Mynster) and lesmyrmidons for your consistent help, it means a lot (Sorry if I've missed anyone else out).
+
+
+#### Further Reading
+
+If you're interested in our privacy perspective we detailed some changes on how we manage our analytics and email updates which might be of interest: [Replacing Google Analytics & Mailchimp](https://www.bookstackapp.com/blog/replacing-ga-and-mailchimp/).
+
+In the v21.05 I wrote about my change of thinking in regards to donations within the project: [Donating to BookStack (Bottom section of the article)](https://www.bookstackapp.com/blog/bookstack-release-v21-05/#donating-to-bookstack).
+
+---
+  
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: &nbsp; <span>Photo by <a href="https://unsplash.com/@r3dmax?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jonatan Pie</a> on <a href="https://unsplash.com/s/photos/mountain-river?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
+  </span></span>
\ No newline at end of file
diff --git a/content/blog/9000-stars-and-the-effects-of-hacker-news.md b/content/blog/9000-stars-and-the-effects-of-hacker-news.md
new file mode 100644 (file)
index 0000000..216f821
--- /dev/null
@@ -0,0 +1,46 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "Reaching 9000 Stars & The Effects of Hacker News"
+date = 2022-04-05T10:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/mountain-stars-martin-jernberg.jpg"
+slug = "9000-stars-and-the-effects-of-hacker-news"
+draft = false
++++
+
+It's been a while since I last put out a blogpost regarding a GitHub star milestone, with the last being a [December 2017 blogpost](/blog/1k-stars-and-v0-19-0/) where we hit 1000 stars.
+Well today I'm happy to say we've now hit 9,000 stars on GitHub! 🥳 🥳 🥳
+
+GitHub star count is mostly a vanity metric, it does not reflect actual project popularity or usage in any way, but it's a metric nonetheless which we can use to see our own relative growth. Our star growth over time can be seen below:
+
+![Line chart showing accelerating GitHub star increase](/images/2022/04/star_growth.png)
+
+As you can see, we generally experience a constant, slight acceleration over time but there's a significant jump in early 2022, and that's mostly due to Hacker News.
+
+## Effects of Hacker News
+
+On the 8th of January I made a ["Show HN" submission on Hacker News](https://news.ycombinator.com/item?id=29851834). This post done very well, especially compared to [previous submissions](https://news.ycombinator.com/from?site=bookstackapp.com) by myself and others. 
+The post reached the top of Hacker News within the next couple of hours, and I received a bunch of positive & constructive comments. Overall it was a pretty great experience.
+
+Our website traffic hit 13k visitors for the day of the post, up on our usual weekend lull of about 700 visitors per day, then we quickly sloped back down over the next two days:
+
+![BookStack Visitor Analytics Line Chart, Showing a spike on the 8th of Jan](/images/2022/04/bookstack_traffic_hn_week.png)
+
+Since then, we've retained a nice ~36% average increase over February and March:
+
+![BookStack last 6 months of site analytics line chart](/images/2022/04/bookstack_traffic_hn_6m.png)
+
+As touched on above, this increase of traffic had an impact on our GitHub star count:
+
+![Stars per month line chart, showing spike for January 2022](/images/2022/04/stars_per_month.png)
+
+We received 736 new stars in January, A big increase over the previous monthly height of 244.
+
+Overall, it brought a mass amount of new eyes to the project, while providing a nice boost in momentum in terms of ongoing visitors.
+
+If you want to explore some of this data yourself, our [website analytics are open here](https://analytics.bookstackapp.com/bookstackapp.com) and statistics for our GitHub project [can be found here](https://gh-stats.bookstackapp.com/).
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@martinjernberg?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Martin Jernberg</a> on <a href="https://unsplash.com/s/photos/nature-stars?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
index 6d49e0fd4c792af0ca4fa20a0bbb0666b0bac324..23d9d6c1cccfbc4dc0b05e2ebcb50fac762f5acc 100644 (file)
@@ -22,7 +22,7 @@ A new BookStack bug-fix release has now been released to resolve a few issues fo
 Update instructions can be found in the links below. If you're having issues running the update commands you may have to run `composer dump-autoload` followed by `php artisan clear-compiled` from the root BookStack directory.
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.11.1)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.11.1)
 
 #### BookStack in a URL 'Sub-directory'
 
@@ -30,8 +30,8 @@ Due to the changes to links within BookStack the application can now be placed o
 
 Due to the many potentially sensitive application files & scripts within a BookStack instance's folder it should not be installed traditionally in an actual folder subdirectory of another website due to many security concerns. Instead the web server should proxy requests to a BookStack instance.
 
-This will be documented soon but if you're eager to set this up and you have some Nginx knowledge you can follow the posts on [the issue request](https://github.com/ssddanbrown/BookStack/issues/40#issuecomment-238538445) to get going.
+This will be documented soon but if you're eager to set this up and you have some Nginx knowledge you can follow the posts on [the issue request](https://github.com/BookStackApp/BookStack/issues/40#issuecomment-238538445) to get going.
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@michelbosma" target="_blank">Michel Bosma</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@michelbosma" target="_blank">Michel Bosma</a></span>
index aee7ab0f5bd0d79f464371b55e92fa7ccee8d8d1..ed8f3a21adbe729cb43b3169ba1d428934d576f6 100644 (file)
@@ -14,7 +14,7 @@ slug = "beta-bugfix-release-v0-12-1"
 A new bugfix has been released to patch up a few issues found in v0.12.
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.12.1)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.12.1)
 
 It was found that I had accidentally set two shortcuts on the same keys, The draft quick save and inline-code format were both mapped to `ctrl+s`. This has now been updated so that inline code is mapped to `Ctrl+Shift+E`. Also, as part of this bugfix the WYSIWYG editor shortcuts on mac will use the command key instead of the ctrl key to better fit with other Mac shortcuts.
 
@@ -24,4 +24,4 @@ Tables have been updated to prevent words being sliced across lines in FireFox a
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@samdasherx13" target="_blank">Samuel Myles</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@samdasherx13" target="_blank">Samuel Myles</a></span>
index 42d238fcf617c7d9aa6ecacf975f2ce4a79b8940..1102076e342857b37af9be08ce283a5d41a4d2a1 100644 (file)
@@ -14,7 +14,7 @@ description = ""
 A second bugfix release has been put together to patch up a some issues found in v0.12.1.
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.12.2)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.12.2)
 
 #### Fixes & Changes
 
@@ -28,4 +28,4 @@ A second bugfix release has been put together to patch up a some issues found in
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@santosh313" target="_blank">Santosh Maharjan</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@santosh313" target="_blank">Santosh Maharjan</a></span>
index c4bd82fb7877bbc4df08c43b2e7dd3d2b88aa4e9..21c09887d99524441aa0fcfc027265d15dc486b6 100644 (file)
@@ -14,7 +14,7 @@ image = "/images/2016/11/jayden-yoon-bookstack-bugfix-v0-13-1.jpg"
 Due to some critical issues, A bugfix release has been released for BookStack v0.13.
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.13.1)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.13.1)
 
 #### Fixes & Changes
 
@@ -24,4 +24,4 @@ Due to some critical issues, A bugfix release has been released for BookStack v0
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@jaydenyoonzk" target="_blank">Jayden Yoon</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@jaydenyoonzk" target="_blank">Jayden Yoon</a></span>
index 749c05b269f80adcd0c8b4fec8fea7f69bb23c6b..c46cfa4e6e67f0d2460653303547ed2625aba8ad 100644 (file)
@@ -13,12 +13,12 @@ tags = ["Releases"]
 It's been a short while since the last release *(43 days to be exact)* but BookStack v0.10 is finally here. Here are some handy links:
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.10.0)
-* [GitHub milestone](https://github.com/ssddanbrown/BookStack/issues?utf8=%E2%9C%93&q=milestone%3A%22BookStack+Beta+v0.10.0%22+)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.10.0)
+* [GitHub milestone](https://github.com/BookStackApp/BookStack/issues?utf8=%E2%9C%93&q=milestone%3A%22BookStack+Beta+v0.10.0%22+)
 * v0.9 Bugfixes:
-    - [v0.9.1](https://github.com/ssddanbrown/BookStack/releases/tag/v0.9.1)
-    - [v0.9.2](https://github.com/ssddanbrown/BookStack/releases/tag/v0.9.2)
-    - [v0.9.3](https://github.com/ssddanbrown/BookStack/releases/tag/v0.9.3)
+    - [v0.9.1](https://github.com/BookStackApp/BookStack/releases/tag/v0.9.1)
+    - [v0.9.2](https://github.com/BookStackApp/BookStack/releases/tag/v0.9.2)
+    - [v0.9.3](https://github.com/BookStackApp/BookStack/releases/tag/v0.9.3)
 
 Most of the development time for this release was spent implementing a tagging system and overhauling the permissions systems which, although mainly for internal purposes, brings some useful extra functionality.
 
@@ -64,4 +64,4 @@ There is now a new option in settings to allow custom HTML content to be inserte
 * Allowed sorting & searching on the users page.
 * Reworked the database migrations to only use simple non-app code to avoid future breakages.
 * Cleaned some of the settings layouts for better consistency and hid more links when you don't have permissions to click them.
-* Made user names as the bottom of entities linked to user profiles. 
\ No newline at end of file
+* Made user names as the bottom of entities linked to user profiles. 
index cdd0f93a33a3388d38c37cc5202e3054f3a58402..5584bd83bc69b5431b35990edb403715eeef83c2 100644 (file)
@@ -13,7 +13,7 @@ description = ""
 BookStack v0.11 has now been released. This version is a cleanup and bugfix release with a few new handy features to make nicer pages and to help organise books easier. Here are the useful links for this release:
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.11.0)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.11.0)
 
 #### Editor Updates
 ###### Callouts
@@ -58,4 +58,4 @@ By default popular books and chapters are shown otherwise you can search for a p
 * Fixed bug with new chapters having an incorrect initial list priority/ordering set.
 * Fixed page sidebar not reacting to window resize.
 * Fixed bug preventing images from being deleted.
-* Fixed activity list bug causing too many hidden activities.
\ No newline at end of file
+* Fixed activity list bug causing too many hidden activities.
index fb18d3640a5db76c8397fe022be4510e04a72c13..db04f0810111dbfabc742781a1948f053714bd2a 100644 (file)
@@ -14,7 +14,7 @@ slug = "beta-release-v0-12-0"
 BookStack v0.12.0 has now been released bringing a range of new features and bug fixes. Let's get to it:
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.12.0)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.12.0)
 
 ### Edit Summaries
 
@@ -65,4 +65,4 @@ Just want to note another little nice UI change, When a page draft is saved an i
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@videmusart" target="_blank">Syd Wachs</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@videmusart" target="_blank">Syd Wachs</a></span>
index e9822006560d4ed6292b34eaa855b129d974682f..b5e2cc3cef042957869f9a0e6fae17e25dd32f65 100644 (file)
@@ -14,7 +14,7 @@ draft = false
 BookStack v0.13.0 has now been released. This release has taken a while but it did require some large under-the-hood updates and brings a few chunky features. Here are the update links:
 
 * [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
-* [GitHub release page](https://github.com/ssddanbrown/BookStack/releases/tag/v0.13.0)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.13.0)
 
 **Please read the additional information** at the bottom of the update instructions page as there are some changes in v0.13 that will likely require some manual intervention due to new system requirements.
 
@@ -89,4 +89,4 @@ If you would like to keep updated on BookStack blog content you can sign up to t
 
 ----
 
-<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@kazuend" target="_blank">kazuend</a></span>
\ No newline at end of file
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: <a href="https://unsplash.com/@kazuend" target="_blank">kazuend</a></span>
index cfa328b97ae7a961d2660c071c4c6604e316b638..7b9fa93709c8fcece57f56fdaf9c5947e7868c17 100644 (file)
@@ -32,9 +32,9 @@ Please consider that malicious exploitation of this vulnerability may have allow
 
 ### Security Process Updates
 
-When enacting upon the above security issues I noticed that the processes for security concerns could be improved. Details of how to report a sensitive security issue can now be found in the [project readme](https://github.com/BookStackApp/BookStack/tree/master#security).
+When enacting upon the above security issues I noticed that the processes for security concerns could be improved. Details of how to report a sensitive security issue can now be found in the [project readme](https://github.com/BookStackApp/BookStack/tree/development#-security).
 
-For the purpose of notifying admins on security issues, A new mailing list has been created which you can [subscribe to here](http://eepurl.com/glIh8z). 
+For the purpose of notifying admins on security issues, A new mailing list has been created which you can [subscribe to here](https://updates.bookstackapp.com/signup/bookstack-security-updates). 
 
 ### Translations
 
index 5075d52fda9723416b98a4488ba0dc6f9b4452b0..16383b3a8ae3324d3c59b41b42bb28f936b52e2f 100644 (file)
@@ -216,7 +216,7 @@ This new command provides a way to semi-emulate that scenario. If desired, you c
 * Updated Turkish translations. Thanks to [@oykenfurkan](https://github.com/BookStackApp/BookStack/pull/1660). ([#1660](https://github.com/BookStackApp/BookStack/pull/1660))
 * Updated Russian translations. Thanks to [@kostefun](https://github.com/BookStackApp/BookStack/pull/1646). ([#1646](https://github.com/BookStackApp/BookStack/pull/1646))
 * Updated German translations. Thanks to [@ezzra](https://github.com/BookStackApp/BookStack/pull/1503). ([#1503](https://github.com/BookStackApp/BookStack/pull/1503))
-* Updated translations for: Portuguese, Brazilian; Chinese Traditional; Chinese Simplified; Dutch; Italian; Ukrainian; Polish; Spanish; Hungarian; German; Turkish; French; Danish; German Informal; Russian; Korean; Spanish, Argentina. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt).
+* Updated translations for: Portuguese, Brazilian; Chinese Traditional; Chinese Simplified; Dutch; Italian; Ukrainian; Polish; Spanish; Hungarian; German; Turkish; French; Danish; German Informal; Russian; Korean; Spanish, Argentina. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt).
 
 ##### Maintenance
 
index 05b93f0d2ede3d1af98691db78ba5f9678ac32be..22afe6fe5a05c1b0a80d767eacdc69d9bced30f8 100644 (file)
@@ -120,7 +120,7 @@ to formally create a GitHub issue.
 * Updated install instructions and scripts to not install development composer packages. ([#1928](https://github.com/BookStackApp/BookStack/issues/1928))
 * Updated list styles to prevent additional margin/padding showing in nested lists. Thanks to [@MikeyMJCO](https://github.com/BookStackApp/BookStack/pull/1913). ([#1913](https://github.com/BookStackApp/BookStack/pull/1913), [#1911](https://github.com/BookStackApp/BookStack/issues/1911))
 * Updated Russian translations. Thanks to [@kostefun](https://github.com/BookStackApp/BookStack/pull/1885) & [@Statium](https://github.com/BookStackApp/BookStack/pull/1837). ([#1885](https://github.com/BookStackApp/BookStack/pull/1885), [#1837](https://github.com/BookStackApp/BookStack/pull/1837))
-* Updated translations for Vietnamese, Danish, Slovenian, Russian, German Informal; German, French, Czech, Swedish, Spanish, Hungarian, Portuguese, Brazilian, Japanese & Chinese Simplified. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt).
+* Updated translations for Vietnamese, Danish, Slovenian, Russian, German Informal; German, French, Czech, Swedish, Spanish, Hungarian, Portuguese, Brazilian, Japanese & Chinese Simplified. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt).
 * Updated "Intended URL" logic to work when "Public Access" is enabled. Thanks to [@Xiphoseer](https://github.com/BookStackApp/BookStack/pull/1817). ([#1817](https://github.com/BookStackApp/BookStack/pull/1817), [#1706](https://github.com/BookStackApp/BookStack/issues/1706))
 * Fixed error that would throw if a user logs in with GitHub while having has a blank 'name'. ([#1853](https://github.com/BookStackApp/BookStack/issues/1853))
 * Fixed validation issues that could occur on image uploads in some environments. Thanks to [@TBK](https://github.com/BookStackApp/BookStack/pull/1900). ([#1900](https://github.com/BookStackApp/BookStack/pull/1900), [#1570](https://github.com/BookStackApp/BookStack/issues/1570))
index eba6dd05d9ef7ff6075f9b7bfa6ce70ef6882cd5..1b7f5fb5f69fe1ae3bba51b2f997f73b116e91ed 100644 (file)
@@ -166,7 +166,7 @@ from the below languages by the below great Crowdin & GitHub members:
 * Updated code-block insert to handle focus, so code blocks can be inserted smoothly via keyboard alone. ([#1972](https://github.com/BookStackApp/BookStack/issues/1972))
 * Updated namespacing used in tests to avoid warnings on recent versions of composer. ([#1924](https://github.com/BookStackApp/BookStack/issues/1924))
 * Updated Chinese translations. Thanks to [@jzoy](https://github.com/BookStackApp/BookStack/pull/2023). ([#2023](https://github.com/BookStackApp/BookStack/pull/2023))
-* Updated translations for Turkish, Slovenian, Swedish, Spanish, Italian, Russian, German Informal, German, French, Chinese Simplified, Portuguese, Brazilian & Hungarian. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt).
+* Updated translations for Turkish, Slovenian, Swedish, Spanish, Italian, Russian, German Informal, German, French, Chinese Simplified, Portuguese, Brazilian & Hungarian. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt).
 * Updated default .htaccess to allow Authorization header for API usage. Thanks to [@osmansorkar](https://github.com/BookStackApp/BookStack/pull/1908). ([#1908](https://github.com/BookStackApp/BookStack/pull/1908))
 * Updated GitHub authorization library to avoid use of deprecated auth methods. ([#1879](https://github.com/BookStackApp/BookStack/issues/1879))
 * Fixed issue where ordered list numbers could be cut-off. This was most apparent on Safari.([#1978](https://github.com/BookStackApp/BookStack/issues/1978))
index 1f62f89c71f60187cccde3f6cf607a2e99ac1137..8147563abecb85fc72648ac880f16460e094cd0f 100644 (file)
@@ -62,7 +62,7 @@ updates from the great mentioned Crowdin members:
 * Added multi-item select to the book-sort interface. ([#2067](https://github.com/BookStackApp/BookStack/issues/2067))
 * Updated authentication system to prevent admins being logged out when changing authentication type, useful when setting up LDAP or SAML. ([#2031](https://github.com/BookStackApp/BookStack/issues/2031))
 * Updated editor focus so that the title is ready-selected if the default, otherwise the editor is focused. ([#2036](https://github.com/BookStackApp/BookStack/issues/2036))
-* Updated translations for Dutch, Korean, French, Turkish, Spanish. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt). ([#2028](https://github.com/BookStackApp/BookStack/pull/2028), [#2071](https://github.com/BookStackApp/BookStack/pull/2071))
+* Updated translations for Dutch, Korean, French, Turkish, Spanish. Thanks to [Crowdin Users](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt). ([#2028](https://github.com/BookStackApp/BookStack/pull/2028), [#2071](https://github.com/BookStackApp/BookStack/pull/2071))
 * Fixed issue where callout styles could not be cycled through via shortcut when in-callout formatting was selected in the editor. ([#2061](https://github.com/BookStackApp/BookStack/issues/2061))
 * Fixed issue where the selection area was not visible in code blocks or the markdown editor when using dark mode. ([#2060](https://github.com/BookStackApp/BookStack/issues/2060))
 * Fixed issue where callouts and code blocks would overlap floated images. ([#2055](https://github.com/BookStackApp/BookStack/issues/2055))
diff --git a/content/blog/beta-release-v0-30-0.md b/content/blog/beta-release-v0-30-0.md
new file mode 100644 (file)
index 0000000..deb0837
--- /dev/null
@@ -0,0 +1,245 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Release v0.30.0"
+date = 2020-09-20T09:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/library-priscilla-du-preez.jpg"
+description = "The v0.30 release introduces the audit log, adds chapters to the API, improves code editing and more"
+slug = "beta-release-v0-30-0"
+draft = false
++++
+
+Although intended to be a quick release cycle, v0.30 is now here 5 months after the last major release. Sketchy personal health, a poorly pet & a busy day-job workload, combined with constant working-from-home, have reduced the amount of time I could afford to spare working on the project but with normality somewhat returning I present BookStack v0.30 which includes an assortment of enhancements. 
+
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.0)
+
+
+Before we get into the features, just a couple of important advisories:
+
+
+**Security Notice** - Possible Privilege Escalation
+
+Thanks to [@Defelo](https://github.com/BookStackApp/BookStack/issues/2105)
+it was advised that current privilege escalation situations are not made clear when applying role permissions.
+Any user with a "Manage app settings", "Manage users" or "Manage roles & role permissions" system permission 
+assigned to one of their roles could technically alter their own permissions to gain wider access.
+A clear advisory of these cases has been added in the UI in v0.30
+but admins are advised to review which users have these permissions with the above in mind.
+
+
+**LDAP & SAML Group Matching** - Potential Change
+
+Thanks to [@nem1989](https://github.com/BookStackApp/BookStack/issues/2032) it was found that 
+BookStack roles would be matched to LDAP/SAML groups based upon the role display name, which is expected,
+but only those roles with a matching "name" value would be considered for this matching. This "name" field was redundant, 
+and has now been removed, but it would store a cleaned version the first-set name of the role.
+All roles will now be considered before being matched on name which may mean that roles which did not sync before, 
+that would have been expected to based on their name, may now start to sync.
+
+
+### Audit Log
+
+User activity within BookStack is shown across various locations of the system but it's
+always shown to a limited length. Additionally, there are some activities that havn't been
+visible without database access such as item deletions.
+
+As of v0.30 you can now see an audit log interface if you have permission to both "Manage
+System Settings" and "Manage Users". This is an unfiltered list of the activities that are 
+currently logged to the database by BookStack. Here's how this looks:
+
+![BookStack Audit Log](/images/2020/09/bookstack_audit_log.png)
+
+In this interface you're able to set a date range for activities in addition to being able to filter by activity type.
+In future releases we'll look to track more activity types and bring them into this interface.
+
+### Code Block Editing Session History
+
+Many people use BookStack to display and store code snippets so the code block editor can be core to the workflow of 
+many users within the platform. Unfortunately, since the code block editor opened in a popup,
+it was fairly easy to lose changes by clicking the popup close button or by accidentally clicking
+outside the popup.
+
+In v0.30 we've added session history to the code block editor:
+
+![BookStack Code Block Code Session Saving](/images/2020/09/code_block_session_saving.png)
+
+Any event that causes the popup to close will now save a copy of the contents into the browser's session
+history. Within the editor you'll see a "Session History" link, if anything is in the store, with a dropdown
+of times showing when code was saved. Clicking one of those times will update the editor with the code saved
+at that time. Note, This store is temporary and intended for short-term recovery where needed; In most browsers
+this data will be cleared as soon as the browser tab is closed.
+
+
+### Attachment Link Insertion
+
+The process of inserting attachments into your page content has now been streamlined.
+A new link button found on the attachment list, when editing a page, allows you to 
+insert an attachment link, with the correct attachment name, into the page content with a single click.
+
+![BookStack Attachment Link Insert](/images/2020/09/attachment_link_insert.png)
+
+
+On FireFox, or any browser when using the MarkDown editor, you can also drag the attachment card directly 
+into your page content. Unfortunately chromium based browser's drag+drop handling, combined with the WYSIWYG editor's
+own event handling, proved too troublesome to implement this reliably for that environment.
+
+
+### API Update - Chapters
+
+Work continues on the API to bring us chapter endpoints in this release.
+As we have for Books, this includes endpoints for exporting to the same
+formats that we support via the standard UI.
+
+![BookStack API Chapters](/images/2020/09/api_chapters.png)
+
+Next up, we'll be looking to implement endpoints for pages.
+If you've played with the API I'd love to hear your feedback in this [GitHub issue](https://github.com/BookStackApp/BookStack/issues/1852).
+
+### Dark Mode Tweaks
+
+Since releasing dark mode in v0.29 we've had feedback regarding some choices
+made in addition to a good set of bugs being reported and fixed.
+
+When implementing dark mode I made the choice to use CSS filters to alter the saturation
+and brightness of imagery in the hopes it would make content sit within the theme better.
+After feedback I realise this was a bad decision; It's effectively altering core user-content
+which should remain in control of the user/editor. In addition, these filters could massively
+affect the legibility of screenshots and similar text-based imagery. Use of these filters on
+images has been removed in v0.30.
+
+### Removal of Vue.js
+
+I absolutely love Vue.js, I've been a heavy user of the library with it being my go-to JS framework
+since version 1.0 after jumping ship from Angular 1. Therefore I used it to drive some of the more
+dynamic elements of BookStack such as the image manager and attachments interface. Within BookStack, I 
+try to limit JS usage where possible, looking at native back-end solutions before jumping to JS solutions.
+For smaller dynamic tasks I've slowly built up small set of "components" written in fairly basic plain JS for tasks
+such as handling dropdowns and complex select menus. 
+
+The trouble with frameworks such as Vue is that they ideally need to own the DOM from the point they're attached to downwards. 
+That leads to friction with the little JS "components" we had elsewhere as they'd either need to be re-written as a
+Vue component or an adapter would need to be created to "wire" the component into Vue.
+
+As much as I love Vue, it wasn't really needed in BookStack and we were not really using the full power of the framework.
+In v0.30 I've converted the existing Vue usages to a combination of back-end driven logic and an extended form of the plain JS
+components we already had. The removal of Vue brings a small reduction in the initial JS bundle download size in addition
+to a greater reduction of code being ran on each page load, leading to a more responsive interface overall.
+
+As part of these changes I've also spent some time trying to document and standardise
+an approach for these plain JS components [which can be seen here](https://github.com/BookStackApp/BookStack/blob/9e11fc33fa6cf657b35af97a268210ec447c59a7/dev/docs/components.md). I'm slowly updating the older components
+in the system to conform to these changes.
+
+### Removal of Webpack
+
+Unlike Vue.js, I've never really liked Webpack due to the many hours I've wasted trying to integrate
+Webpack based build systems into existing projects. That said, I've always respected the Webpack project and
+it's developers for the developed ecosystem and the sheer amount Webpack is able to do.
+
+As of v0.30 we have removed Webpack from the development flow of BookStack. Instead we're now using
+SASS directly for CSS builds (Thanks [@timoschwarzer](https://github.com/BookStackApp/BookStack/pull/2166) for swapping
+this to the newer dart-sass during this release cycle) and using [esbuild](https://github.com/evanw/esbuild) for JS building & bundling. Our build systems 
+are all simply [npm scripts which can be seen here](https://github.com/BookStackApp/BookStack/blob/9e11fc33fa6cf657b35af97a268210ec447c59a7/package.json#L4-L9).
+
+In addition to a simpler setup, these changes bring some good performance improvements; As a rough example, These changes bring the development build time of both JS & CSS down from about 2.7 seconds to about 1.5 seconds on my development system.
+
+### Failed Access Logging
+
+Thanks to [@benrubson](https://github.com/BookStackApp/BookStack/pull/1881) it's now possible for failed login events
+to be logged. This will function for both the standard email & password login as well as LDAP logins.
+
+To enable this you simple need to define the `LOG_FAILED_LOGIN_MESSAGE` option in your `.env` file like so:
+
+```bash
+LOG_FAILED_LOGIN_MESSAGE="Failed login for %u"
+```
+
+The optional "%u" element of the message will be replaced with the username or email provided in the login attempt
+when the message is logged. By default messages will be logged via the php `error_log` function which, in most
+cases, will log to your webserver error log files.
+
+### Translations
+
+As always our terrific translating tribe continue to provide their awesome efforts as this release brings updates
+to the below languages by the following fantastic Crowdin & GitHub members:
+
+* Orenda (OREDNA) - *Bulgarian*
+* Marek Pavelka (marapavelka) - *Czech*
+* Venkinovec - *Czech*
+* Tommy Ku (tommyku) - *Japanese*, *Chinese Traditional*
+* Michał Bielejewski  (bielej) - *Polish*
+* jozefrebjak - *Slovak*
+* Ikhwan Koo (Ikhwan.Koo) - *Korean*
+* Whay (remkovdhoef) - *Dutch*
+* jc7115 - *Chinese Traditional*
+* 주서현 (seohyeon.joo) - *Korean*
+* nutsflag - *French*
+* Mykola Ronik (Mantikor) - *Ukrainian*
+* ReadySystems - *Arabic*
+* m0uch0 - *Spanish*
+* Rodrigo Saczuk Niz (rodrigoniz) - *Portuguese, Brazilian*
+* HFinch - *German*, *German Informal*
+* cipi1965 - *Italian*
+* brechtgijsens - *Dutch*
+* Emil Petersen (emoyly) - *Danish*
+* Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+* Lowkey (v587ygq) - *Chinese Simplified*
+* Statium - *Russian*
+* Alex Lee (qianmengnet) - *Chinese Simplified*
+* Ali Yasir Yılmaz (ayyilmaz) - *Turkish*
+* sdl-blue - *German Informal*
+* sqlik - *Polish*
+* Julio Alberto García (Yllelder) - *Spanish*
+* Beenbag - *German*, *German Informal*
+* Roy van Schaijk (royvanschaijk) - *Dutch*
+* Simsimpicpic - *French*
+* Zenahr Barzani (Zenahr) - *German*, *Japanese*, *Dutch*, *German Informal*
+* tatsuya.info - *Japanese*
+* fadiapp - *Arabic*
+* Jakub “Jéžiš” Bouček (jakubboucek) - *Czech*
+* [@Honvid](https://github.com/BookStackApp/BookStack/pull/2157) - *Chinese Simplified*
+
+### Full List of Changes
+
+* Added API endpoints for chapters.
+* Added audit log to the settings area. ([#2173](https://github.com/BookStackApp/BookStack/issues/2173), [#1167](https://github.com/BookStackApp/BookStack/issues/1167))
+* Added the ability to insert an attachment link directly into the current editor window. ([#1460](https://github.com/BookStackApp/BookStack/issues/1460))
+* Added session-based code-block editor auto-save to prevent potential loss of content. ([#1398](https://github.com/BookStackApp/BookStack/issues/1398))
+* Added warning wording around role system permissions to indicate what permissions could allow privilege escalation. ([#2105](https://github.com/BookStackApp/BookStack/issues/2105))
+* Added the ability to log login failures to a file. Thanks to [@benrubson](https://github.com/BookStackApp/BookStack/pull/1881). ([#1881](https://github.com/BookStackApp/BookStack/pull/1881), [#728](https://github.com/BookStackApp/BookStack/issues/728))
+* Updated Simplified Chinese translations. Thanks to [@Honvid](https://github.com/BookStackApp/BookStack/pull/2157). ([#2157](https://github.com/BookStackApp/BookStack/pull/2157))
+* Updated WYSIWYG editor css to put editor in it's own layer to improve degraded dark mode performance. ([#2154](https://github.com/BookStackApp/BookStack/issues/2154))
+* Updated Czech translations. Thanks to [@jakubboucek](https://github.com/BookStackApp/BookStack/pull/2238). ([#2238](https://github.com/BookStackApp/BookStack/pull/2238))
+* Updated permission system so that the permission map table does not contain ID's since database limits could be met in scenarios where permissions were automatically refreshed on a frequent basis. ([#2091](https://github.com/BookStackApp/BookStack/issues/2091))
+* Updated to role table in the database to remove a redundant name field which fixes issue where changing a role name would not change the name used to match with LDAP groups. ([#2032](https://github.com/BookStackApp/BookStack/issues/2032))
+* Updated URL slug generation to achieve a much cleaner result when non-ascii characters are used. Thanks to [@drzippie](https://github.com/BookStackApp/BookStack/pull/2165). ([#2165](https://github.com/BookStackApp/BookStack/pull/2165), [#2026](https://github.com/BookStackApp/BookStack/issues/2026), [#1765](https://github.com/BookStackApp/BookStack/issues/1765))
+* Updated error reporting so that not-found errors are not written to the log, causing logs to fill much quicker than expected. ([#2110](https://github.com/BookStackApp/BookStack/issues/2110))
+* Updated dark mode styles to remove filters applied to images so that they display as expected. ([#2045](https://github.com/BookStackApp/BookStack/issues/2045))
+* Removed Vue.js from project & started standardisation of custom basic component system. ([#2202](https://github.com/BookStackApp/BookStack/issues/2202))
+* Replaced dev usage of node-sass with dart-sass. Thanks to [@timoschwarzer](https://github.com/BookStackApp/BookStack/pull/2166). ([#2166](https://github.com/BookStackApp/BookStack/pull/2166))
+* Fixed issue where, upon role delete, users would not be migrated when specified to during role delete flow. ([#2211](https://github.com/BookStackApp/BookStack/issues/2211))
+* Fixed issue where the system would error on upload of images that contain a hash in the name. ([#2161](https://github.com/BookStackApp/BookStack/issues/2161))
+* Fixed scenario where page drafts would show as saved where request would actually fail, leading to loss of data. Added a browser-side storage mechanism for emergency use. ([#2150](https://github.com/BookStackApp/BookStack/issues/2150))
+* Fixed issue where LDAP groups would not sync on initial login due to the email confirmation system taking over before the group sync would run. ([#2082](https://github.com/BookStackApp/BookStack/issues/2082))
+* Fixed issue where the redirect upon login could lead to an external site. ([#2073](https://github.com/BookStackApp/BookStack/issues/2073))
+* Fixed low visibility of horizontal lines when dark mode is in use. ([#2209](https://github.com/BookStackApp/BookStack/issues/2209))
+* Fixed issue where HTML entities would be seen in page preview content. Thanks to [@mr-vinn](https://github.com/BookStackApp/BookStack/pull/2257). ([#2257](https://github.com/BookStackApp/BookStack/pull/2257), [#2114](https://github.com/BookStackApp/BookStack/issues/2114))
+* Fixed issue where previous page content would be indexed upon save instead of the fresh content. ([#2042](https://github.com/BookStackApp/BookStack/issues/2042))
+* Fixed issue where an error would be thrown on SAML logout request from the IdP. ([#2002](https://github.com/BookStackApp/BookStack/issues/2002))
+* Fixed bad pagination styling which would result in invisible numbering. ([#1839](https://github.com/BookStackApp/BookStack/issues/1839))
+* Fixed incorrect and misleading behaviour when saving a comment with no content. ([#1836](https://github.com/BookStackApp/BookStack/issues/1836))
+
+
+### Next Steps
+
+For v0.31 my main focus will be adding pages to the API which will be a bit more involved than the other endpoints we've added so far. That would complete the core API endpoints I wanted to initially implement; After that I'll allow issues to be created to request other API endpoints that people may need.
+
+Now that I've added the audit log I'd like to expand the activities we track to include things such as setting changes, logins & user creations so that admins will be able to review administration operations. As part of that work I'll try to start a deeper scoping into how content notifications could fit into the application.
+
+I've opened a proposal to implement proper "Owner" controls, [which can be seen here](https://github.com/BookStackApp/BookStack/issues/2246). At the moment the permission system has the ability to apply different permissions for someone's own content but this currently relates to the creator. This causes issues in scenarios where someone would create elements, such as a book, on behalf of another user. These changes would mean a "Owner" user would be assigned to each item, the creator by default, but that "Owner" could easily be re-assigned where required. I'll likely implement this as part of the next release cycle unless there are any major concerns. 
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@priscilladupreez?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Priscilla Du Preez</a> on <a href="https://unsplash.com/s/photos/books?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-release-v0-31-0.md b/content/blog/beta-release-v0-31-0.md
new file mode 100644 (file)
index 0000000..4d33d10
--- /dev/null
@@ -0,0 +1,210 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Release v0.31.0"
+date = 2021-01-03T21:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/winter-fox-birger-strahl.jpg"
+description = "We begin 2021 with BookStack v0.31 which comes with recycle bin functionality, the page REST API endpoints and much more"
+slug = "beta-release-v0-31-0"
+draft = false
++++
+
+We kick of this optimistic year with BookStack v0.31 which includes some great additions & updates to existing functionality including
+a new recycle bin system, controllable item ownership, audit log changes, page API endpoints and much more.
+
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.31.0)
+
+
+Just to note, There were a few security releases for v0.30. If you're not upgrading from v0.30.7 be sure to 
+read through the version specific notes on the [updates page](https://www.bookstackapp.com/docs/admin/updates).
+
+
+### Recycle Bin
+
+Ever had an accidental deletion in your instance that you needed to undo? Now you can,
+without having to restore a database backup, using the new recycle bin system. 
+When you delete a shelf, book, chapter or page they'll now be sent to the recycle bin:
+
+
+![Recycle Bin Listing](/images/2021/01/recycle_bin.png)
+
+
+On each item you can choose to restore or permanently delete it as required. By default, Items deleted
+over 30 days ago may be automatically permanently deleted from the recycle bin.
+
+The recycle bin can be accessed via the maintenance page where you'll also be provided with an overview of what is 
+currently in the bin:
+
+![Recycle Bin Maintenance Overview](/images/2021/01/recycle_bin_maintenance.png)
+
+The inclusion of the recycle bin also introduces a change into how chapter deletion works. Previously deleting
+a chapter would cause all child pages to be moved to the parent book. From v0.31, deleting a chapter will send
+the chapter and all child pages to the recycle bin. This aligns with the deletion behaviour of books.
+
+
+### Item Ownership
+
+Since March in 2016 BookStack has had permissions available that permit the owner of content to make certain actions, For scenarios
+such as "user is able to create pages within their own books". While potentially useful, these permissions were hard to 
+use in practice since the owner would always simply be the creator.
+
+In v0.31 the owner is now a separately tracked user, defaulting to the creator. The owner can be changed
+on the permissions view of a shelf, book, chapter or page as shown below:
+
+![Page OwnerShip Change](/images/2021/01/ownership_change.png)
+
+When you delete a user, you'll now be given the option to transfer ownership to another user if required.
+
+These changes should make it much easier to setup scenarios where you have user-owned books where
+they can only create, edit and delete within their own book.
+
+### Audit Log Updates
+
+With the last feature release introducing the audit log, time has been spent this release cycle on expanding the tracked activities
+to include many more events such as logins, user-management actions and settings update actions.
+
+![New Audit Log Activities](/images/2021/01/audit_log_updates.png)
+
+### User List Changes
+
+A common requirement when managing users is to see who's inactive and therefore might need to be removed from the system.
+This was previously tricky to do without direct database queries or careful manual monitoring but now in v0.31
+the latest activity will now be shown on the users list within a sortable column:
+
+![User List with Latest Activity](/images/2021/01/user_list_activity.png)
+
+Since you can sort by this column you can quickly find inactive users. Note, the latest activity date
+reflected is based on the activity tracked in the audit-log, so does not include view/read only events but should 
+include anything that counts as a modification. Activities made before v0.31 may not be reflected.
+
+### New Revision Changes System
+
+When viewing a revision you have the option to preview pages.
+This was done through the [gathercontent/htmldiff](https://github.com/gathercontent/htmldiff) which was great
+but had not been supported in a while and required the PHP Tidy extension which could be tricky to locate and 
+install on some systems. 
+
+In v0.31 we've now switched to [ssddanbrown/htmldiff](https://github.com/ssddanbrown/htmldiff/) which I ported
+from a [c# implementation found here](https://github.com/Rohland/htmldiff.net) which is a port of a [ruby implementation found here](https://github.com/myobie/htmldiff).  Major credit to [@Rohland](https://github.com/Rohland) and [@myobie](https://github.com/myobie) for their original work which I
+have simply ported.
+
+![Revision Changes View](/images/2021/01/changes_view.png)
+
+This new library does not have the PHP Tidy extension requirement so should make installation & maintenance
+easier for some. From my testing this new library has appeared to work without issue but we will have to see
+how it performs in wider use. 
+
+### API Update - Pages
+
+This release brings page endpoints to the REST API. This completes the initial phase
+of the API now that we have CRUD endpoints for shelves, books, chapters and pages.
+
+![Pages API Documentation](/images/2021/01/pages_api.png)
+
+Now the core content parts are in place, I'm open to GitHub issues being created to request
+specific features or endpoints so further actions can be performed. 
+
+To support usage of the API, I've setup a new BookStack api-scripts repository on GitHub:
+https://github.com/BookStackApp/api-scripts. This will be a collection of useful scripts I,
+or others, create as examples or for specific tasks. These can be used directly, or as a base/guide to
+create other scripts.
+
+
+### Iframe & Cookie Security Updates
+
+Over the last 6 months some of the mainstream browsers have added additional protections for cookies,
+restricting the default usage within a third-party context. For BookStack, this meant that access
+through an iframe may not fully work due to cookies being blocked.
+
+In v0.31, we've added additional controls to prevent usage within an iframe. [CSP frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) headers will now be set, and used by modern browsers, to ensure it will only load within an iframe 
+where the parent page is on the same host as BookStack. 
+
+A new `ALLOWED_IFRAME_HOSTS` option, to be used in the `.env` file, can be used to allow iframe access for certain hosts. This can be used like so:
+
+```bash
+# Adding a single host
+ALLOWED_IFRAME_HOSTS="https://example.com"
+
+# Mulitple hosts can be separated with a space
+ALLOWED_IFRAME_HOSTS="https://a.example.com https://b.example.com"
+```
+
+Setting this option will also adjust cookie security so that they can be set in a third-party context, and hence work when inside an iframe.
+
+Details of this have been added to the [security page of the docs](/docs/admin/security/#iframe-control).
+
+
+### Translations
+
+Big thanks to [@Swoy](https://github.com/BookStackApp/BookStack/pull/2336) who as provided Norwegian translations to BookStack for this release.
+In addition, the below list shows the fantastic translators that have made changes since v0.30 and the languages they've updated:
+
+* [@Swoy](https://github.com/BookStackApp/BookStack/pull/2336) - *Norwegian*
+* Andrej Močan (andrejm) - *Slovenian*
+* gilane9_ - *Arabic*
+* Jakub Bouček (jakubboucek) - *Czech*
+* Raed alnahdi (raednahdi) - *Arabic*
+* rcy - *Swedish*
+* Mykola Ronik (Mantikor) - *Ukrainian*
+* m0uch0 - *Spanish*
+* Xiphoseer - *German*
+* 10935336 - *Chinese Simplified*
+* MerlinSVK (merlinsvk) - *Slovak*
+* nutsflag - *French*
+* Kauê Sena (kaue.sena.ks) - *Portuguese, Brazilian*
+* Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+* Vuong Trung Hieu (fpooon) - *Vietnamese*
+* milesteg - *Hungarian*
+* Statium - *Russian*
+* Ghost_chu (dbguichu) - *Chinese Simplified*
+* Ikhwan Koo (Ikhwan.Koo) - *Korean*
+* Marco (cdrfun) - *German*
+* MatthieuParis - *French*
+* Douradinho - *Portuguese, Brazilian*
+* Lowkey (v587ygq) - *Chinese Simplified*
+* Beenbag - *German*
+* ReadySystems - *Arabic*
+* Gaku Yaguchi (tama11) - *Japanese*
+* 孟繁阳 (FanyangMeng) - *Chinese Simplified*
+
+
+
+### Full List of Changes
+
+* Added recycle bin implementation. ([#2283](https://github.com/BookStackApp/BookStack/pull/2283), [#2183](https://github.com/BookStackApp/BookStack/issues/2183), [#280](https://github.com/BookStackApp/BookStack/issues/280))
+* Added Norwegian translations to BookStack. Thanks to [@Swoy](https://github.com/BookStackApp/BookStack/pull/2336). ([#2336](https://github.com/BookStackApp/BookStack/pull/2336))
+* Added ownership system for pages, chapters, books and shelves. ([#2436](https://github.com/BookStackApp/BookStack/pull/2436), [#2246](https://github.com/BookStackApp/BookStack/issues/2246))
+* Added host iframe control with cookie security management. ([#2427](https://github.com/BookStackApp/BookStack/issues/2427), [#2207](https://github.com/BookStackApp/BookStack/issues/2207))
+* Added API endpoints for pages. ([#2382](https://github.com/BookStackApp/BookStack/pull/2382))
+* Added many more activity types to the audit-log. ([#2360](https://github.com/BookStackApp/BookStack/pull/2360), [#1243](https://github.com/BookStackApp/BookStack/issues/1243))
+* Added a sortable "Latest Activity" column to the users list. ([#848](https://github.com/BookStackApp/BookStack/issues/848))
+* Replaced revision diff library so that the php tidy extension is no longer required. ([#2347](https://github.com/BookStackApp/BookStack/issues/2347), [#1553](https://github.com/BookStackApp/BookStack/issues/1553))
+* Updated GitLab authentication to use the `read_user` scope. ([#2359](https://github.com/BookStackApp/BookStack/issues/2359))
+* Updated revision restore to add sensible default change summary text. Thanks to [@rondaa](https://github.com/BookStackApp/BookStack/pull/2353). ([#2353](https://github.com/BookStackApp/BookStack/pull/2353), [#2349](https://github.com/BookStackApp/BookStack/issues/2349))
+* Updated the "Cleanup Images" maintenance option wording for clarity. ([#2352](https://github.com/BookStackApp/BookStack/issues/2352))
+* Updated dev docker setup to install composer dependencies in Docker entrypoint. Thanks to [@timoschwarzer](https://github.com/BookStackApp/BookStack/pull/2298). ([#2298](https://github.com/BookStackApp/BookStack/pull/2298))
+* Updated chapter delete behaviour so pages are removed instead of being moved to the parent book. ([#2164](https://github.com/BookStackApp/BookStack/issues/2164))
+* Updated grid-layout book/shelf item names to better fit into two lines. ([#1469](https://github.com/BookStackApp/BookStack/issues/1469))
+* Updated translations. ([#2439](https://github.com/BookStackApp/BookStack/pull/2439), [#2327](https://github.com/BookStackApp/BookStack/pull/2327))
+* Fixed issue where the export dropdown may show cut-off with options hidden. Thanks to [@shubhamosmosys](https://github.com/BookStackApp/BookStack/pull/2416). ([#2416](https://github.com/BookStackApp/BookStack/pull/2416))
+
+
+### Next Steps
+
+Over the last few months we've had a good number of authentication-based pull requests, in addition to some others, which I've been somewhat ignoring so I'll
+look to spend some time reviewing a few of those.
+
+Now we have the core elements of the API integrated we'll now see what other features people may need. I'm imagining we'd add a few endpoints each future release for a while.
+
+With the API base down and the activity system fleshed out, now may be a good time to implement an outbound web-hook system.
+I'll likely create an implementation proposal so I can ensure we'd be covering the main use-cases required.
+
+PHP 8 support is another thing I'll look to work on over the next release cycle. Some work has been put into this but, due to 
+scale of changes in PHP 8 and the rate that some required packages move at, it's a trickier process than previous new PHP versions.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@bist31?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Birger Strahl</a> on <a href="https://unsplash.com/t/animals?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
index a6421108d08c8546b38ec3d762224a51a10a301c..e657e7b023ac878e7603bf94616a48705c54b71b 100644 (file)
@@ -10,7 +10,7 @@ tags = ["Releases"]
 
 +++
 
-BookStack Beta v0.7.6 has now been released. The release can be found [here on GitHub](https://github.com/ssddanbrown/BookStack/releases/tag/v0.7.6) and [update instructions can be found here](https://github.com/ssddanbrown/BookStack/blob/master/readme.md#updating-bookstack).
+BookStack Beta v0.7.6 has now been released. The release can be found [here on GitHub](https://github.com/BookStackApp/BookStack/releases/tag/v0.7.6) and [update instructions can be found here](https://github.com/BookStackApp/BookStack/blob/development/readme.md#updating-bookstack).
 
 **Here are some important things to note with this update:**
 
@@ -48,4 +48,4 @@ When searching the system you can now put words in double quotes, `"like this"`,
 ### Bug Fixes & Small Updates
 
 * Fixed homepage 'Recent Pages' list showing the same pages as the 'Recently Created Pages' list.
-* Added configration support for Memcached.
\ No newline at end of file
+* Added configration support for Memcached.
index b7a4878eede539a59a1a2302b196c7f1dd4570a3..e0ab44b7b95063a62866d20c7ddf67cf56189fda 100644 (file)
@@ -12,11 +12,11 @@ author = "Dan Brown"
 
 **Update**
 
-[BookStack v0.8.1 has since been released](https://github.com/ssddanbrown/BookStack/releases/tag/v0.8.0) to address some bugs.
+[BookStack v0.8.1 has since been released](https://github.com/BookStackApp/BookStack/releases/tag/v0.8.0) to address some bugs.
 
 ----
 
-BookStack v0.8.0 has now been released!. The release can be found [here on GitHub](https://github.com/ssddanbrown/BookStack/releases/tag/v0.8.0) and [update/install instructions can be found here](https://github.com/ssddanbrown/BookStack/blob/master/readme.md#updating-bookstack). 
+BookStack v0.8.0 has now been released!. The release can be found [here on GitHub](https://github.com/BookStackApp/BookStack/releases/tag/v0.8.0) and [update/install instructions can be found here](https://github.com/BookStackApp/BookStack/blob/development/readme.md#updating-bookstack). 
 
 We realised that we have pretty much been randomly creating the release numbers so far so but from now on we are going to stick to [Semantic Versioning 2.0.0](http://semver.org/) as much as possible. Since this release has some large new features that are upgrade-compatible we're jumping off v0.7.x and going direct to v0.8.0.
 
index 3e22f5b99cc2abacf9f320c188ade154665797b5..c8eb698ef501ff18fe1fa584bca1248245dccea9 100644 (file)
@@ -8,10 +8,10 @@ date = 2016-03-30T16:13:09Z
 
 +++
 
-BookStack v0.8.2 has just been push up to the release branch in the official [GitHub repository](https://github.com/ssddanbrown/BookStack/releases/tag/v0.8.2). This is a bugfix release to fix a few small things up before the next feature release. Update instructions can be found in the [new documentation pages here](https://www.bookstackapp.com/docs/admin/updates).
+BookStack v0.8.2 has just been push up to the release branch in the official [GitHub repository](https://github.com/BookStackApp/BookStack/releases/tag/v0.8.2). This is a bugfix release to fix a few small things up before the next feature release. Update instructions can be found in the [new documentation pages here](https://www.bookstackapp.com/docs/admin/updates).
 
 The biggest update is to the Book, Chapter & Page restrictions that were introduced in version 0.7.6. This has been re-named from 'Restrictions' to 'Permissions' since that's a much more appropriate name for them. Also it was noticed that the 'Restriction' system did not allow a role, with given permissions selected, to perform actions that they did not also have permissions for on a role level. Both role and asset permissions were required before which prevented this system being really useful.
 
-This behaviour has now been amended so that asset-level permissions will now override role-level permissions. This makes total sense since, on assets, you have to specify what actions to allow on a role-by-role basis anyway. More details about this change can be found in my comment on [this issue](https://github.com/ssddanbrown/BookStack/issues/89#issuecomment-203135563).
+This behaviour has now been amended so that asset-level permissions will now override role-level permissions. This makes total sense since, on assets, you have to specify what actions to allow on a role-by-role basis anyway. More details about this change can be found in my comment on [this issue](https://github.com/BookStackApp/BookStack/issues/89#issuecomment-203135563).
 
-One other tweak is a presentational fix in the header. Longer names will be cut down (Or hidden if necessary) to prevent them from dropping down onto a new line and taking up space. Details of this issue can be [found here](https://github.com/ssddanbrown/BookStack/issues/87).
\ No newline at end of file
+One other tweak is a presentational fix in the header. Longer names will be cut down (Or hidden if necessary) to prevent them from dropping down onto a new line and taking up space. Details of this issue can be [found here](https://github.com/BookStackApp/BookStack/issues/87).
diff --git a/content/blog/beta-security-release-v0-30-4.md b/content/blog/beta-security-release-v0-30-4.md
new file mode 100644 (file)
index 0000000..4294e58
--- /dev/null
@@ -0,0 +1,70 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.30.4"
+date = 2020-10-31T16:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/locks-marcos-mayer.jpg"
+description = "This release contains some security fixes to prevent various XSS attacks"
+slug = "beta-release-v0-30-4"
+draft = false
++++
+
+
+XSS and user-injected auto-redirect vulnerabilities have been found within the page content & attachment components of BookStack which BookStack v0.30.4 looks to address. These are primarily a concern if untrusted users can edit content on your BookStack instance.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.4)
+
+
+### Impact
+
+1. A user with permissions to edit a page could insert JavaScript code through the use of `javascript:` URIs within a link or form which would run, within the context of the current page, when clicked or submitted. 
+
+2. A user with permissions to edit a page could insert a particular meta tag which could be used to silently redirect users to a alternative location upon visit of a page.
+
+3. A user with permissions to edit a page could add an attached link which would execute untrusted JavaScript code when clicked by a viewer of the page.
+
+### Patches
+
+The issues were addressed in BookStack v0.30.4. 
+
+Dangerous content may remain in the database. The in-page vulnerabilities will be removed before being displayed on a page but dangerous attachment content will remain if exploited. If you think this could have been exploited you can search for potential cases with the following SQL commands:
+
+```sql
+# XSS within page content:
+select * from pages where html like '%javascript:%';
+
+# Auto-redirect within page content:
+select * from pages where html like '%<meta%';
+
+# XSS in page link attachments:
+select a.name as attachment_name, p.name as page_name, p.id as page_id from attachments a left join pages p on (a.uploaded_to=p.id) where a.path like '%javascript:%';
+```
+
+### Workarounds
+
+Page edit permissions could be limited to only those that are trusted until you can upgrade although this will not address existing exploitation of this vulnerability. 
+
+### References
+
+* [BookStack Beta v0.30.4](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.4)
+* [GitHub Security Page - XSS/Redirect in Page Content](https://github.com/BookStackApp/BookStack/security/advisories/GHSA-r2cf-8778-3jgp)
+* [GitHub Security Page - XSS in Page Attachment](https://github.com/BookStackApp/BookStack/security/advisories/GHSA-7p2j-4h6p-cq3h)
+
+### Attribution
+
+* Thanks to [@PercussiveElbow](https://github.com/PercussiveElbow) for the discovery, reporting, patching and testing of the page-content vulnerabilities.
+* Thanks to Yassine ABOUKIR (https://twitter.com/yassineaboukir/) for the discovery and reporting of the page attachment vulnerability.
+
+### More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@mmayyer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">marcos mayer</a> on <a href="https://unsplash.com/s/photos/lock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-security-release-v0-30-5.md b/content/blog/beta-security-release-v0-30-5.md
new file mode 100644 (file)
index 0000000..f80ebc6
--- /dev/null
@@ -0,0 +1,54 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.30.5"
+date = 2020-12-06T20:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-jon-moore.jpg"
+description = "This release contains some security fixes to prevent phishing and server-side request forgery"
+slug = "beta-release-v0-30-5"
+draft = false
++++
+
+
+Phishing and and server-side request forgery vulnerabilities have been found within BookStack. Release v0.30.5 will remove this server-side request forgery issue while bringing updated wording and advisories to prevent the potential phishing vulnerability.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.5)
+
+
+### Impact
+
+1. A user with permissions to edit a page could set certain image URL's within a page to manipulate functionality in the exporting system, which would allow them to make server side requests and/or have access to a wider scope of files within the BookStack file storage locations. This is primarily a concern if untrusted users are able to edit pages in your instance.
+
+2. A malicious attacker could craft a password reset request with an alternate host address, resulting in a password reset email being sent to someone with an alternate destination. This could be used for phishing attempts with a sight to gain further access if successful. This is a primarily a concern on hosts where requests to unexpected domain names could reach your BookStack instance.
+
+### Patches
+
+Within v0.30.5 the above server-side request forgery vulnerability will no longer exist since that specific functionality was removed. Within v0.30.5 the default state and wording within the provided `.env.example` file was updated to encorage filling of the `APP_URL` parameter (See below).
+
+### Workarounds
+
+To help prevent the potential phishing vulnerability, please ensure you have set the `APP_URL` option in your `.env` file. The value of this should exactly match the base URL you are using to host BookStack.
+
+To prevent exploitation of the server-side request forgery issue, page edit permissions could be limited to only those that are trusted until you can upgrade. 
+
+### References
+
+* [BookStack Beta v0.30.5](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.5)
+* [GitHub Security Page - Server Side Request Forgery](https://github.com/BookStackApp/BookStack/security/advisories/GHSA-8wfc-w2r5-x7cr)
+
+### Attribution
+
+* Thanks to [@PercussiveElbow](https://github.com/PercussiveElbow) for the responsible discovery & reporting of this vulnerability.
+
+### More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@thejmoore?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jon Moore</a> on <a href="https://unsplash.com/s/photos/locks?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-security-release-v0-30-6.md b/content/blog/beta-security-release-v0-30-6.md
new file mode 100644 (file)
index 0000000..f159954
--- /dev/null
@@ -0,0 +1,51 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.30.6"
+date = 2020-12-17T21:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-door-waldemar-brandt.jpg"
+description = "BookStack v0.30.6 has been released to address an issue that could lead to restricted page content being visible in certain circumstances."
+slug = "beta-release-v0-30-6"
+draft = false
++++
+
+
+BookStack v0.30.6 has been released to address an issue that could lead to restricted page content being visible in certain circumstances.
+You should upgrade to this released as soon as possible if you make use of page-level permissions at all.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.6)
+
+
+### Impact
+
+If a chapter was visible to a user, but all of it's pages were made not visible, then the details of these pages could be visible. Within the BookStack interface, the names of the pages and preview content could be seen. If the parent book was exported then this would include the content of the pages that had been restricted.
+
+### Patches
+
+This has been patched in v0.30.6.
+
+### Workarounds
+
+Please update. As a temporary workaround you could ensure that there is at least one other page within a chapter that's visible to users. 
+
+### References
+
+* [BookStack Beta v0.30.6](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.6)
+* [GitHub Issue #2414](https://github.com/BookStackApp/BookStack/issues/2414)
+
+### Attribution
+
+A big thanks to [@cdrfun](https://github.com/cdrfun) for [discovering and reporting](https://github.com/BookStackApp/BookStack/issues/2414) this issue.
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@waldemarbrandt67w?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Waldemar Brandt</a> on <a href="https://unsplash.com/s/photos/lock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-security-release-v0-30-7.md b/content/blog/beta-security-release-v0-30-7.md
new file mode 100644 (file)
index 0000000..f8bc7ed
--- /dev/null
@@ -0,0 +1,51 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.30.7"
+date = 2020-12-18T14:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-aubrey-odom.jpg"
+description = "In continuation of the patches in v0.30.6, BookStack v0.30.7 has been released to address an issue that could lead to restricted page content being made visible in exports."
+slug = "beta-release-v0-30-7"
+draft = false
++++
+
+
+In continuation of the patches in v0.30.6, BookStack v0.30.7 has been released to address an issue that could lead to restricted page content being made visible in exports.
+As with the last release, You should upgrade to this released as soon as possible if you make use of page-level permissions at all. Apologies for the frequency of security releases.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.7)
+
+
+### Impact
+
+The content of pages made non-viewable to a user via permissions, within a visible parent, could be seen via the plaintext export option. Before v0.30.6 this would have applied only to scenarios where all pages within the chapter were made non-visible. In v0.30.6 this would make all pages within the chapter visible.
+
+### Patches
+
+This has been patched in v0.30.7.
+
+### Workarounds
+
+Please update. As a temporary workaround you could make parent chapters/books non accessible.
+
+### References
+
+* [BookStack Beta v0.30.7](https://github.com/BookStackApp/BookStack/releases/tag/v0.30.7)
+* [GitHub Issue #2414](https://github.com/BookStackApp/BookStack/issues/2414)
+
+### Attribution
+
+A big thanks again to [@cdrfun](https://github.com/cdrfun) for [discovering and reporting](https://github.com/BookStackApp/BookStack/issues/2414) this issue.
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@octoberroses?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Aubrey Odom</a> on <a href="https://unsplash.com/s/photos/lock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-security-release-v0-31-4.md b/content/blog/beta-security-release-v0-31-4.md
new file mode 100644 (file)
index 0000000..29b29d5
--- /dev/null
@@ -0,0 +1,32 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.31.4"
+date = 2021-01-16T18:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/gate-masaaki-komori.jpg"
+slug = "beta-release-v0-31-4"
+draft = false
++++
+
+BookStack v0.31.4 has been released. This security release updates the [Laravel framework version](https://blog.laravel.com/security-laravel-62012-7303-released), due to a vulnerability that could occur if request data was crafted and then used in a certain way. While it is not known if such a case exists in BookStack, this release updates the framework as a pre-emptive measure.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.31.4)
+
+### Markdown editing in v0.31
+
+In addition to this security release, A range of patch releases (v0.31.1, v0.31.2 & v0.31.3) have been made available recently
+which largely covers issues in how markdown content is rendered upon save. In BookStack v0.31 I changed the way we render
+markdown content so it's done server-side upon save. This was done so that markdown could be used via the API and to prepare for future changes. These patch releases have worked to better align the abilities of the new back-end renderer and the existing front-end renderer, that you see as a preview when editing a page.
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@gaspanik?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Masaaki Komori</a> on <a href="https://unsplash.com/s/photos/gate?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/beta-security-release-v0-31-5.md b/content/blog/beta-security-release-v0-31-5.md
new file mode 100644 (file)
index 0000000..5c412bb
--- /dev/null
@@ -0,0 +1,26 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "Beta Security Release v0.31.5"
+date = 2021-02-02T21:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-markus-spiske.jpg"
+slug = "beta-release-v0-31-5"
+draft = false
++++
+
+BookStack v0.31.5 has been released. As with the previous release (v0.31.4) this updates the Laravel framework version used to help avoid a potential vulnerability when requests were crafted in a certain manner. While it is not known if such a case exists in BookStack, this release updates the framework as a pre-emptive measure.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v0.31.5)
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@markusspiske?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Markus Spiske</a> on <a href="https://unsplash.com/s/photos/bike-lock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
diff --git a/content/blog/bookstack-in-2021.md b/content/blog/bookstack-in-2021.md
new file mode 100644 (file)
index 0000000..e0f6c32
--- /dev/null
@@ -0,0 +1,168 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "BookStack in 2021"
+image = "/images/blog-cover-images/robin-alfred-kenneally.jpg"
+author = "Dan Brown"
+slug = "bookstack-in-2021"
+draft = false
+date = 2021-12-31T19:30:00Z
++++
+
+Thought it would be nice to take some time out to look back over the last year
+and review how things have progressed. This'll be a relatively high level summary
+but more detailed figures can be found in our [six years of BookStack](/blog/6-years-of-bookstack/)
+post from back in July.
+
+### Development Rate
+
+Development rate in 2021 has been fairly steady. The chart below shows releases, of any type, per month from the start of 2020 through to now:
+
+![2021 Development Rate Chart](/images/2021/12/2021_development_rate.png)
+
+For 2021 we've had releases every single month with 7 feature releases and 30 patch 
+releases. In the last few months, [since leaving my job](https://danb.me/blog/posts/leaving-my-job-to-focus-on-open-source/), I've been able to wrap up a feature release every month thanks to having much more time available to devote to the project.
+I hope to continue this cadence although a focus on some larger upcoming features may hinder this at some level.
+
+### New Features & Enhancements
+
+Below lists many of the major additions we've added to BookStack in 2021
+across the 7 feature & 30 patch releases:
+
+- Tag overview page
+- Favorites system
+- Recycle bin
+- Content ownership
+- Multi-factor authentication
+- OpenID Connect authentication
+- Outgoing webhooks
+- Chapter, book & role copying
+- Custom footer links
+- Logical theme system implementation
+- 8 new languages:
+  - Norwegian, Bosnian, Catalan, Indonesian, Latvian, Portuguese, Lithuanian, Estonian
+- Next/Previous page & chapter navigation
+- A large amount of search enhancements
+  - Tags within search results
+  - More advanced parsing and scoring
+  - Relative usage based-scoring
+- LDAP user avatar import
+- Markdown export
+- Improved accessibility
+  - Usage of new contrast preferences
+  - Addition of "Skip to content" link
+  - Header keyboard navigation overhaul
+- API enhancements
+  - Page endpoint
+  - Search endpoints
+  - Image upload via page markdown/html content
+  - Attachment endpoints
+- Audit log enhancements
+  - Greater event tracking
+  - More search options
+  - Addition of IP address tracking
+- Shelf sorting
+- A new debug view
+- Implementation of Content Security Policy for greater security
+- Laravel framework update from Laravel 6 to Laravel 8
+- PHP 8 & 8.1 support
+
+Personally, my favorite addition has been the "Favorites" system since it was relatively easy to implement
+while having a large affect on my own usage, in allow personal content list curation for quick 
+access.
+
+The developments to the API this year have meant it's now viable for many use-cases, and it's been 
+good to hear feedback of people using this to automate and speed-up processes. The recent addition
+of webhooks, along with the implementation of the logical theme system, mean that BookStack
+is now much more extensible and open for integration.
+
+Some of the most challenging developments this year have been then authentication elements; OpenID Connect and 
+multi-factor authentication. Understanding the relevant specifications, while attempting to understand the 
+various desired user/environment use-cases, takes a lot of time and discovery. It's been personally
+beneficial to learn some new web standards but the effort and time required, while knowing the likely
+limited existing BookStack audience usage, can be demoralizing. Going into 2022 I'll be even more defensive
+when it comes to implementing further authentication features.
+
+Gaining an extra 8 languages is pretty incredible. I can't thank [the translating community](https://github.com/BookStackApp/BookStack/blob/development/.github/translators.txt)
+enough for the work they do to add and update language content.
+
+### Version Numbering Change
+
+Back in April, [as of v21.04](/blog/bookstack-release-v21-04/), we dropped our beta 
+status and moved to a new versioning scheme which follows a year-month style format. 
+This change went pretty smoothly and the new scheme has already been very
+beneficial to me when dealing with support requests, allowing me to instantly know the relative
+age of someone's BookStack instance just by their version number. Looking back, I'm
+happy we made this change.
+
+### YouTube Videos
+
+Producing official BookStack videos has been a new thing for 2021.
+You can [find the YouTube channel here](https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g).
+This has been an interesting new learning process for me as I attempt to improve the presentation
+and quality of these videos, but it's also been quite fun during moments I want to do something
+a little more creative. 
+
+Below is my most recent video, at time of writing, which goes over using webhooks in BookStack:
+
+<iframe width="100%" height="420" src="https://www.youtube.com/embed/_zIp1ruGpoI" title="YouTube video player" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+Having videos on specific BookStack topics has already proved very handy when needing to reference
+certain functionality and processes when supporting others so I'll look to continue producing videos throughout 2022.
+
+### Official Twitter Account
+
+In October I created an [official BookStack twitter account](https://twitter.com/bookstack_app) 
+so people can follow project updates without having to also scroll through 
+cat pictures on my personal handle. This is something I should have done sooner really
+since it's clear that people are more likely to tag the project in their own tweets when 
+an official account exists, providing a network boost affect. 
+
+### Sponsors & Project Funding
+
+Over the last year I've been accepting [sponsorship/donations via GitHub](https://github.com/sponsors/ssddanbrown).
+Initially most of these donations were used to help donate to upstream projects used by BookStack.
+Over the last few months I've stopped expanding that idea, while not being employed, to assess viability
+of working on BookStack full time. 
+
+![GitHub Sponsor Progress, 23% towards $2k goal](/images/2021/12/sponsor_progress.png)
+
+A massive thanks to all those that have sponsored me so far. 
+There's been over 50 unique donators contributing in various amounts. 
+About half of these are one-off donations with the other half being monthly/yearly commitments.
+I set a monthly goal on my profile of $2,000 since that would roughly cover my living costs 
+to work on BookStack full time (If I chose to take that path).
+
+A special massive thanks to JGraph (Known for [draw.io/diagrams.net](https://www.diagrams.net/)) and
+[Stellar Hosted](https://www.stellarhosted.com/) for their especially large monthly sponsorships.
+I've since formalised the larger "Company" sponsorship tiers, to provide logo display on the 
+BookStack website homepage in addition to the project readme, as a token of thanks and to encourage
+further company level monthly sponsorships.
+
+![BookStack Silver and Bronze Sponsors](/images/2021/12/company_sponsors.png)
+
+
+### Website Usage
+
+Throughout 2021 our bookstackapp.com website usage has been fairly flat, with it lowering in the 
+northern hemisphere summer months. Ideally I'd like to see this grow at a greater rate
+as BookStack reaches a larger audience but I find it difficult to gain engagement outside of
+existing BookStack specific channels.
+
+![Website analytics for 2021](/images/2021/12/site_stats.png)
+
+All of our website analytics are accessible here: [BookStack Plausible Analytics Instance](https://analytics.bookstackapp.com/bookstackapp.com).
+
+### Going into 2022
+
+As of now, I'm still planning on spending the next 3 months focused on BookStack. My attention will be
+primarily dedicated to building the new editor. The next stage after that [on the roadmap is](https://github.com/BookStackApp/BookStack#%EF%B8%8F-road-map) is a permission system review but there may be a stage before this to
+heavily restructure the BookStack content database to support future plans in permission & URL handling.
+
+Over these next few months I'll be further assessing my own stance on BookStack work, exploring how maintainership
+fits in with my own career desires & if it's viable to work on BookStack full time.
+
+---
+  
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: &nbsp; <span>Photo by <a href="https://unsplash.com/@alken?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Alfred Kenneally</a> on <a href="https://unsplash.com/s/photos/mountain-river?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
+  </span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-04.md b/content/blog/bookstack-release-v21-04.md
new file mode 100644 (file)
index 0000000..71fdd24
--- /dev/null
@@ -0,0 +1,227 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.04"
+date = 2021-04-09T20:20:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/spring-arno-smit.jpg"
+slug = "bookstack-release-v21-04"
+draft = false
++++
+
+Today is the launch of BookStack v21.04 which is our next feature release after Beta v0.31.
+For this release we're dropping the beta and changing our version scheme as detailed below. 
+This release has no single major feature but is instead focused on a range of fixes, improvements and community contributions.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.04)
+
+
+**Upgrade Notices**
+
+- PHP 7.3 or greater is now required to run BookStack. If you previously installed BookStack using the 18.04 script, please see the details of this release in our [update notes](/docs/admin/updates/#updating-to-v2004-or-higher) for the commands that should help you upgrade to PHP 8.
+- User profile pages, and user-based search filters, now use name-based "slugs" rather than being ID based. Any old links or instructions you may have for these elements may need to be updated.
+
+### Dropping the Beta Status & Changing our Version Scheme
+
+As mentioned above, after collecting [some feedback on GitHub](https://github.com/BookStackApp/BookStack/issues/2570), I'm dropping the beta status from any versions and release notes. 
+In terms of stability and code, nothing really is changing.
+BookStack has been fairly stable for a while now, and I've always attempted to ensure a stable upgrade path on releases.
+My primary reason for keeping the beta status was really to omit a little extra liability, and I was hesitant to label any release as `v1.0` since I could not define a reasonable criteria for `v1.0` that I'd be happy with.
+The beta status was causing issues for some people, where their hosting/platform would not provide any support due to the beta flag.
+
+
+In terms of version numbers, we're jumping from Beta v0.31 to BookStack v21.04. 
+As mentioned above, I couldn't really define a `v1.0` so we're skipping right over it and using the Ubuntu versioning system as inspiration.
+BookStack versions will now be in the format of v`<2 DIGIT YEAR>`.`<2 DIGIT MONTH>`[.`<OPTIONAL PATCH DIGIT>`]. As an example:
+
+- v21.06 _(June 2021 launched feature release)_
+- v21.06.5 (Fifth patch of June 2021 release, even if released in a later month)
+- v22.10 _(October 2022 launched feature release)_
+
+That change is technically compatible with the old versioning, while now providing context about the release date within the version. 
+This can be extremely useful when people reference their BookStack version as it'll instantly provide context of release date without needing to look that up.
+
+### Support for Custom Footer Links
+
+Some scenarios, such as those for GDPR compliance, require links to be shown on every page.
+To support these cases, You can now add custom footer links to your BookStack instance via the settings page:
+
+![BookStack Footer Settings Interface](/images/2021/04/footer_settings.png)
+
+You can specify the URL and text for each link. The description of the setting details how you can use the BookStack translation system to automatically translate some common phrases such as "Privacy Policy" and "Terms of Service".
+
+Once set, these will be shown at the bottom of almost every view including those that are public such as the login & register pages.
+
+![BookStack Footer Display Example](/images/2021/04/footer_display.png)
+
+Thanks to [@james-geiger](https://github.com/BookStackApp/BookStack/pull/1973) for pushing this forward and providing an initial implementation of this feature.
+
+### Search Content by Owner
+
+Within search inputs, and on the search page, it's now possible to search for content belonging to a specific owner:
+
+![BookStack Owner Search Example View](/images/2021/04/search_owned_by.png)
+
+Thanks to [@benediktvolke](https://github.com/BookStackApp/BookStack/pull/2561) for implementing this feature.
+
+### Backend Theme System
+
+Using the existing theme folder system, it's now possible to customize back-end PHP functionality without altering core project files.
+To set this up you'd create a `functions.php` file within your theme folder, in which you can use specific events we expose. For example:
+
+```js
+<?php
+
+use BookStack\Facades\Theme;
+use BookStack\Theming\ThemeEvents;
+
+Theme::listen(ThemeEvents::APP_BOOT, function($app) {
+    \Log::info('BookStack was booted up!');
+});
+```
+
+This system is documented within the project source itself, and can be [viewed on GitHub here](https://github.com/BookStackApp/BookStack/blob/release/dev/docs/logical-theme-system.md).
+
+The intention of this system is provide a mechanism for people to implement back-end functionality that's not suited to be in the core project.
+
+**Note:** This system is considered to be in beta for this release version as I'll be gathering feedback on this initial implementation to potentially make further changes if needed.
+
+### Sort Control within Shelves
+
+Sort options have been previously available within the books & shelves listings.
+This update brings sort options for viewing books within a shelf.
+
+![BookStack Shelf Sort Display Example](/images/2021/04/shelf_sort.png)
+
+This defaults to the user-defined order, managed via editing a shelf, but this allows custom ordering by name, updated date or created date.
+Sort ordering changes are saved to the current user session.
+
+Thanks to [@guillaumehanotel](https://github.com/BookStackApp/BookStack/pull/2515) for providing the initial implementation for this feature.
+
+
+### Audit Log User Filter
+
+The audit log has been updated with the ability to search for activities created by a specific user in the system:
+
+![BookStack Audit Log User Filter Preview](/images/2021/04/audit_log_filter.png)
+
+### Healthcheck Endpoint
+
+A healthcheck endpoint is now included in the system which can be useful in automated and high-availability scenarios.
+This endpoint will perform some simple checks on the database, cache & session, then return the status of each as JSON:
+
+```js
+{"database":true,"cache":true,"session":true}
+```
+
+### LDAP TLS Support
+
+Proper TLS support has now been added to BookStack. This can be enabled by adding `LDAP_START_TLS=true` to your `.env` file.
+When enabled, BookStack will ensure a TLS connection is made. 
+If the TLS connection fails for any reason then an error will be thrown.
+
+Thanks to [@Body4](https://github.com/BookStackApp/BookStack/pull/2376) for pushing this forward with a contributed implementation.
+
+### PHP 8.0 Support
+
+PHP 8.0 is now supported by BookStack. This was a fair bit more involved than previous PHP 7.x support due to PHP 8.0 being a fairly big release
+which included a fair few breaking changes. Due to dependency support, we've had to bump the minimum supported PHP version to PHP 7.3 from 7.2.5.
+
+### Dark Mode by Default Option
+
+It's now possible to set dark mode as the default visual option for an instance. This can be set by adding the following to your `.env` file:
+
+```bash
+APP_DEFAULT_DARK_MODE=true
+```
+
+Note that this may be overridden by any user's session or set preference.
+
+### Translations
+
+This release brings an additional 5 new language options for users to select. Note, Some of these are still in progress so may be somewhat incomplete. Massive thanks to the names listed here for their fantastic work:
+
+- Bosnian - *Thanks to semirte on Crowdin*
+- Catalan - *Thanks to [Ereza](https://github.com/BookStackApp/BookStack/pull/2536) on GitHub*
+- Indonesian - *Thanks to Irfan Hukama Arsyad (IrfanArsyad) on Crowdin* 
+- Latvian - *Thanks to aarchijs, Martins Pilsetnieks (pilsetnieks), Reinis Mednis (Mednis) and HenrijsS on Crowdin*
+- Portuguese - *Thanks to Luís Tiago Favas (starkyller) on Crowdin*
+
+In addition, we've also had a wonderfully large amount of translation updates since the original v0.31 release. Again, A thanks to all names listed below for their incredible efforts:
+
+- [@geins](https://github.com/geins) - *German*
+- [@benediktvolke](https://github.com/benediktvolke) - *German*
+- [@Baptistou](https://github.com/Baptistou) - *French*
+- [@arcoai](https://github.com/arcoai) - *Spanish*
+- Jeff Huang (s8321414) - *Chinese Traditional*
+- Yonatan Magier (yonatanmgr) - *Hebrew*
+- Statium - *Russian*
+- FastHogi - *German Informal; German*
+- Mykola Ronik (Mantikor) - *Ukrainian*
+- Ali Yasir Yılmaz (ayyilmaz) - *Turkish*
+- Ole Anders (Swoy) - *Norwegian Bokmal*
+- Atlochowski (atlochowski) - *Polish*
+- 10935336 - *Chinese Simplified*
+- Simon (DefaultSimon) - *Slovenian*
+- nikservik - *Ukrainian; Russian; Polish*
+- m0uch0 - *Spanish*
+- Simsimpicpic - *French*
+- jackaaa - *Chinese Traditional*
+- Rodrigo Saczuk Niz (rodrigoniz) - *Portuguese, Brazilian*
+- nutsflag - *French*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- Vuong Trung Hieu (fpooon) - *Vietnamese*
+- cipi1965 - *Italian*
+- Pascal R-B (pborgner) - *German*
+- johnroyer - *Chinese Traditional*
+- Boris (Ginfred) - *Russian*
+
+### Full List of Changes
+
+* Added back-end theme system. ([#2639](https://github.com/BookStackApp/BookStack/pull/2639))
+* Added `APP_VIEWS_BOOKSHELF` .env option to set default view type within a shelf. Thanks to [@philjak](https://github.com/BookStackApp/BookStack/pull/2591). ([#2591](https://github.com/BookStackApp/BookStack/pull/2591))
+* Added `owned_by` search filter. Thanks to [@benediktvolke](https://github.com/BookStackApp/BookStack/pull/2561). ([#2561](https://github.com/BookStackApp/BookStack/pull/2561))
+* Added sorting for Books within Shelves. Thanks to [@guillaumehanotel](https://github.com/BookStackApp/BookStack/pull/2515). ([#2515](https://github.com/BookStackApp/BookStack/pull/2515), [#1742](https://github.com/BookStackApp/BookStack/issues/1742))
+* Added user filter to the Audit Log. ([#2472](https://github.com/BookStackApp/BookStack/issues/2472))
+* Added a healthcheck endpoint. ([#2467](https://github.com/BookStackApp/BookStack/issues/2467))
+* Added TLS support to the LDAP system. Thanks to [@Body4](https://github.com/BookStackApp/BookStack/pull/2376). ([#2376](https://github.com/BookStackApp/BookStack/pull/2376))
+* Added .env variable to set default system light/dark mode option. ([#2081](https://github.com/BookStackApp/BookStack/issues/2081))
+* Added the ability to configure custom footer links via the settings screen. Thanks to [@james-geiger](https://github.com/BookStackApp/BookStack/pull/1973). ([#1973](https://github.com/BookStackApp/BookStack/pull/1973))
+* Added create buttons to the books and shelves homepage view options. Thanks to [@philjak](https://github.com/BookStackApp/BookStack/pull/1756). ([#1756](https://github.com/BookStackApp/BookStack/pull/1756))
+* Updated minimum required PHP version to 7.3 and added PHP 8.0 support. ([#2648](https://github.com/BookStackApp/BookStack/issues/2648), [#2388](https://github.com/BookStackApp/BookStack/issues/2388))
+* Updated non-admin reference to users to be slug-based instead of id-based. ([#2626](https://github.com/BookStackApp/BookStack/pull/2626), [#2525](https://github.com/BookStackApp/BookStack/issues/2525))
+* Updated file upload system to remove dots in the filename instead of simply preventing upload. Thanks to [@Hecke29](https://github.com/BookStackApp/BookStack/pull/2611). ([#2611](https://github.com/BookStackApp/BookStack/pull/2611), [#2217](https://github.com/BookStackApp/BookStack/issues/2217))
+* Updated the versioning system used by the project. ([#2570](https://github.com/BookStackApp/BookStack/issues/2570))
+* Updated export format to not include user and revision links in content meta details. ([#2526](https://github.com/BookStackApp/BookStack/issues/2526))
+* Updated docker development environment to work with our php tests and to fix permissions with the node service. Thanks to [@Abijeet](https://github.com/BookStackApp/BookStack/pull/2522). ([#2522](https://github.com/BookStackApp/BookStack/pull/2522), [#2510](https://github.com/BookStackApp/BookStack/pull/2510))
+* Updated the systems for loading code blocks to be quicker & more efficient, especially within the WYSIWYG editor. ([#2518](https://github.com/BookStackApp/BookStack/issues/2518))
+* Updated libraries used for revision diffs to provide much better performance with large amounts of content. ([#2503](https://github.com/BookStackApp/BookStack/issues/2503))
+* Updated user profile password fields to disable autocomplete. Thanks to [@l1n](https://github.com/BookStackApp/BookStack/pull/2484). ([#2484](https://github.com/BookStackApp/BookStack/pull/2484))
+* Updated header so search is more commonly centered. ([#2310](https://github.com/BookStackApp/BookStack/issues/2310))
+* Updated "Move Page" interface to allow efficient keyboard navigation. ([#2064](https://github.com/BookStackApp/BookStack/issues/2064))
+* Updated our test-case files so they are less likely to trigger virus scan systems. ([#1571](https://github.com/BookStackApp/BookStack/issues/1571))
+* Updated WYSIWYG editor to include some bottom padding for readability. ([#1075](https://github.com/BookStackApp/BookStack/issues/1075))
+* Fixed issue where code blocks would not appear when within `<details>` HTML elements, added via the markdown editor. ([#781](https://github.com/BookStackApp/BookStack/issues/781))
+* Fixed issue where the `bookstack:update-url` would not change the URL used for a custom header logo image. ([#2546](https://github.com/BookStackApp/BookStack/issues/2546))
+* Fixed issue where saving without any changes would still result in revisions being created. ([#1846](https://github.com/BookStackApp/BookStack/issues/1846), [#1737](https://github.com/BookStackApp/BookStack/issues/1737))
+* Optimized and cleaned some core permission system components. ([#2633](https://github.com/BookStackApp/BookStack/issues/2633))
+* Removed mentions of unavailable `mail` mail driver from our files. ([#2657](https://github.com/BookStackApp/BookStack/issues/2657))
+
+
+### Next Steps
+
+For the next release I want to continue to resolve some of the pending pull requests on GitHub.
+For a few of these I've been waiting until I can build a back-end customization system, as done in this release, so some requests will be directed to use
+that system instead of including changes in core, which should give the system a good initial test.
+I hope to work through the outstanding SAML issues if I can get a working active directory set-up to replicate and test.
+
+During this cycle I'll be starting to think about the next step on the roadmap, where we'll be reviewing the editors used in BookStack. There probably won't be any coding for the feature during this cycle but I'll be raising proposals and/or RFCs to plan this out.
+
+MFA is an subject that would be good to takle soon but working on auth components can get tiresome, especially as this is an area where people often need configurability.
+
+Since we've now bumped the minimum required PHP version to 7.3, I'll probably start upgrading the codebase from Laravel 6 to Laravel 8 so we gain some new features to play with.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@_entreprenerd?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Arno Smit</a> on <a href="https://unsplash.com/s/photos/spring?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-05.md b/content/blog/bookstack-release-v21-05.md
new file mode 100644 (file)
index 0000000..f5d316f
--- /dev/null
@@ -0,0 +1,224 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.05"
+date = 2021-05-30T14:32:33Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/book-collection-fredmarriage.jpg"
+slug = "bookstack-release-v21-05"
+draft = false
++++
+
+BookStack v21.05 has now been released which brings along new user interface
+features & enhancements including a favourites system and easier in-book
+navigation.
+
+The previous release, BookStack v21.04, also received a bunch of fixes & enhancements during
+the last couple of months so we'll also delve into a few of those changes within this post.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.05)
+
+
+### Favourites System
+
+For quite a while I've wanted to be able to favourite specific items within BookStack for easier future location.
+For this release cycle I decided to be a bit selfish and spend a couple of nights getting this implemented.
+Thanks to all those that provided feedback in regards to the naming in [GitHub issue 2053](https://github.com/BookStackApp/BookStack/issues/2053).
+
+When viewing a shelf, book, chapter or page while logged in you'll see a new "Favourite" action
+situated next to the export action.
+
+![BookStack Favourites Action Link](/images/2021/05/favourites-action.png)
+
+
+Clicking this will add the item to your personal favourites.
+Now when you're on the homepage you'll see a list of your most viewed favourites:
+
+
+![BookStack Favourites Home List](/images/2021/05/favourites-home-list.png)
+
+You can view all your favourites by clicking the header of the homepage list shown above or via
+a link within the profile dropdown menu in the header bar:
+
+
+![BookStack Favourites Link in Profile Menu](/images/2021/05/favourites-profile-menu.png)
+
+This new feature makes it really easy to curate a personal collection of items within the system.
+In the future we may able to use this for other purposes; For example, for watching/notification control.
+
+### Next/Previous Page & Chapter Navigation
+
+Much like a physical book, you'll often want to move to the next or previous page when you get to the
+bottom of the current one. To support this we now have actions below the page content to move to the 
+next, or previous, chapter or page from the current one within the ordering of the current book.
+
+![BookStack Next & Previous Content Navigation Preview](/images/2021/05/next-prev-nav.png)
+
+The links are designed to not be distracting by default, like the other non-content interface, but will
+become more apparent upon interaction. Thanks to [@shubhamosmosys](https://github.com/BookStackApp/BookStack/pull/2511) for providing an initial
+implementation of this and also a thanks to [@james-geiger](https://github.com/BookStackApp/BookStack/issues/1381#issuecomment-540105003)
+for supporting a work-around for this in the mean time.
+
+### Tags Within Search Results
+
+Tags applied to content will now be shown within search results:
+
+![Tags in Search Example](/images/2021/05/tags-in-search.png)
+
+They are shown below the content preview, with a more muted design to prevent them from being too distracting when 
+parsing the search results. Thanks to [@burnoutberni](https://github.com/BookStackApp/BookStack/pull/2487) for an 
+initial implementation of this feature.
+
+### LDAP User Avatar Import
+
+When using LDAP for authentication, you can now specify an attribute for fetching
+a JPEG user avatar image. This is done via a `LDAP_THUMBNAIL_ATTRIBUTE` in your `.env` like so:
+
+```bash
+LDAP_THUMBNAIL_ATTRIBUTE=jpegphoto
+```
+
+BookStack will import and use this thumbnail data as the user avatar upon login or registration
+if that user doesn't have an existing avatar image set.
+Thanks to [@jasonhoule](https://github.com/BookStackApp/BookStack/pull/2320) for providing
+the pull request with the initial implementation of this feature.
+
+### Improved Header Accessibility
+
+In BookStack patch release v21.04.1, the accessibility of the mobile-style header
+controls was significantly improved. Previously you could not use the dropdown menu
+nor the Info/Content layout controls via keyboard alone. These issues
+have now been addressed providing a much more accessible experience at smaller
+screen sizes.
+
+![Improved Header Accessibility Example](/images/2021/05/header-a11y.png)
+
+A big thanks to [@Flameborn and @tspivey for reporting and verifying](https://github.com/BookStackApp/BookStack/issues/2681) 
+the previous limitations.
+
+### Dark Mode Updates
+
+Within the v21.04.3 & v21.04.4 patch releases we addressed a good few outstanding 
+issues with dark mode. This included the leftover layout control light styles showing in 
+dark mode.
+
+<table>
+<tr>
+       <td>
+               <h4>v21.04</h4>
+               <img src="/images/2021/05/dark-mode-header-2104.png" alt="Dark mode interface controls in v21.04">
+       </td>
+       <td>
+               <h4>v21.05</h4>
+               <img src="/images/2021/05/dark-mode-header-2105.png" alt="Dark mode interface controls in v21.05">
+       </td>
+</tr>
+</table>
+
+
+### Translations
+
+As usual, our terrific translating team have been at work to bring the following language updates since
+the original v21.04 release:
+
+- Gerwin de Keijzer (gdekeijzer) - *Dutch; German; German Informal*
+- Jonas Anker Rasmussen (jonasanker) - *Danish*
+- Francesco Franchina (ffranchina) - *Italian*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- Aimrane Kds (aimrane.kds) - *Arabic*
+- kometchtech - *Japanese*
+- Martins Pilsetnieks (pilsetnieks) - *Latvian*
+- nutsflag - *French*
+- Auri (Atalonica) - *Catalan*
+- m0uch0 - *Spanish*
+- 10935336 - *Chinese Simplified*
+- Statium - *Russian*
+- Luís Tiago Favas (starkyller) - *Portuguese*
+- Rodrigo Saczuk Niz (rodrigoniz) - *Portuguese, Brazilian*
+- whenwesober - *Indonesian*
+- aarchijs - *Latvian*
+- Mykola Ronik (Mantikor) - *Ukrainian*
+- [@Jokuna](https://github.com/BookStackApp/BookStack/pull/2716) - *Korean*
+
+
+### Full List of Changes
+
+**Released in v21.05**
+
+* Added shelf/book/chapter/page favourite system. ([#2748](https://github.com/BookStackApp/BookStack/pull/2748))
+* Added previous/next navigation to chapters and pages. Thanks to [@shubhamosmosys](https://github.com/BookStackApp/BookStack/pull/2511). ([#2511](https://github.com/BookStackApp/BookStack/pull/2511), [#1381](https://github.com/BookStackApp/BookStack/issues/1381))
+* Added display of tags within search results. Thanks to [@burnoutberni](https://github.com/BookStackApp/BookStack/pull/2487). ([#2487](https://github.com/BookStackApp/BookStack/pull/2487), [#2462](https://github.com/BookStackApp/BookStack/issues/2462))
+* Added the ability to import JPEG user avatar images during LDAP login/registration. Thanks to [@jasonhoule](https://github.com/BookStackApp/BookStack/pull/2320). ([#2320](https://github.com/BookStackApp/BookStack/pull/2320), [#1161](https://github.com/BookStackApp/BookStack/issues/1161))
+* Updated export meta date format to align with the format used in revisions. ([#2771](https://github.com/BookStackApp/BookStack/issues/2771))
+* Updated drawing manager system to verify host on post messages for additional security. ([#2769](https://github.com/BookStackApp/BookStack/issues/2769))
+* Updated potential external links with `rel="noopener"` for better security . Thanks to [@CorruptComputer](https://github.com/BookStackApp/BookStack/pull/2768). ([#2768](https://github.com/BookStackApp/BookStack/pull/2768))
+* Updated drawing upload error handling to better advise when images are too large for the server. ([#2740](https://github.com/BookStackApp/BookStack/issues/2740))
+* Updated page deletions to also delete related revisions. ([#2668](https://github.com/BookStackApp/BookStack/issues/2668))
+* Updated shelf, book & chapter creation/edit views to autofocus on the name input. ([#1956](https://github.com/BookStackApp/BookStack/issues/1956))
+* Updated translations with latest Crowdin changes. ([#2764](https://github.com/BookStackApp/BookStack/pull/2764))
+* Fixed issue where user search field could stack too early in certain languages. ([#2147](https://github.com/BookStackApp/BookStack/issues/2147))
+
+**Released in v21.04.1 through v21.04.6**
+
+* Added a way to configure options on a social driver, for the initial redirects, through the `Theme::addSocialDriver` system. ([#2759](https://github.com/BookStackApp/BookStack/issues/2759))
+* Added a new `SAML2_IDP_AUTHNCONTEXT` option for SAML2 authentication since the default did not work well for some Windows environments. Thanks to [@ivir](https://github.com/BookStackApp/BookStack/pull/1998). ([#1998](https://github.com/BookStackApp/BookStack/pull/1998))
+* Updated mobile header elements for much better keyboard/screen-reader accessibility. ([#2681](https://github.com/BookStackApp/BookStack/issues/2681))
+* Updated WYSIWYG editor code-block handling provide a more stable undo/redo experience. ([#2602](https://github.com/BookStackApp/BookStack/issues/2602))
+* Updated AWS S3 SDK to fix incompatibility with Minio. ([#2689](https://github.com/BookStackApp/BookStack/issues/2689))
+* Updated translations with latest Crowdin changes. ([#2691](https://github.com/BookStackApp/BookStack/pull/2691), [#2695](https://github.com/BookStackApp/BookStack/pull/2695), [#2719](https://github.com/BookStackApp/BookStack/pull/2719), [#2672](https://github.com/BookStackApp/BookStack/pull/2672), [#2737](https://github.com/BookStackApp/BookStack/pull/2737))
+* Updated migration string column lengths to better fit within restrictive index limits ([#2710](https://github.com/BookStackApp/BookStack/issues/2710))
+* Updated select box styles with to work around default iOS styles causing issues in dark mode. ([#2709](https://github.com/BookStackApp/BookStack/issues/2709))
+* Updated styles of layout view buttons in mobile screen sizes to respect dark mode.
+* Updated image upload behaviour for s3 style uploads to set public permissions as part of the upload request instead of a separate request.
+* Updated Korean translations. Thanks to [@Jokuna](https://github.com/BookStackApp/BookStack/pull/2716). ([#2716](https://github.com/BookStackApp/BookStack/pull/2716))
+* Updated table style handling across exports types to be consistent. ([#2666](https://github.com/BookStackApp/BookStack/issues/2666))
+* Updated S3 ACL setting so ACLs are set via another request, as per pre-v21.04.2, but only when actually use AWS S3. ([#2739](https://github.com/BookStackApp/BookStack/issues/2739))
+* Updated overflowing table content to be consistent. Thanks to [@dopyrory3](https://github.com/BookStackApp/BookStack/pull/2735). ([#2735](https://github.com/BookStackApp/BookStack/pull/2735), [#2732](https://github.com/BookStackApp/BookStack/issues/2732))
+* Updated export system to remove JavaScript used in Custom HTML Head Content to prevent errors or strange behaviour. ([#2490](https://github.com/BookStackApp/BookStack/issues/2490))
+* Improved error messaging when attempting to access a non-existent image file. ([#2696](https://github.com/BookStackApp/BookStack/issues/2696))
+* Fixed issue where "Recently Viewed" would show non-viewed content for new users. ([#2703](https://github.com/BookStackApp/BookStack/issues/2703))
+* Fixed issue where a page could become inaccessible when the creator no longer existed. ([#2687](https://github.com/BookStackApp/BookStack/issues/2687))
+* Fixed HTTP JSON detection when an encoding is in the response JSON content type. ([#2684](https://github.com/BookStackApp/BookStack/issues/2684))
+* Fixed page export error thrown when the created by, or last updated by user, had been deleted. ([#2733](https://github.com/BookStackApp/BookStack/issues/2733))
+* Fixed white borders on layout buttons when in dark mode when using Safari. ([#2728](https://github.com/BookStackApp/BookStack/issues/2728))
+* Fixed error during PDF export in some cases due to incorrect path. ([#2746](https://github.com/BookStackApp/BookStack/issues/2746))
+* Fixed error thrown when saving a markdown page with empty content. ([#2741](https://github.com/BookStackApp/BookStack/issues/2741))
+* Fixed scenario where recent Image upload visibility changes caused issues on hosting where webserver and PHP process group/user differ. ([#2758](https://github.com/BookStackApp/BookStack/issues/2758))
+
+
+### Next Steps
+
+As planned for this release cycle, I've been through a good chunk of the outstanding pull requests on GitHub.
+I'll look to continue this for another release cycle as I'd ideally want to address most of these before
+updating the Laravel framework version, which will soon be due.
+
+To start thinking about our next "Editor Review" roadmap stage, I've [opened a scoping issue on GitHub](https://github.com/BookStackApp/BookStack/issues/2738) to gain feedback on potential options. If you have experience on developing with any of the options posted then feedback is appreciated. I'll be looking to dig a little deeper into this within the next release cycle or two.
+
+
+### Donating to BookStack
+
+Donations are always something I stayed away from. I always thought that people should spend their money
+on more worthy causes, since I didn't really need the money myself nor would I expect it to have a 
+significant impact on the project. That said, people really wanted to provide donations and it was raised
+multiple times. Ideas like bug-bounties were floated but I never wanted money to be a core motivator
+for BookStack features and efforts.
+
+After thinking it through over the last few years, I realised that donations could be used for the following:
+
+1. Support the projects & people that BookStack is built upon by forwarding donations onwards.
+2. Gauge the feasibility of using the income to potentially one day work on open source full time.
+
+The second idea is a bit of a pipe-dream but for now I'll focus on the first, supporting other projects.
+Since the end of last year I've been testing out utilising GitHub sponsors:
+
+<iframe src="https://github.com/sponsors/ssddanbrown/button" title="Sponsor ssddanbrown" height="35" width="116" style="border: 0;" loading="lazy"></iframe>
+
+GitHub sponsors provides a multitude of options for one-off and monthly donations.
+As my donations build up I'll look to support additional projects and people.
+If the donations ever get to a point where they could support me working on open source full time, I'll likely change focus and only support myself
+until I could get to a point of stable sustainability to be able to then support others again.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@fredmarriage?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">freddie marriage</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-08.md b/content/blog/bookstack-release-v21-08.md
new file mode 100644 (file)
index 0000000..5768878
--- /dev/null
@@ -0,0 +1,210 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.08"
+date = 2021-08-31T21:01:23Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lighthouse-dimitry_b.jpg"
+slug = "bookstack-release-v21-08"
+draft = false
++++
+
+Today we release BookStack v21.08, which brings along multi-factor authentication support in addition to a
+number of other nice features. Within this post we'll dive into some of the biggest new changes since the v21.05 release.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.08)
+
+
+**Upgrade Notices**
+
+- **Config & Administration** - The introduction of multi-factor authentication brings the first use of encryption in the platform.
+  This uses the `APP_KEY` value in your `.env` file. Ensure you have this stored safely since it would be required if you ever
+  restore/migrate your instance to another system.
+- **Security/Exports** - During this release cycle it was highlighted that server-side request forgery could be achieved via the 
+  PDF export system. External fetching in the default PDF renderer has been disabled by default. The WKHTMLtoPDF renderer will now 
+  not be used if active. Either of these changes can be overridden by setting `ALLOW_UNTRUSTED_SERVER_FETCHING=true` in your `.env` file.
+  This should only be used were only trusted users can create and export content. To support this we've added permissions that allow disabling of exports per role.
+- **Security/Authentication** - A slight change was made in relation to how email addresses are confirmed. Email confirmations are now primarily checked at point-of-login rather
+  than being checked on every request. Enabling email confirmation, or email domain restrictions, may no longer take action on unconfirmed users right away in the future.
+
+
+### Multi-Factor Authentication
+
+Multi-factor authentication (MFA) can now be enabled for user accounts in BookStack.
+Two different MFA methods are available in this initial release of the feature:
+
+1. TOTP, Labelled as "Mobile App" (Google/Microsoft Authenticator etc...)
+2. Backup Codes (A list of single-use codes)
+
+MFA can be enabled by any user accounts in the system. It can be enforced at a per-role level
+via a new "Requires Multi-Factor Authentication" checkbox found when editing a role:
+
+![View of MFA required checkbox on role edit page](/images/2021/08/mfa-role-permission.png)
+
+When required, users will be forced to setup at least one MFA method upon next login.
+For those with at least one method configured, the system will require an MFA method to be used
+upon login:
+
+![MFA Verificiation View](/images/2021/08/mfa-verify-view.png)
+
+To help in the scenario where someone may lose their MFA credentials, a new system command
+has been added which will clear all MFA methods for the given user:
+
+```bash
+php artisan bookstack:reset-mfa --email=john@example.com
+```
+
+This feature was more effort than expected, partially due to needing to refactor how
+authentication is performed within BookStack, but it should provide a significant 
+benefit to instances that house sensitive content.
+
+### Markdown Export
+
+In addition to the PDF, plaintext and HTML export options, you can now export pages,
+chapters and books as markdown:
+
+![List of page export options including markdown](/images/2021/08/export-options-with-markdown.png)
+
+For pages that have not been written in the markdown editor, we'll attempt to convert
+the underlying HTML content to markdown. 
+This new markdown export option has also been added to the API. Note: This format does
+not contain the image data like the HTML option since readability and cleanliness have taken
+priority.
+
+### Role-Based Export Permissions
+
+A new "Export content" role permission has been added to BookStack. This will be given to
+all roles by default upon upgrade. This new permission allows admins to control who can 
+see and use the "Export" option that's available via the API or on any page, chapter or book.
+
+### "Skip to content" Link
+
+A new accessibility feature was added in v21.05.3, providing a "Skip to main content" link on the
+first element of focus on the page. This link is not visible by default but will appear when focused
+upon, typically by hitting tab after landing on a page.
+
+![View of the Skip to content link](/images/2021/08/skip-to-content-link.png)
+
+### Upload Images in Page Content via API
+
+As of v21.05.1 it's now possible to upload images via a page's HTML content.
+To utilise this, the image just needs to be provided as a base64 encoded data URI within the
+src of an img tag like so:
+
+```json
+{
+       "html": "<p><img src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQAB'></p>"
+}
+```
+
+Upon POST/PUT of this data, BookStack will extract these images out to their own files, as if they had 
+been uploaded via the UI. This is not yet available for markdown content.
+
+### Non-Download Attachment Links
+
+Within BookStack v21.05.2 we added the ability to open/reference attachments without
+forcing the file to be downloaded. This can be useful for files that your browser may support
+like images and PDFs, where they could then open in their own tab instead of being downloaded.
+
+![Preview of a non-download attachment link](/images/2021/08/non-download-attachment-link.png)
+
+This feature is fairly hidden. You can either Ctrl/Cmd+Click the attachment link or add `?open=true` 
+to the end of any current attachment link. I'd like to build this option into the interface at some
+point to make it easier to find & use where desired.
+
+
+### Translations
+
+This release brings a new language option of Lithuanian!
+Big thanks to [@ffranchina](https://github.com/BookStackApp/BookStack/pull/2868) and their translators
+for providing this new language.
+
+Upon that, the below wonderful people have provided translation updates to the shown languages
+since the initial v21.05 release:
+
+- Behzad HosseinPoor (behzad.hp) - *Persian*
+- Jakub Bouček (jakubboucek) - *Czech*
+- syn7ax69 - *Bulgarian; Turkish*
+- Ole Aldric (Swoy) - *Norwegian Bokmal*
+- whenwesober - *Indonesian*
+- m0uch0 - *Spanish*
+- Alexander Predl (Harveyhase68) - *German*
+- scureza - *Italian*
+- Gustav Kånåhols (Kurbitz) - *Swedish*
+- 10 935 336 - *Chinese Simplified*
+- Michał Stelmach (stelmach-web) - *Polish*
+- Francesco Franchina (ffranchina) - *Italian*
+- arniom - *French*
+- 林祖年 (contagion) - *Chinese Traditional*
+- nutsflag - *French*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- Vuong Trung Hieu (fpooon) - *Vietnamese*
+- Irfan Hukama Arsyad (IrfanArsyad) - *Indonesian*
+- semirte - *Bosnian*
+- Luís Tiago Favas (starkyller) - *Portuguese*
+- Statium - *Russian*
+- Gerwin de Keijzer (gdekeijzer) - *German; Dutch*
+- aarchijs - *Latvian*
+- Lis Maestrelo (lismtrl) - *Portuguese, Brazilian*
+- Nathanaël (nathanaelhoun) - *French*
+- A Ibnu Hibban (abd.ibnuhibban) - *Indonesian*
+- Martins Pilsetnieks (pilsetnieks) - *Latvian*
+- Frost-ZX - *Chinese Simplified*
+- Kuzma Simonov (ovmach) - *Russian*
+- Vojtěch Krystek (acantophis) - *Czech*
+- Blaade - *French*
+- Siamak Guodarzi (siamakgoudarzi88) - *Persian*
+
+
+### Full List of Changes
+
+**Released in v21.08**
+
+* Added multi-factor authentication system. ([#2827](https://github.com/BookStackApp/BookStack/pull/2827), [#1118](https://github.com/BookStackApp/BookStack/issues/1118))
+* Added the ability to export content as Markdown. Thanks to [@nikhiljha](https://github.com/BookStackApp/BookStack/pull/2115). ([#2115](https://github.com/BookStackApp/BookStack/pull/2115), [#1717](https://github.com/BookStackApp/BookStack/issues/1717))
+* Added role permissions for exporting content. ([#2899](https://github.com/BookStackApp/BookStack/pull/2899), [#1251](https://github.com/BookStackApp/BookStack/issues/1251))
+* Added an advisory notice on the shelf permissions page regarding the lack of cascade. ([#2876](https://github.com/BookStackApp/BookStack/issues/2876))
+* Added Lithuanian language translations. Thanks to [@ffranchina](https://github.com/BookStackApp/BookStack/pull/2868). ([#2868](https://github.com/BookStackApp/BookStack/pull/2868))
+* Added item parent link in recycle bin restore to make parent item restore easier. Thanks to [@arjvand](https://github.com/BookStackApp/BookStack/pull/2682). ([#2682](https://github.com/BookStackApp/BookStack/pull/2682), [#2594](https://github.com/BookStackApp/BookStack/issues/2594))
+* Added some core opengraph tags to content. Thanks to [@james-geiger](https://github.com/BookStackApp/BookStack/pull/2393). ([#2393](https://github.com/BookStackApp/BookStack/pull/2393), [#2348](https://github.com/BookStackApp/BookStack/issues/2348))
+* Updated blade views to be more consistent and follow a documented convention. ([#2805](https://github.com/BookStackApp/BookStack/issues/2805))
+* Fixed markdown blockquotes not rendering correctly in preview. ([#2858](https://github.com/BookStackApp/BookStack/issues/2858), [#2837](https://github.com/BookStackApp/BookStack/issues/2837))
+* Fixed issue on API where page updates can remove HTML. ([#2856](https://github.com/BookStackApp/BookStack/issues/2856))
+* Fixed inconsistency in list display and nesting. ([#2854](https://github.com/BookStackApp/BookStack/issues/2854))
+* Standardised styling of the codebase. ([#2820](https://github.com/BookStackApp/BookStack/pull/2820))
+
+**Released in v21.05.1 through v21.05.4**
+
+* Added base64 image extraction within page content. Thanks to [@awarre](https://github.com/BookStackApp/BookStack/pull/2700). ([#2700](https://github.com/BookStackApp/BookStack/pull/2700), [#2631](https://github.com/BookStackApp/BookStack/issues/2631))
+* Added Croatian translations. Thanks to [@ffranchina](https://github.com/BookStackApp/BookStack/pull/2784). ([#2784](https://github.com/BookStackApp/BookStack/pull/2784), [#2785](https://github.com/BookStackApp/BookStack/pull/2785))
+* Added VB.NET code block highlighting option. ([#2869](https://github.com/BookStackApp/BookStack/issues/2869))
+* Added a "Skip to content" link as first page focus item for accessibility use. ([#2810](https://github.com/BookStackApp/BookStack/issues/2810))
+* Added the ability to serve attachments without forcing downloads. ([#2791](https://github.com/BookStackApp/BookStack/pull/2791))
+* Updated item permission roles list to be sorted alphabetically. ([#2782](https://github.com/BookStackApp/BookStack/issues/2782))
+* Updated social account detachment to have CSRF protection. ([#2808](https://github.com/BookStackApp/BookStack/issues/2808))
+* Updated PHP dependency versions.
+* Updated translations with latest changes from Crowdin. ([#2790](https://github.com/BookStackApp/BookStack/pull/2790))
+* Merged in latest Crowdin translations. ([#2787](https://github.com/BookStackApp/BookStack/pull/2787), [#2777](https://github.com/BookStackApp/BookStack/pull/2777))
+* Improved audit log user select list stability. ([#2863](https://github.com/BookStackApp/BookStack/issues/2863))
+* Fixed incorrect styling of favourites sidebar when using a non-default homepage option. ([#2783](https://github.com/BookStackApp/BookStack/issues/2783))
+* Fixed issue where empty HTML comments could cause errors. ([#2804](https://github.com/BookStackApp/BookStack/issues/2804))
+* Extracted not found text into it's own view for easier overridding ([58117bc](https://github.com/BookStackApp/BookStack/commit/58117bcf2d91b72620de3e34b0daa705da519f5e))
+* Fixed issue where translations system may attempt to load from the root directory when a theme was not in use. ([#2836](https://github.com/BookStackApp/BookStack/issues/2836))
+* Fixed issue where user profile pages item "View All" links used ids hence did not link to proper searches. ([#2857](https://github.com/BookStackApp/BookStack/issues/2857))
+
+
+### Next Steps
+
+This will likely be my last feature release before [I leave my current job](https://danb.me/blog/posts/leaving-my-job-to-focus-on-open-source/)
+and start focusing on BookStack for a while. For the next month or so I'll just be sneaking in bugfixes and minor improvements as patch releases.
+
+Over the last couple of releases I've made good progress in merging in pending pull requests, so I'll now look to upgrade
+the framework of BookStack from Laravel 6 to Laravel 8. As part of this I'll probably do some more cleanup of the codebase.
+
+I'm not sure what I'll be starting with once I'm working on BookStack full time. The search system is in much need of improvement
+so that may be the first challenge I tackle.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@dimitry_b?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Dimitry B</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-10.md b/content/blog/bookstack-release-v21-10.md
new file mode 100644 (file)
index 0000000..114c435
--- /dev/null
@@ -0,0 +1,222 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.10"
+date = 2021-10-25T14:20:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/gate-benofthenorth.jpg"
+slug = "bookstack-release-v21-10"
+draft = false
++++
+
+October brings us BookStack v21.10. This release is primarily intended to wrap up a few 
+loose ends before we make more substantial framework changes, but it does bring with
+it a new authentication option in addition to some new API endpoints.
+In the below we'll dive into many of the new features and improvements added
+[since v21.08](/blog/bookstack-release-v21-08/).
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.10)
+
+
+**Upgrade Notices**
+
+- **Security Releases** - There were some security vulnerabilities found during the life of 
+  v21.08. See the [v21.08.2](/blog/bookstack-release-v21-08-2/) and
+  [v21.08.5](/blog/bookstack-release-v21-08-5/) posts for more details.
+- **Content Security Policy** - v21.08.2 introduced the use of BookStack-applied CSP headers. These
+  could potentially conflict with any CSP headers set a server-level. The use of these are detailed
+  in the post below and details of the headers can be found on the [security page here](/docs/admin/security/#csp).
+
+### OpenID Connect Authentication
+
+v21.10 brings with it the option of using OpenID Connect (OIDC) as a primary authentication method
+within BookStack.
+This allows authentication integration with a wide range of providers that support the OIDC standard. 
+The implementation includes basic auto-discovery of endpoints & keys for easier configuration.
+During development it has been tested with Okta, KeyCloak and Auth0. 
+
+![BookStack OpenID Connect Login View](/images/2021/10/oidc.png)
+
+For this initial implementation we don't yet support group sync but I've opened up a
+[GitHub issue here](https://github.com/BookStackApp/BookStack/issues/3004) to gain feedback on how
+identity providers supply groups. Please contribute feedback on that issue if you desire OIDC group support.
+
+You can find documentation on [using OpenID Connect here](/docs/admin/oidc-auth/).
+
+### API Updates
+
+#### Attachment Endpoints
+
+A new set of API endpoints have been introduced to support the management of attachments. 
+Full CRUD & listing operations are supported for both file-upload and external-link style
+attachments.
+
+![Attachment API Endpoints View in API docs](/images/2021/10/attachment-api-endpoints.png)
+
+#### Image Upload via Markdown Content
+
+Since v21.05.1 it has been possible to upload images as part of an API page update or create operation,
+via embedding the image as a data-uri within HTML content. This release builds upon that to bring 
+the same functionality to markdown content on the same API endpoints; For example:
+
+```markdown
+![My image](data:image/png;base64,ABC123...)
+```
+
+### TOTP URL During MFA Setup
+
+Since the TOTP MFA system was added to BookStack in the last feature release, it was reported
+that some TOTP-handling services require a URL, or secret code, instead of a QR code. To support
+this, as of v21.08.1, we now show the TOTP URL below the QR code, which the secret can be copied out from 
+if needed.
+
+![Preview of TOTP URL input within MFA TOTP Setup process](/images/2021/10/totp-url.png)
+
+### IP Address in Audit Log
+
+Within BookStack v21.08.4 the audit log was updated to now record and show the related user IP address 
+for activities. Note, if you're using a reverse proxy in front of BookStack you may need to configure
+the `APP_PROXIES` .env option [as shown here](https://github.com/BookStackApp/BookStack/blob/9c2b8057ab7b744c0824a9a3e48c3ccd36b8c103/.env.example.complete#L45-L51) otherwise the reported IP
+address may be that of the proxying system instead of the user.
+
+![IP Address shown in Audit Log View](/images/2021/10/audit-log-ip.png)
+
+Thanks to [@johnroyer](https://github.com/BookStackApp/BookStack/pull/2936) for the addition of this feature.
+
+### Smarter Concurrent Editing Detection & Warnings
+
+Thanks to [@MatthieuParis](https://github.com/BookStackApp/BookStack/pull/2877), BookStack will now run
+additional potential editor conflict detection upon draft saves whereas previously this would only run upon the
+start of an editing session. This should prove especially useful when someone has the editor open for 
+a while, since there's now a much greater chance of being alerted if someone else starts editing the
+same page. This enhancement was added as part of v21.08.5.
+
+![Preview of warning message shown on editor conflict detection](/images/2021/10/editor-conflicts.png)
+
+### New Debug View
+
+In v21.08.6 a new BookStack specific debug view was introduced.
+This was introduced to limit the accidental sharing of errors and confidential details, while
+providing important details & potentially helpful BookStack specific resources to the admin
+attempting to debug the problem at hand.
+
+![Preview of the page BookStack error debug view](/images/2021/10/debug-view.png)
+
+
+### Translations
+
+Another feature release, another language added to BookStack. A massive thanks to Indrek Haav for
+adding Estonian to BookStack!
+
+As usual, there's been a bunch of translation updates via CrowdIn since the last feature release.
+Thanks a bunch to all those listed below for their great continued work!
+
+- Indrek Haav (IndrekHaav) - *Estonian*
+- na3shkw - *Japanese*
+- Nicolas Pawlak (Mikolajek) - *French; Polish; German*
+- Michał Lipok (mLipok) - *Polish*
+- Hl2run - *Slovak*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- FastHogi - *German; German Informal*
+- Statium - *Russian*
+- Giancarlo Di Massa (digitall-it) - *Italian*
+- Thomas Hansen (thomasdk81) - *Danish*
+- Ngo Tri Hoai (trihoai) - *Vietnamese*
+- Luís Tiago Favas (starkyller) - *Portuguese*
+- Francesco Franchina (ffranchina) - *Italian*
+- Radim Pesek (ramess18) - *Czech*
+- aarchijs - *Latvian*
+- 10935336 - *Chinese Simplified*
+- m0uch0 - *Spanish*
+- nutsflag - *French*
+- anastasiia.motylko - *Ukrainian*
+- M Nafis Al Mukhdi (mnafisalmukhdi1) - *Indonesian*
+- 慕容潭谈 (591442386) - *Chinese Simplified*
+
+### Introduction of Content Security Policy
+
+Within v21.08.2 [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+headers were added to the responses served by BookStack.
+This massively helps reduce the impact of a wide range of potential XSS vulnerabilities by restricting
+the types of scripts that can run within the content of a page.
+
+Content within the "Custom HTML Head Content" is automatically parsed to be correctly tagged so it adheres
+to the fairly strict policy set. Any custom script additions you've made may need adjustment.
+
+Details of headers set can be found in the [CSP section of our security page](/docs/admin/security/#csp).
+
+### SAML 2 Enhancements
+
+The SAML 2.0 authentication system has received some attention in this release.
+It has been found that logging in via
+SAML could lose the original intended location context of a user, leading to them being redirect to the 
+homepage after login instead of the page they actually wanted to visit. This was due to a change in cookies
+causing the user's session to be lost during the SAML flow. This release tweaks the flow so the session
+is kept for correct expected redirection.
+
+Upon the above, A couple of new `.env` options have been introduced to allow the configuring of service
+provider certificate and key:
+
+```bash
+# Service Provider Certificate & Key (Optional)
+# Providing these will provide key data within BookStack's metadata endpoint
+# while implicitly enabling signing on Authn and Logout requests.
+SAML2_SP_x509=<cert_data>
+SAML2_SP_x509_KEY=<key_data>
+```
+
+These options were primarily added to help fix single-logout-service requests when using 
+ADFS.Thanks to [@theodor-franke](https://github.com/BookStackApp/BookStack/pull/2902) for helping by doing the initial work to implement these changes.
+
+
+### Full List of Changes
+
+**Released in v21.10**
+
+* Added OpenID Connect authentication option. Thanks to [@jasperweyne](https://github.com/BookStackApp/BookStack/pull/2169). ([#2960](https://github.com/BookStackApp/BookStack/pull/2960), [#2169](https://github.com/BookStackApp/BookStack/pull/2169), [#1390](https://github.com/BookStackApp/BookStack/issues/1390), [#1157](https://github.com/BookStackApp/BookStack/issues/1157))
+* Added Attachment API endpoints. ([#2986](https://github.com/BookStackApp/BookStack/pull/2986), [#2942](https://github.com/BookStackApp/BookStack/issues/2942))
+* Added Estonian language to BookStack via Crowdin. ([#2979](https://github.com/BookStackApp/BookStack/issues/2979))
+* Added support for SAML2 SLS signing to help address issues with ADFS. Thanks to [@theodor-franke](https://github.com/BookStackApp/BookStack/pull/2902). ([#2902](https://github.com/BookStackApp/BookStack/pull/2902))
+* Added support for base64 image content within markdown text via page POST/PUT. ([#2898](https://github.com/BookStackApp/BookStack/issues/2898))
+* Updated translations from Crowdin contributors. ([#2983](https://github.com/BookStackApp/BookStack/pull/2983))
+* Updated SAML ACS post flow to retain user session and therefore redirect to the correct location upon login. ([#2996](https://github.com/BookStackApp/BookStack/pull/2996), [#2552](https://github.com/BookStackApp/BookStack/issues/2552))
+* Fixed padding within book-tree sidebar items. Thanks to [@ffranchina](https://github.com/BookStackApp/BookStack/pull/3000). ([#3000](https://github.com/BookStackApp/BookStack/pull/3000))
+
+**Released in v21.08.1 through v21.08.6**
+
+* Added custom whoops-based debug view which fixes issue where debug view would not show content due to CSP rules. ([#2977](https://github.com/BookStackApp/BookStack/pull/2977), [#2976](https://github.com/BookStackApp/BookStack/issues/2976))
+* Added throttling to password reset requests. ([ca764ca](https://github.com/BookStackApp/BookStack/commit/ca764caf2d55a5c9bac61718d656423b0c3a060b))
+* Added IP address to tracked activities and displayed in audit log. Thanks to [@johnroyer](https://github.com/BookStackApp/BookStack/pull/2936). ([#2936](https://github.com/BookStackApp/BookStack/pull/2936), [#2747](https://github.com/BookStackApp/BookStack/issues/2747))
+* Added the option to use database table prefixes. Thanks to [@floviolleau](https://github.com/BookStackApp/BookStack/pull/2935). ([#2935](https://github.com/BookStackApp/BookStack/pull/2935))
+* Added concurrent page editing warnings upon draft save events. Thanks to [@MatthieuParis](https://github.com/BookStackApp/BookStack/pull/2877) ([#2877](https://github.com/BookStackApp/BookStack/pull/2877))
+* Allowed the use of content includes when using a custom homepage.
+* Updated DOMPDF chroot directory to prevent potential unintended file access. ([#2965](https://github.com/BookStackApp/BookStack/pull/2965))
+* Updated TOTP setup flow to display a URL of the QR code contents during setup for non-QR scanning usage. ([#2908](https://github.com/BookStackApp/BookStack/issues/2908))
+* Updated translations with latest content from Crowdin. ([#2926](https://github.com/BookStackApp/BookStack/pull/2926), [#2915](https://github.com/BookStackApp/BookStack/pull/2915), [#2906](https://github.com/BookStackApp/BookStack/pull/2906), [#2980](https://github.com/BookStackApp/BookStack/pull/2980), [#2953](https://github.com/BookStackApp/BookStack/pull/2953))
+* Fixed broken page ordering on various views. ([#2905](https://github.com/BookStackApp/BookStack/issues/2905))
+* Fixed vulnerability where a malicious user with page edit access could enter script that would execute upon page view.
+* Fixed certain "Custom HTML Head Content" being incorrectly altered or converted. ([#2923](https://github.com/BookStackApp/BookStack/issues/2923), [#2914](https://github.com/BookStackApp/BookStack/issues/2914))
+* Converted old test cases to remove reliance on BrowserKit. ([#2928](https://github.com/BookStackApp/BookStack/pull/2928))
+* Fixed incorrect audit log detail on social account sign-in. ([#2930](https://github.com/BookStackApp/BookStack/issues/2930))
+* Fixed issue where QR codes were not readable when using dark mode. ([#2925](https://github.com/BookStackApp/BookStack/issues/2925))
+* Fixed issue where TOTP setup would provide guest email address upon QR code scan when MFA setup was enforced at login. ([#2971](https://github.com/BookStackApp/BookStack/issues/2971))
+
+
+### Next Steps
+
+This release marks the first feature release since [I left my job](https://danb.me/blog/posts/leaving-my-job-to-focus-on-open-source/) to focus on BookStack and other bits
+for a while. My main focus of v21.10 was to work through some of the challenging
+& time consuming authentication elements that have been on the backlog for a while. 
+Having the extra time to dedicate to these has been helpful to perform the discovery and learning
+required without frustratingly consuming many-a-weekend.
+
+Now I've reduced some of the PR backlog, initial focus going into this week will be on 
+upgrading the codebase framework from Laravel 6 to Laravel 8 (As mentioned the "Next Steps"
+of the last two feature release posts). Once this upgrade is done I'll look to test things out
+via making improvements to existing systems. Both the search system and tagging capabilities are
+in need of some attention and hence is where I may spend some time.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@benofthenorth?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ben Griffiths</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-11.md b/content/blog/bookstack-release-v21-11.md
new file mode 100644 (file)
index 0000000..89de99b
--- /dev/null
@@ -0,0 +1,176 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.11"
+date = 2021-11-16T10:04:45Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/autumn-road-sebastian_unrau.jpg"
+slug = "bookstack-release-v21-11"
+draft = false
++++
+
+Today we release BookStack v21.11 which focuses on a couple of areas that have gone
+untouched for a while; Those areas being tags and the site-wide search system. These changes
+sit upon more substantial framework upgrade work that has occurred this release cycle.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.11)
+
+
+**Upgrade Notices**
+
+- **Security Releases** - There were some security vulnerabilities found during the life of 
+  v21.10. See the [v21.10.1](/blog/bookstack-release-v21-10-1/), [v21.10.2](/blog/bookstack-release-v21-10-2/) and
+  [v21.10.3](/blog/bookstack-release-v21-10-3/) posts for more details.
+- **API Changes** - As of v21.11 any dates in API responses will be formatted as per ISO-8601, with `2019-12-02T20:01:00.283041Z` reflecting an example of this format. You may need to review any of your scripts that utilise dates from API responses.
+- **Upload Limit** - System file upload limits are now configured using a `FILE_UPLOAD_SIZE_LIMIT` option in your 
+  `.env` file. This value is specified as an integer and represents the max upload size in MegaBytes. This defaults to 50MB. This replaces the old `window.uploadLimit` HTML head option that could be set.
+- **Search Index Changes** - As detailed below, there have been search indexing and scoring changes in v21.11. 
+  It's recommended to run `php artisan bookstack:regenerate-search` to ensure a consistent search experience and take
+  advantage of these changes.
+- **Logout Endpoints** - Logout endpoints have now changed to be CSRF protected POST endpoints instead of GET endpoints. If you were using these for any external purposes you may now need to implement an alternative workflow.
+
+
+### New Tags Overview
+
+A listing of used tags can now be found within BookStack. This view initially shows all used
+tag names along with counts of usage, broken down by item type (Total, page, chapter, book, shelf):
+
+![Tags Overview Page Preview](/images/2021/11/tags-page.png)
+
+Clicking the tag name, or the counts, will start a search for that particular tag and item type.
+Shown on the right is the count of unique values used against the tag name. Clicking this will take
+you to a similar view for that specific tag name, displaying all the values used for that tag.
+
+This view can be accessed via the actions menu when viewing all books or shelves in the system.
+
+### Search System Enhancements
+
+During this release cycle I decided to pay special attention to the search system.
+The most obvious change is an improvement in how search results are displayed.
+Item names, descriptions and tags will reflect the searched terms and show them in bold
+with some surrounding context:
+
+![Search Enhancements Preview](/images/2021/11/search-results.png)
+
+Upon this aesthetic change, which should help visual parsing of results, the following
+improvements have been made to the search indexing and scoring system:
+
+- Terms searched will now have their scores relatively adjusted based upon frequency
+  in the database. This should help prevent common, smaller terms causing so much noise in results.
+- Page content is now parsed so that a score boost is given to terms within content headings.
+- The score boost for terms in item titles has been significantly increased.
+- Standard terms will now match against the names and values of tags.
+- Search terms that had issues, due to containing certain delimiters (For example IP addresses), will
+  now be auto-converted to become an "exact" search term.
+- The regenerate-search command will now report some level of progress to the user as it runs.
+
+Put together, these changes should result in a big overall improvement to the search system and provide
+much more accurate results in a format that's easier to read.
+
+### Search API Endpoints
+
+As is common, the API has received new functionality. A "Search" endpoint has been added which allows
+you to run queries against items within BookStack using [the same filters and options](/docs/user/searching/)
+available when using the main search bar within the interface. 
+
+![Search API Preview](/images/2021/11/search-api.png)
+
+The behavior of this endpoint is a bit quirky compared to others so ensure you
+read the documentation carefully if intending to use this.
+
+### Framework Upgrades
+
+As an early part of this release I worked to upgrade our framework from Laravel 6 to Laravel 8.
+To help this upgrade I used [Laravel shift](https://laravelshift.com/) to automate much of the busy 
+work. Moving to Laravel 8 puts us on the latest release for the first time in quite a while, and means
+that we can take advantage of the latest framework features where needed. There won't really be a
+noticeable impact to users but it should make development more pleasant while setting us up
+to eventually move to the next Laravel long-term-support release.
+
+A big thanks to the Laravel team, especially for their support on the LTS releases which has allowed
+us to retain a steady and feasible upgrade path for users in terms of system requirements.
+
+### Translations
+
+Thanks once again to our transcendent translating team. Since the last feature release the 
+below members have been doing fantastic work on Crowdin to keep text translated and up-to-date:
+
+- jozefrebjak - *Slovak*
+- Indrek Haav (IndrekHaav) - *Estonian*
+- Martins Pilsetnieks (pilsetnieks) - *Latvian*
+- na3shkw - *Japanese*
+- Gerwin de Keijzer (gdekeijzer) - *Dutch*
+- m0uch0 - *Spanish*
+- nutsflag - *French*
+- sulfo - *Danish*
+- Michał Lipok (mLipok) - *Polish*
+- Raukze - *German*
+- Nicolas Pawlak (Mikolajek) - *French; German; Polish*
+- zygimantus - *Lithuanian*
+- aarchijs - *Latvian*
+
+
+### Official Twitter & YouTube Channel
+
+Over the last month I've spent a bit of time focusing on some of the higher-level project elements.
+As part of this I've set-up an official Twitter account for BookStack: [@bookstack_app](https://twitter.com/bookstack_app). This means you can follow project updates and progress without having to also scroll 
+past pictures of my cat.
+
+I've also created a [BookStack YouTube channel](https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g). I mentioned wanting to record some project videos in the "[A Year of BookStack](/blog/1-year-of-bookstack/)" blogpost; 5 years later I've finally started on these.
+Currently there's just two videos to guide installation options but more should be coming with improved audio quality.
+
+<iframe width="560" height="315" src="https://www.youtube.com/embed/ShqUjt33uOs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe>
+
+<iframe width="560" height="315" src="https://www.youtube.com/embed/dbDzPIv8Cf8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe>
+
+### Full List of Changes
+
+**Released in v21.11**
+
+* Added a new tag view. ([#3042](https://github.com/BookStackApp/BookStack/pull/3042), [#738](https://github.com/BookStackApp/BookStack/issues/738))
+* Added a wide series of improvements to the search system, including: ([#3043](https://github.com/BookStackApp/BookStack/pull/3043), [#2840](https://github.com/BookStackApp/BookStack/issues/2840))
+  * Added highlighting of search terms in search results. ([#1891](https://github.com/BookStackApp/BookStack/issues/1891), [#997](https://github.com/BookStackApp/BookStack/issues/997))
+  * Added matching of tag names and values through normal search terms. ([#1577](https://github.com/BookStackApp/BookStack/issues/1577))
+* Added search API endpoints. ([#909](https://github.com/BookStackApp/BookStack/issues/909))
+* Added new `.env` option to limit file uploads. ([#3033](https://github.com/BookStackApp/BookStack/issues/3033))
+* Updated the used Laravel framework from version 6 to version 8. Thanks to @laravel-shift for accelerating this. ([#3012](https://github.com/BookStackApp/BookStack/pull/3012), [#3011](https://github.com/BookStackApp/BookStack/pull/3011))
+* Implemented initial use of static analysis for PHP code. ([#3039](https://github.com/BookStackApp/BookStack/pull/3039))
+* Updated Slack and Facebook logos to be current. Thanks to [@na3shkw](https://github.com/BookStackApp/BookStack/pull/3032). ([#3032](https://github.com/BookStackApp/BookStack/pull/3032))
+* Updated user invite/email-confirmation journeys to help prevent potential malicious user manipulation. Thanks again to @haxatron for reporting.  ([#3050](https://github.com/BookStackApp/BookStack/issues/3050))
+* Updated logout endpoints to be POST to prevent potential CSRF concerns. Thanks to @hdvinnie for reporting. ([#3047](https://github.com/BookStackApp/BookStack/issues/3047))
+* Updated page include system to retain the `pre` tags when including a code block. ([#2406](https://github.com/BookStackApp/BookStack/issues/2406))
+* Updated translations with latest changes from Crowdin. ([#3040](https://github.com/BookStackApp/BookStack/pull/3040))
+* Fixed issue where using the back button in the page editor could lead you to the same page. ([#2834](https://github.com/BookStackApp/BookStack/issues/2834))
+* Fixed issue where setting new search filters could remove existing created_by & updated_by filters. ([#2736](https://github.com/BookStackApp/BookStack/issues/2736))
+* Fixed issue where markdown draft pages could convert to HTML. ([#3054](https://github.com/BookStackApp/BookStack/issues/3054))
+* Fixed issue where "Skip to content" link could be visible on print views. ([#3051](https://github.com/BookStackApp/BookStack/issues/3051))
+
+**Released in v21.10.1 through v21.10.3**
+
+* Fixed image upload vulnerability. Thanks to @haxatron ([#3010](https://github.com/BookStackApp/BookStack/issues/3010))
+* Fixed capitalization for Estonian language option. Thanks to [@IndrekHaav](https://github.com/BookStackApp/BookStack/pull/3008). ([#3008](https://github.com/BookStackApp/BookStack/pull/3008))
+* Updated PHP packages to prevent abandoned warning. ([#3007](https://github.com/BookStackApp/BookStack/issues/3007))
+* Updated translations with latest changes from Crowdin. ([#3006](https://github.com/BookStackApp/BookStack/pull/3006), [#3023](https://github.com/BookStackApp/BookStack/pull/3023), [#3014](https://github.com/BookStackApp/BookStack/pull/3014))
+* Made further fixes to address image upload vulnerability. Thanks again to @haxatron ([#3019](https://github.com/BookStackApp/BookStack/issues/3019))
+* Updated AzureAD login library to work with the new Microsoft Graph API. ([#3028](https://github.com/BookStackApp/BookStack/issues/3028))
+* Fixed path image file path traversal vulnerability. Thanks @theworstcomrade for reporting. ([#3030](https://github.com/BookStackApp/BookStack/issues/3030))
+* Prevented HTML attachments being served inline. Thanks @theworstcomrade for reporting. ([#3027](https://github.com/BookStackApp/BookStack/issues/3027))
+
+### Next Steps
+
+Within the latter stage of the v21.11 I went through some of the older issues in the project
+to address them if still relevant. I'll probably continue that work to produce a few 
+patch releases.
+
+As mentioned above I'm intending to produce more videos for [the BookStack YouTube channel](https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g).
+Will work on these in times when I want to do something a bit more creative.
+
+In terms of larger features, I'll start getting deeper into
+[assessing a new page editor](https://github.com/BookStackApp/BookStack/issues/2738)
+which takes us to the next major project roadmap milestone.
+
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@sebastian_unrau?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sebastian Unrau</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v21-12.md b/content/blog/bookstack-release-v21-12.md
new file mode 100644 (file)
index 0000000..f2ead28
--- /dev/null
@@ -0,0 +1,211 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v21.12"
+date = 2021-12-22T16:45:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/reindeer-joe-green.jpg"
+slug = "bookstack-release-v21-12"
+draft = false
++++
+
+As our last feature release of the year BookStack v21.12 is now available.
+Upon a bunch of fixes & improvements, this release features outgoing webhooks in
+addition to the ability of copying entire chapters and books.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.12)
+
+
+**Upgrade Notices**
+
+- **Security Releases** - There were a couple of security vulnerabilities found during the life of 
+  v21.11. See the [v21.11.2](/blog/bookstack-release-v21-11-2/) and [v21.11.3](/blog/bookstack-release-v21-11-3/) posts for more details.
+
+
+### Outgoing Webhooks
+
+BookStack can now send out webhooks! These are web requests that are emitted by BookStack
+when someone performs an action in the system (Updates page, Creates book etc..).
+Webhooks can be created by an administrator in the application "Settings > Webhooks" view.
+
+![Webhook list in settings](/images/2021/12/webhooks_list.png)
+
+Webhooks can be triggered by any event that's tracked in the audit log. You can choose
+specific events to trigger your webhooks or you can trigger upon any available system event.
+
+![Webhook from view](/images/2021/12/webhook_form.png)
+
+When triggered, BookStack will send a HTTP POST JSON request to the provided endpoint
+with a common set of details regarding the event. The general data format is shown when creating
+or editing a webhook, but the below is an example of a `page_update` event:
+
+```json
+{
+    "event": "page_update",
+    "text": "Benny updated page \"My wonderful updated page\"",
+    "triggered_at": "2021-12-11T22:25:10.000000Z",
+    "triggered_by": {
+        "id": 1,
+        "name": "Benny",
+        "slug": "benny"
+    },
+    "triggered_by_profile_url": "https://bookstack.local/user/benny",
+    "webhook_id": 2,
+    "webhook_name": "My page update webhook",
+    "url": "https://bookstack.local/books/my-awesome-book/page/my-wonderful-updated-page",
+    "related_item": {
+        "id": 2432,
+        "book_id": 13,
+        "chapter_id": 554,
+        "name": "My wonderful updated page",
+        "slug": "my-wonderful-updated-page",
+        "priority": 2,
+        "created_at": "2021-12-11T21:53:24.000000Z",
+        "updated_at": "2021-12-11T22:25:10.000000Z",
+        "created_by": 1,
+        "updated_by": 1,
+        "draft": false,
+        "revision_count": 9,
+        "template": false,
+        "owned_by": 1
+    }
+}
+```
+
+Webhooks have a `text` property (Populated where possible, depending on event) which allows them to 
+be used directly in slack or slack-compatible services. If you wanted to get more advanced
+you could build your own middleware or use a service like [Zapier](https://zapier.com/) or [n8n](https://n8n.io/)
+to glue actions & services together. Some possibilities could be:
+
+- Email an admin when application settings are changed.
+- Post in slack when a book is deleted.
+- Log page change occurrences to a Google doc.
+- Update a BookStack page via the REST API when new users register.
+
+Since webhooks need to make external HTTP requests, they have potential to slow down a system.
+If you're creating a large amount of webhooks, that'd be triggered frequently, we have a way to run these in the background to
+prevent user experience slowdowns; [Details can be found here](/docs/admin/email-webhooks/#async-action-handling).
+
+### Copy Entire Chapters & Books
+
+It's been possible to copy a page for a while now. With v21.12 it's now possible to 
+copy entire chapters or even books. Performing this action will also copy all child chapters
+and/or pages in a single smooth action. Copy views will now show warnings to confirm
+copy behavior within BookStack so the necessary considerations can be made.
+
+![Book copy view](/images/2021/12/copy_book.png)
+
+These new abilities bring some great potential new workflow advancements such as being
+able to create "templated" books, pre-configured with the right chapter & page structure,
+ready to be copied out.
+
+### Copy Roles
+
+When creating or updating a role there can be a lot of permissions to configure.
+Creating a set of similar roles could be a time consuming experience. 
+As of v21.12 there's now a "Copy" action when viewing an existing role which
+will take you to the role create view with all fields filled as per the copied role.
+
+![Role copy button](/images/2021/12/copy_role.png)
+
+### Audit Log IP Address Search
+
+The audit log has received another update thanks to [@johnroyer](https://github.com/BookStackApp/BookStack/pull/3081).
+It's now possible to search the audit log by IP address. 
+The search is a prefix-match so you can filter using just the starting 
+portion if needing to match an IP range.
+
+![IP address search in audit log](/images/2021/12/audit_log_search.png)
+
+### Search API Updates
+
+In v21.11 we added the new search API endpoint. Within v21.11.3 a few extra useful properties
+were added to item response data:
+
+```json
+{
+   ...
+    "url": "https://example.com/books/my-book/page/how-advanced-are-cats",
+    "preview_html": {
+      "name": "How advanced are <strong>cats</strong>?",
+      "content": "<strong>cats</strong> are some of the most advanced animals in the world."
+    },
+   ...
+}
+```
+
+These additions save you from needing to do extra work to formulate the full 
+item URL or preview content.
+
+### Logical Theme System Custom Commands
+
+The [logical theme system](https://github.com/BookStackApp/BookStack/blob/development/dev/docs/logical-theme-system.md)
+has been updated to allow registration of custom commands. These are actions you'd typically
+run on command line via `php artisan bookstack:<command>`.
+
+I recently produced a getting started guide for the local theme system which includes
+registering custom commands:
+
+<iframe width="100%" height="360" src="https://www.youtube.com/embed/YVbpm_35crQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+
+
+### Translations
+
+As always our awesome translators have been doing their work to keep language
+content up-to-date. The below are the great contributions since v21.11:
+
+- Vitaliy (gviabcua) - *Ukrainian*
+- Marco (cdrfun) - *German; German Informal*
+- mannycarreiro - *Portuguese*
+- m0uch0 - *Spanish*
+- na3shkw - *Japanese*
+- marinkaberg - *Russian*
+- 10935336 - *Chinese Simplified*
+- Luís Tiago Favas (starkyller) - *Portuguese*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- nutsflag - *French*
+- Thiago Rafael Pereira de Carvalho (thiago.rafael) - *Portuguese, Brazilian*
+- scureza - *Italian*
+- Ken Roger Bolgnes (kenbo124) - *Norwegian Bokmal*
+- Nguyen Hung Phuong (hnwolf) - *Vietnamese*
+- Indrek Haav (IndrekHaav) - *Estonian*
+- Umut ERGENE (umutergene67) - *Turkish*
+
+
+### Full List of Changes
+
+**Released in v21.12**
+
+* Added webhooks. ([#147](https://github.com/BookStackApp/BookStack/pull/147), [#3099](https://github.com/BookStackApp/BookStack/pull/3099))
+* Added ability to copy books, chapters & roles. ([#3118](https://github.com/BookStackApp/BookStack/pull/3118), [#1123](https://github.com/BookStackApp/BookStack/issues/1123))
+* Added audit log IP address search. Thanks to [@johnroyer](https://github.com/BookStackApp/BookStack/pull/3081). ([#3081](https://github.com/BookStackApp/BookStack/pull/3081))
+* Updated translations with latest Crowdin changes. ([#3117](https://github.com/BookStackApp/BookStack/pull/3117))
+* Fixed issue where non-ascii content could break search result previews. Thanks to [@Kristian-Krastev](https://github.com/BookStackApp/BookStack/pull/3113). ([#3113](https://github.com/BookStackApp/BookStack/pull/3113))
+* Fixed mismatched password validation rules across the application. ([#2237](https://github.com/BookStackApp/BookStack/issues/2237))
+
+**Released in v21.11.1 through v21.11.3**
+
+* Added custom command support to the logical theme system. ([#3072](https://github.com/BookStackApp/BookStack/pull/3072))
+* Added support for `prefers-contrast` media setting to increase contrast in faded areas when active. ([#2634](https://github.com/BookStackApp/BookStack/issues/2634))
+* Updated user search to help prevent discovery and harvesting of user information. Thanks @haxatron for reporting. ([#3108](https://github.com/BookStackApp/BookStack/issues/3108))
+* Updated search API results to include the highlighted preview content. ([#3096](https://github.com/BookStackApp/BookStack/issues/3096))
+* Updated search API results to include item URL. ([#3080](https://github.com/BookStackApp/BookStack/issues/3080))
+* Updated translations with latest Crowdin changes. ([#3093](https://github.com/BookStackApp/BookStack/pull/3093), [#3076](https://github.com/BookStackApp/BookStack/pull/3076), [#3057](https://github.com/BookStackApp/BookStack/pull/3057))
+* Updated TOTP confirmation view to autofocus on code input. Thanks to [@raccettura](https://github.com/BookStackApp/BookStack/pull/3068). ([#3068](https://github.com/BookStackApp/BookStack/pull/3068))
+* Updated any links on homepage lists to be more obvious & accessible. ([#3046](https://github.com/BookStackApp/BookStack/issues/3046))
+* Fixed issue with greater-than-expected visibility on page-draft-related items. Thanks @haxatron for reporting. ([#3086](https://github.com/BookStackApp/BookStack/issues/3086))
+* Fixed issue where public API access was not limited by system public control in certain conditions. ([#3091](https://github.com/BookStackApp/BookStack/issues/3091))
+* Fixed faulty page navigation links when headers are nested within other content. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3069). ([#3069](https://github.com/BookStackApp/BookStack/pull/3069), [#3058](https://github.com/BookStackApp/BookStack/issues/3058))
+
+### Next Steps
+
+Focus going into next year will be on the editor. I've started assessing new options but found it difficult to harness
+the time needed this year, so it will be my priority going into 2022.
+
+I have continued to produce more videos on the [BookStack YouTube channel](https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g).
+I'll look to continue this when wanting to get away from code-level work for a bit.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@jg?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Joe Green</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v22-02.md b/content/blog/bookstack-release-v22-02.md
new file mode 100644 (file)
index 0000000..e1866cc
--- /dev/null
@@ -0,0 +1,224 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v22.02"
+date = 2022-02-26T12:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/blossom-tegethoff.jpg"
+slug = "bookstack-release-v22-02"
+draft = false
++++
+
+Today we announce the first BookStack feature release of 2022.
+This brings updates & features to the WYSIWYG editor, user management API endpoints and much more. In this post we cover features added in this release
+in addition to some notable changes in the v21.12 patch releases.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v22.02)
+
+
+**Upgrade Notices**
+
+- **PHP Requirements Change** - The minimum required version of PHP has changed from 7.3 to 7.4.
+- **Composer Requirements Change** - As of release v21.12.3 composer version 2.0 or greater is required. See our [v21.12.3 update notes](/docs/admin/updates/#updating-to-v21123-or-higher) for details on upgrading this.
+- **Security Releases** - During this last release cycle there was a security update. See the [v21.12.1 blog post](/blog/bookstack-release-v21-12-1/) for more detail.
+
+### WYSIWYG Editor Updates
+
+The main work for this release has been focused on the WYSIWYG editor.
+The library used by BookStack, TinyMCE, has been updated to a much newer version and we've reorganized the connected BookStack codebase.
+
+In terms of usage, everything should remain familiar. The most apparent changes are to the toolbar:
+
+![New Editor Toolbar Styling](/images/2022/02/editor-toolbar.png)
+
+The styling and size of the buttons has changed to be a little more modern and easier to use. The location of some buttons have been moved, with lesser-used controls being placed behind overflow menus to keep the default toolbar focused & simple. Code formatting options have been moved out from the block formats dropdown to instead be inline & insert options. A new help "(?)" toolbar button has been added to show editor license information in addition providing a handy list of available shortcuts.
+
+For those using BookStack in a language other than English, the editor is now linked to our translation system meaning that, where language files have been updated, controls within the editor should now be in your preferred language:
+
+![Example of editor controls in French](/images/2022/02/editor-translations.png)
+
+You'll probably notice other usability and speed improvements gained by the more modern underlying library, otherwise all existing features and abilities should remain the same.
+
+Within this release cycle I did spend some time exploring alternative options but [I found](https://github.com/BookStackApp/BookStack/issues/2738#issuecomment-1022683101) that moving away from TinyMCE would be quite a painful process for us so instead I chose a more stable path for now.
+
+### Collapsible Content Block WYSIWYG Editor Support
+
+For the first time in while we've added a new content type to the WYSIWYG editor: The collapsible content block. These blocks allow the grouping of multiple other blocks of content into a container that can be toggled open by the reader. 
+
+<video loading="lazy" controls="true" src="/images/2022/02/collapsible-blocks.mp4" muted></video>
+
+These blocks are open within the editor and closed when viewed on a page. They can be toggled closed within the editor for cleaner editing or previewing purposes. The label used on the toggle bar can be edited as desired. When page content is exported these sections are still represented although in an open state so that content is visible. 
+
+Behind the scenes these blocks use the HTML standard `<details>`/`<summary>` elements as described on [MDN here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details). Markdown users have been able to use these within BookStack, using the HTML representation, but the inclusion in the WYSIWYG editor helps align abilities. 
+
+The addition of these blocks adds some great potential use-cases to empower your documentation; From cleaning up large detailed sections or code-blocks, to being able to provide spoiler-free question and answer style content:
+
+![Question and Answers using collapsible blocks](/images/2022/02/collapse-blocks-qa.png)
+
+
+### User Management API Endpoints
+
+In this release the API receives another set of abilities. There are new endpoints for 
+user listing, creation, updating and deletion. 
+All of these actions require "Manage Users" role permissions by the API client user.
+
+![Preview of user API docs](/images/2022/02/api-user-endpoints.png)
+
+These new endpoints unlock a whole new avenue for automated and programmatic user management 
+in addition to allowing bulk operations to be script-able. Need to migrate thousands
+of users into BookStack? Now it's possible!
+
+You can find more detail for these endpoints via our [demo site API docs](https://demo.bookstackapp.com/api/docs#users-list).
+
+### Webhook Improvements
+
+BookStack v21.12 added support for outgoing webhooks. In v.21.12.1 this system was enhanced with the following additions/changes:
+
+- Webhook network errors will now be caught as to not break next page loading.
+- Webhook "Last Called" time, "Last Errored" time and "Last Error" messages are now recorded against the webhook and shown in the webhook edit page for easier debugging.
+- The HTTP webhook call timeout is now configurable per-webhook.
+
+![Example of webhook debug details](/images/2022/02/webhook-debug-help.png)
+
+These changes should greatly improve the debugging process, if needed, when setting up webhooks while allowing them to be used in a much more reliable manner.
+
+### Language Selection on User Creation
+
+As of v21.12.4 you can now select a preferred language when creating a new user in the system:
+
+![New user language select control](/images/2022/02/user-create-language.png)
+
+Upon showing the correct language upon first login, this means that the new user can now receive the invitation email in the most suitable language, instead of it being based on the language of the admin creating the user, which was not very intuitive. 
+
+### PDF Export Page Size Option
+
+A new `.env` option was added in v21.12.4 to provide control over PDF export page size.
+By default Bookstack will use an A4 page size but this can be changed to "US Letter" size by
+adding the following to your `.env` file:
+
+```bash
+# US Letter PDF export size
+EXPORT_PAGE_SIZE=letter
+```
+
+### Enhancements to the "Recently Updated Pages" List
+
+In BookStack v21.12.3 the "Recently Updated Pages" view, accessed via the homepage card of the same name,
+was enhanced with some additional context-specific details:
+
+- Last update author and last update time are now shown.
+- The parent book/chapter chain are displayed for each item.
+
+![Preview of "Recently updated pages" list](/images/2022/02/recently-updated-enhancements.png)
+
+Special thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3177) for helping to evolve this area of the application.
+
+### Translations
+
+As is common, our remarkable translators have continued their great work to keep the language strings up to date. 
+This release added a bulk-load of new terms, for use within the editor, so a particularly big thanks from me to those that have translated all those extra terms since inclusion. The contributors and their updates made, since the initial v21.12 release, are as follows:
+
+- Saeed (saeed205) - *Persian*
+- SmokingCrop - *Dutch*
+- Dan Brown (ssddanbrown) - *Korean*
+- Indrek Haav (IndrekHaav) - *Estonian*
+- Maciej Lebiest (Szwendacz) - *Polish*
+- DiscordDigital - *German; German Informal*
+- 10935336 - *Chinese Simplified*
+- scureza - *Italian*
+- Gábor Marton (dodver) - *Hungarian*
+- Jasell - *Swedish*
+- Tomáš Batelka (Vofy) - *Czech*
+- Martins Pilsetnieks (pilsetnieks) - *Latvian*
+- na3shkw - *Japanese*
+- m0uch0 - *Spanish*
+- Mundo Racional (ismael.mesquita) - *Portuguese, Brazilian*
+- Leonardo Mario Martinez (leonardo.m.martinez) - *Spanish, Argentina*
+- Zarik (3apuk) - *Russian*
+- nutsflag - *French*
+- Ravid Shachar (ravidshachar) - *Hebrew*
+- Helga Guchshenskaya (guchshenskaya) - *Russian*
+- Gerwin de Keijzer (gdekeijzer) - *German Informal; Dutch*
+- daniel chou (chou0214) - *Chinese Traditional*
+- Julesdevops - *French*
+- Nicolas Pawlak (Mikolajek) - *French*
+- ChacMaster - *Portuguese, Brazilian*
+- Manolis PATRIARCHE (m.patriarche) - *French*
+- Mohammed Haboubi (haboubi92) - *Arabic*
+- peter cerny (posli.to.semka) - *Slovak*
+- roncallyt - *Portuguese, Brazilian*
+- goegol - *Dutch*
+- Pavel Karlin (pavelkarlin) - *Russian*
+- Khroners - *French*
+- msevgen - *Turkish*
+- Ali Shaatani (a.shaatani) - *Arabic*
+- @ististyle - *Korean*
+
+### Development Git Branch Changes
+
+For those closer to the BookStack code, within the last release we've changed the default development branch name from `master` to `development`. This was to add clarity to the branch's purpose while avoiding any potential concerns of offence.
+
+If you have any existing links or branch references, GitHub should redirect and/or warn about this change upon interaction.
+
+### Full List of Changes
+
+**Released in v22.02**
+
+* Added collapsible content blocks support to the WYSIWYG editor. ([#78](https://github.com/BookStackApp/BookStack/issues/78), [#3260](https://github.com/BookStackApp/BookStack/pull/3260))
+* Added translation support to the WYSIWYG editor. ([#1838](https://github.com/BookStackApp/BookStack/issues/1838))
+* Added user management API endpoints. ([#3238](https://github.com/BookStackApp/BookStack/pull/3238), [#1363](https://github.com/BookStackApp/BookStack/issues/1363), [#2701](https://github.com/BookStackApp/BookStack/issues/2701))
+* Changed minimum PHP version from 7.3 to 7.4. ([#3245](https://github.com/BookStackApp/BookStack/pull/3245), [#3152](https://github.com/BookStackApp/BookStack/issues/3152))
+* Updated translations with latest Crowdin changes. ([#3258](https://github.com/BookStackApp/BookStack/pull/3258), [#3251](https://github.com/BookStackApp/BookStack/pull/3251), [#3259](https://github.com/BookStackApp/BookStack/pull/3259))
+* Updated Korean translations. Thanks to [@ististyle](https://github.com/BookStackApp/BookStack/pull/3256). ([#3256](https://github.com/BookStackApp/BookStack/pull/3256))
+* Updated TinyMCE WYSIWYG editor to the latest version. ([#3247](https://github.com/BookStackApp/BookStack/pull/3247))
+* Improved PDF export rendering of images within tables. ([#3190](https://github.com/BookStackApp/BookStack/issues/3190))
+* Fixed potential web console error message when loading the editor. ([#2461](https://github.com/BookStackApp/BookStack/issues/2461))
+* Fixed issue where OIDC token failures would not be shown to the user. ([#3264](https://github.com/BookStackApp/BookStack/issues/3264))
+* Fixed issue where the editor could jump-scroll to the top after format change on FireFox ([#2692](https://github.com/BookStackApp/BookStack/issues/2692))
+
+**Released in v21.12.1 through v21.12.5**
+
+* Added timeout and debugging statuses to webhooks. ([#3139](https://github.com/BookStackApp/BookStack/pull/3139))
+* Added new webhook_call_before logical theme system event hook. ([#3138](https://github.com/BookStackApp/BookStack/pull/3138))
+* Added `--external-auth-id` option to the `bookstack:create-admin` command for use with LDAP/SAML2/OIDC instances. ([#3222](https://github.com/BookStackApp/BookStack/issues/3222))
+* Added the ability select preferred language when creating a new user. ([#2408](https://github.com/BookStackApp/BookStack/issues/2408), [#2576](https://github.com/BookStackApp/BookStack/issues/2576))
+* Added configuration option for PDF export page size. ([#995](https://github.com/BookStackApp/BookStack/issues/995))
+* Added text for "file" validation messages to provide better responses in Attachment API validation failures. ([#3248](https://github.com/BookStackApp/BookStack/issues/3248))
+* Improved handling of uploaded images when thumbnails fail to load. ([#3142](https://github.com/BookStackApp/BookStack/issues/3142))
+* Updated support for APNG images to retain animation. ([#3136](https://github.com/BookStackApp/BookStack/issues/3136))
+* Updated book sort and chapter move handling to enforce more permissions. ([#3134](https://github.com/BookStackApp/BookStack/issues/3134))
+* Updated item-search/select box to autofocus on search field. ([#3127](https://github.com/BookStackApp/BookStack/issues/3127))
+* Updated webhooks to not stop application on endpoint call failure. ([#3122](https://github.com/BookStackApp/BookStack/issues/3122))
+* Updated translations with latest Crowdin changes. ([#3117](https://github.com/BookStackApp/BookStack/pull/3117),[#3148](https://github.com/BookStackApp/BookStack/pull/3148),[#3214](https://github.com/BookStackApp/BookStack/pull/3214),[#3225](https://github.com/BookStackApp/BookStack/pull/3225),[#3158](https://github.com/BookStackApp/BookStack/pull/3158))
+* Updated development docker environment with xdebug support. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3193). ([#3193](https://github.com/BookStackApp/BookStack/pull/3193))
+* Updated user creation flow to not persist the user on invitation sending failure. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3179). ([#3179](https://github.com/BookStackApp/BookStack/pull/3179), [#3174](https://github.com/BookStackApp/BookStack/issues/3174))
+* Updated "Recently Updated Pages" view to show update author and date. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3177). ([#3177](https://github.com/BookStackApp/BookStack/pull/3177), [#3045](https://github.com/BookStackApp/BookStack/issues/3045))
+* Updated PDF page export image display to help fix image sizing issues again. ([#3120](https://github.com/BookStackApp/BookStack/issues/3120))
+* Updated "Recently Updated Pages" view to show parent context chain. ([#3183](https://github.com/BookStackApp/BookStack/issues/3183))
+* Updated 503 error view to simplify and prevent thrown errors. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3210). ([#3210](https://github.com/BookStackApp/BookStack/pull/3210), [#3205](https://github.com/BookStackApp/BookStack/issues/3205))
+* Fixed WYSIWYG editor code block creation across mulitple lines and block elements. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3246). ([#3246](https://github.com/BookStackApp/BookStack/pull/3246), [#3200](https://github.com/BookStackApp/BookStack/issues/3200))
+* Fixed markdown image data URI extraction failing on large images due to regex match limits. ([#3249](https://github.com/BookStackApp/BookStack/issues/3249))
+* Fixed mis-represented default registration role and allowed disabling of this option. ([#3220](https://github.com/BookStackApp/BookStack/issues/3220), [#2338](https://github.com/BookStackApp/BookStack/issues/2338))
+* Fixed OIDC autodiscovery when keys are provided in a certain format, as provided by Azure. ([#3206](https://github.com/BookStackApp/BookStack/issues/3206))
+* Fixed potential errors in revision diff view when multi-byte characters are used. ([#3170](https://github.com/BookStackApp/BookStack/issues/3170))
+* Fixed duplicate display in image gallery when uploading multiple images at once. ([#3160](https://github.com/BookStackApp/BookStack/issues/3160))
+* Fixed inaccurate markdown editor cursor position upon sidebar usage. ([#3186](https://github.com/BookStackApp/BookStack/issues/3186))
+* Fixed issue where webhooks would error for specific recycle bin operations. ([#3154](https://github.com/BookStackApp/BookStack/issues/3154))
+* Fixed Spanish invite email subject translation. Thanks to [@AitorMatxi](https://github.com/BookStackApp/BookStack/pull/3153). ([#3153](https://github.com/BookStackApp/BookStack/pull/3153))
+* Fixed issue where custom homepage could cause strange deletion behavior and lead to errors. ([#3150](https://github.com/BookStackApp/BookStack/issues/3150))
+* Fixed webhooks list view issue where columns would become to narrow. ([#3135](https://github.com/BookStackApp/BookStack/issues/3135))
+* Fixed linked images showing small in PDF export. ([#3120](https://github.com/BookStackApp/BookStack/issues/3120))
+* Fixed issue where pasting certain code blocks would cause erratic editor behavior. ([#3133](https://github.com/BookStackApp/BookStack/issues/3133))
+* Development change: The default development branch name is now `development` instead of `master`. ([#3195](https://github.com/BookStackApp/BookStack/issues/3195))
+
+### Next Steps
+
+This release was the first in working upon our "Editor Alignment & Review" [road-map](https://github.com/BookStackApp/BookStack#%EF%B8%8F-road-map) item. 
+For this next feature release I'll be looking to get deeper into this road-map item to align markdown & wysiwyg feature-sets and potentially offer easier traversal between these editor options.
+
+With the updates made to the WYSIWYG editor I think it's likely we'll have a flurry of patch releases to enhance features and patch issues that pop up from wider usage.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@tegethoff?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Mark Tegethoff</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v22-03.md b/content/blog/bookstack-release-v22-03.md
new file mode 100644 (file)
index 0000000..1c1ae6b
--- /dev/null
@@ -0,0 +1,236 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v22.03"
+date = 2022-03-30T12:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/spring-bird-brian-breeden.jpg"
+slug = "bookstack-release-v22-03"
+draft = false
++++
+
+Today we release BookStack v22.03 which features some further additions to the WYSIWYG editor,
+aiming to align its feature-set with our markdown editor. We also see some changes to the settings 
+view while LDAP users get a useful new debugging option.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v22.03)
+
+**Upgrade Notices**
+
+- **Webhook Data Changes** - Properties found at the `related_item -> created_by/updated_by/owned_by` path of the webhook data will now be an object instead of an ID integer. If you were using these ids you'd now need to access them within the relevant objects. (For example `related_item.created_by.id`).
+- **Security Releases** - During this last release cycle there was a security update. See the [v22.02.3 blog post](/blog/bookstack-release-v22-02-3/) for more detail.
+
+### Official Support Services & Website Updates
+
+While not a particular feature for this release, I've been spending time this release
+cycle putting together some official support services. These services are primarily targeted at
+business that require an assured level of support.
+
+Details regarding the launch of this service can be [found in this blogpost](/blog/bookstack-support-services/)
+and details of the available plans can be seen on [our new support page](/support/).
+
+Adding an additional link to our website header made things a little too busy so I've also redesigned
+that area of this site to make things a little cleaner. We now have a multi-level site menu and a search bar
+has been added to the center, mimicking the header style of BookStack itself.
+
+### WYSIWYG Editor Task-Lists
+
+You can now have checkbox task-lists within the WYSIWYG editor. 
+Such lists have been part of BookStack's markdown editor capabilities
+for a long time but now the editors are aligned on supporting this option.
+Task-lists can be found in the list section overflow in the WYSIWYG toolbar:
+
+![WYSIWYG Editor Task List Example](/images/2022/03/task_lists.png)
+
+Checked status can be controlled when editing the page by simply clicking the checkbox.
+This checked status will then be reflected when the page is viewed, with the checkbox
+in a non-editable state.
+
+### Link Control in WYSIWYG Editor
+
+Links within the WYSIWYG editor are now easier to manage.
+Previously, removing a link would be a non-obvious chore of editing the link
+via the main toolbar then emptying the link input before pressing save.
+Now, when you're focused on a link, you'll 
+see a toolbar to allow quick and easy link editing, removal or opening:
+
+![WYSIWYG Editor Toolbar Link with three buttons: Edit link, Remove link & Open link in new tab](/images/2022/03/link_toolbar.png)
+
+In addition, a new shortcut has been added to the editor. 
+You can press `Ctrl+Shift+K` (Or `Cmd+Shift+K` on MacOS) to instantly show a popup
+for quick linking to existing BookStack content:
+
+![Link Selector Popup Modal Window Preview](/images/2022/03/link_selector.png)
+
+### Settings Interface Changes
+
+Within the settings view we would previously show three categories of settings,
+each in their own panel with their own "Save" button. 
+In some cases, this could prove frustrating as a user may click the save button of
+section "A", which would loose any settings changed within section "B".
+To avoid this the settings view has been split out to a page-per-category with
+a navigation bar on the side:
+
+![Example of new settings view, categorised via a left sidebar menu](/images/2022/03/settings_view.png)
+
+### Webhook Updates
+
+There have been some further changes to webhooks based upon community feedback.
+`created_by`/`updated_by`/`owned_by` details on the sent `related_item` property
+will now be objects instead of ids, which themselves contain a few details
+such as `id`, `name` and `slug`.
+
+In addition, Page creation and update events will now include revision details
+within the `related_item` content. 
+
+Put all together, the POST data will now look something like this for a page update event:
+
+```json
+{
+    "event": "page_update",
+    "...",
+    "related_item": {
+        "id": 2432,
+        "...",
+        "created_by": {
+            "id": 1,
+            "name": "Benny",
+            "slug": "benny"
+        },
+        "updated_by": {
+            "id": 1,
+            "name": "Benny",
+            "slug": "benny"
+        },
+        "owned_by": {
+            "id": 1,
+            "name": "Benny",
+            "slug": "benny"
+        },
+       "current_revision": {
+            "id": 597,
+            "page_id": 2598,
+            "name": "My wonderful updated page",
+            "created_by": 1,
+            "created_at": "2021-12-11T21:53:24.000000Z",
+            "updated_at": "2021-12-11T21:53:24.000000Z",
+            "slug": "my-wonderful-updated-page",
+            "book_slug": "my-awesome-book",
+            "type": "version",
+            "summary": "Updated the title and fixed some spelling",
+            "revision_number": 2
+        }
+    }
+}
+```
+
+For those customizing the webhook data via our [logical theme system](https://github.com/BookStackApp/BookStack/blob/development/dev/docs/logical-theme-system.md), it may be useful
+to know I've extracted out the default webhook formatting to it's own [class which can be seen here](https://github.com/BookStackApp/BookStack/blob/0887c396940b99b37cd1bec4387dc7e112f171cf/app/Actions/WebhookFormatter.php). If needed, you could use this to emulate the base default webhook data as a starting point.
+
+### LDAP Group Debugging
+
+When configuring LDAP authentication, enabling and configuring the group syncing
+would be a common pain-point. We do have a `LDAP_DUMP_USER_DETAILS` debugging 
+option but this did not contain any group, or "memberOf" details.
+Instead you'd have to run manual external LDAP searches, out of BookStack, to emulate
+what might be happening.
+
+In this release, we've now added a new `LDAP_DUMP_USER_GROUPS` option.
+Setting this to true will stop login requests, at the point of parsing group details,
+and dump the found details out to the browser as JSON. You'll see the raw
+data fetched from the LDAP system in addition to how BookStack has parsed that data, upon any
+fetching of nested groups:
+
+```json
+{
+  "details_from_ldap": {
+    "0": "memberof",
+    "memberof": {
+      "0": "cn=Editor,ou=Users,o=abc123,dc=jumpcloud,dc=com",
+      "1": "cn=Wizards,ou=Users,o=abc123,dc=jumpcloud,dc=com",
+      "2": "cn=All Users,ou=Users,o=abc123,dc=jumpcloud,dc=com",
+      "count": 3
+    },
+    "count": 1,
+    "dn": "uid=bjacobs,ou=Users,o=abc123,dc=jumpcloud,dc=com"
+  },
+  "parsed_direct_user_groups": [
+    "Editor",
+    "Wizards",
+    "All Users"
+  ],
+  "parsed_recursive_user_groups": [
+    "Editor",
+    "Wizards",
+    "All Users"
+  ]
+}
+```
+
+More details can be found in [our LDAP documentation](/docs/admin/ldap-auth/).
+
+### Translations
+
+We have a new language in this release, Basque! Work on this language is still in progress
+but a big thanks to "Xabi" on Crowdin for starting work on these translations!
+
+Upon that, we've had lots of updates since the last feature release provided 
+by the wonderful contributors listed below:
+
+- metaarch - *Bulgarian*
+- Xabi (xabikip) - *Basque*
+- Vitaliy (gviabcua) - *Ukrainian*
+- pedromcsousa - *Portuguese*
+- na3shkw - *Japanese*
+- Nir Louk (looknear) - *Hebrew*
+- Statium - *Russian*
+- scureza - *Italian*
+- MASOUD HOSSEINY (masoudme) - *Persian*
+- Indrek Haav (IndrekHaav) - *Estonian*
+- stothew - *German*
+- m0uch0 - *Spanish*
+- SmokingCrop - *Dutch*
+- Maciej Lebiest (Szwendacz) - *Polish*
+- Martins Pilsetnieks (pilsetnieks) - *Latvian*
+- 10935336 - *Chinese Simplified*
+
+### Full List of Changes
+
+**Released in v22.03**
+
+* Added support for checkbox tasklists in the WYSIWYG editor. ([#3333](https://github.com/BookStackApp/BookStack/pull/3333), [#4](https://github.com/BookStackApp/BookStack/issues/4))
+* Added WYSIWYG control to remove & edit links. ([#3276](https://github.com/BookStackApp/BookStack/issues/3276), [#3298](https://github.com/BookStackApp/BookStack/pull/3298))
+* Added WYSIWYG `Ctrl+Shift+K` shortcut to show entity selector popup shortcut in WYSIWYG editor. ([#3244](https://github.com/BookStackApp/BookStack/issues/3244), [#3298](https://github.com/BookStackApp/BookStack/pull/3298))
+* Added LDAP user group debugging option. ([#3345](https://github.com/BookStackApp/BookStack/issues/3345))
+* Added support for the Basque language. ([#3296](https://github.com/BookStackApp/BookStack/issues/3296))
+* Updated settings view with a re-organized layout for a less confusing user experience. ([#3349](https://github.com/BookStackApp/BookStack/pull/3349), [#3221](https://github.com/BookStackApp/BookStack/issues/3221))
+* Updated code block rendering in WYSIWYG to help prevent scroll jumping upon undo/redo. ([#3326](https://github.com/BookStackApp/BookStack/issues/3326))
+* Updated translations with latest Crowdin updates. ([#3320](https://github.com/BookStackApp/BookStack/pull/3320))
+* Updated webhook data to include details of page/chapter/shelf/book creator/updater/owner. ([#3279](https://github.com/BookStackApp/BookStack/issues/3279))
+* Updated webhook data to include revision details on page_update and page_create events. ([#3218](https://github.com/BookStackApp/BookStack/issues/3218))
+* Fixed lack of translation support for some editor buttons. ([#3342](https://github.com/BookStackApp/BookStack/issues/3342))
+* Fixed incorrect page concatenation in book markdown export. ([#3341](https://github.com/BookStackApp/BookStack/issues/3341))
+* Fixed usage of `<br>` tags within code blocks instead of newlines when using the WYSIWYG editor. ([#3327](https://github.com/BookStackApp/BookStack/issues/3327))
+* Fixed image thumbnail generation not taking EXIF rotation data into account. ([#1854](https://github.com/BookStackApp/BookStack/issues/1854))
+
+
+**Released in v22.02.1 through v22.02.3**
+
+* Updated editor references to avoid caching issue that would prevent WYSIWYG editor from opening. ([#3293](https://github.com/BookStackApp/BookStack/issues/3293))
+* Updated code blocks within the editor to be more reliable, especially on first insertion. ([#3292](https://github.com/BookStackApp/BookStack/issues/3292))
+* Added cache breaker to WYSIWYG onward loading to prevent plugin errors appearing if cached. ([#3303](https://github.com/BookStackApp/BookStack/pull/3303))
+* Updated translations with latest Crowdin changes. ([#3301](https://github.com/BookStackApp/BookStack/pull/3301), [#3312](https://github.com/BookStackApp/BookStack/pull/3312), [#3291](https://github.com/BookStackApp/BookStack/pull/3291))
+* Updated sidebar fade to be more subtle when in dark mode. ([#3203](https://github.com/BookStackApp/BookStack/issues/3203))
+* Fixed WYISWYG editor issue where blank lines would collapse. ([#3302](https://github.com/BookStackApp/BookStack/issues/3302))
+* Added iframe allow-list control to prevent a range of malicious uses of untrusted iframe sources. ([#3314](https://github.com/BookStackApp/BookStack/issues/3314))
+
+### Next Steps
+
+With the changes in this release, the WYSIWYG and markdown editors are now much better aligned in terms of features & functionality.
+Over the next month the focus will be on providing the ability to switch between the editor types. 
+This will likely be at a per-page level, with permission controls to decide who wields that power to change editor type.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@bcbreeden?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Brian Breeden</a> on <a href="https://unsplash.com/s/photos/spring?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-release-v22-04.md b/content/blog/bookstack-release-v22-04.md
new file mode 100644 (file)
index 0000000..6e25b5b
--- /dev/null
@@ -0,0 +1,158 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Release v22.04"
+date = 2022-04-29T12:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/cat-pounce-dorothea-oldani.jpg"
+slug = "bookstack-release-v22-04"
+draft = false
++++
+
+Today brings the release of BookStack v22.04! This includes the much-awaited feature
+of easier page editor switching, in addition to a bunch of other additions and improvements.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v22.04)
+
+**Upgrade Notices**
+
+- **Database Changes** - This release makes some significant changes to data within the database which may cause the update to take a little longer than usual to run. Please give the update extra time to complete.
+- **REST API Page Create/Update Changes** - Create & update page requests now have the potential to change the current editor type for that page, depending on the content type sent in the request, if the API user has permission to change the page editor.
+- **URL Handling** - The way we handle URLs has changed this release to hopefully address some issues in specific scenarios. These changes have been tested and should not affect existing working environments but there's an increased risk this release for setups with more complex URL handling. Please [raise an issue](https://github.com/BookStackApp/BookStack/issues/new/choose) or jump into our Discord server if you have any issues with URLs after upgrading.
+
+### Switch Between WYSIWYG & Markdown While Editing
+
+It's now possible to switch between the WYSIWYG editor and Markdown editor while editing a page!
+This has been a much requested feature ever since we originally added the Markdown editor.
+
+<video src="/images/2022/04/bookstack_editor_switch.mp4" muted controls="true"></video>
+
+When editing a page you can use the central dropdown menu at the top which will provide editor switching options (If you have the required permission to do so).
+There are two options when changing from WYSIWYG to Markdown:
+
+![Preview of the editor top dropdown menu showing two 'Switch to markdown editor' options](/images/2022/04/editor_switch_dropdown.png)
+
+These two options exist to offer different conversion handling of your page content:
+
+- **Clean Content** - This is a system-cleaned markdown output, which is much nicer but has potential for formatting loss and potential functionality breaks (Things depending on HTML attributes/IDs for example).
+- **Stable Content** - This retains existing HTML content in Markdown to avoid any potential functionality breakages or loss of formatting. This is similar to switching the global option now then re-opening a page for edit.
+
+When you chose to change editor, a modal warning will show to emphasise the potential side-affects of changing the content.
+The current page being edited will be saved as a draft at this stage. 
+Choosing to continue will then reload the page with the new chosen editor.
+The editor used is essentially saved against the page itself, to prevent un-intentional conversion of page content.
+
+![View of modal window explaining warnings to consider when changing editor](/images/2022/04/edit_switch_warning.png)
+
+We realise that this functionality may not be desirable in all environments, so a new "Change page editor" role permission
+now exists to control who has the ability to change the editor for a page. This will be provided only to the default "Admin" role upon upgrade. 
+
+The old "Page Editor" customization setting is now used to define the default system page editor option, primarily used for new pages.
+
+![A view of the system editor option, now re-labelled as default editor](/images/2022/04/default_editor_option.png)
+
+Keep in mind that users, without the  "Change page editor" permission, may still see an editor that's not configured as the system default if it has been changed by a user with permission.
+
+
+### Recycle Bin API Endpoints
+
+The rollout of additional API endpoints continues, with this release adding API endpoints for the recycle bin:
+
+![Screenshot of recycle bin API endpoints in BookStack API docs page](/images/2022/04/recycle_bin_endpoints.png)
+
+These allow external integration for listing, restoring and destroying recycle bin contents. 
+The listing endpoint contains a little added detail regarding the deleted item and its parent/child content to help
+cover most use-cases you might have.
+
+A big thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3377) for their work to implement this functionality.
+
+### New Editor Event to Configure Diagrams.net
+
+For those that like to hack and customize, we've extended our custom editor events
+to also emit a 'configure' editor event when loading diagrams.net (formerly draw.io) in BookStack.
+This follows the pattern of [our other existing editor events](/docs/admin/hacking-bookstack/#bookstack-editor-events) and can be used like so:
+
+```html
+<script>
+    window.addEventListener('editor-drawio::configure', event => {
+        const config = event.detail.config;
+        config.sidebarWidth = 900;
+    });
+</script>
+```
+
+If you've set a custom `DRAWIO` .env option, you'll need to ensure that contains a `configure=1` query parameter for this to work.
+This event provides loads of abilities of customization.
+[Consult this diagram.net FAQ page](https://www.diagrams.net/doc/faq/configure-diagram-editor) for a list of the options you can define.
+
+### File Handling Efficiency Improvements
+
+In this release we've reviewed all the main actions of uploading and downloading files to make things much more efficient.
+Now, where possible, such file handling will be dealt with by streaming data instead of reading and writing the entire contents. 
+This uses much less system memory and therefore it's far less likely that you'll hit PHP memory limits.
+In fact, this now allows uploading and downloading of attachments that are far larger than your PHP memory limit.
+
+### Translations
+
+The wonderful wizards of language within our community have continued their efforts
+to keep the BookStack translations up-to-date. 
+A big thanks to the following translators for their efforts since the last feature release:
+
+- Xabi (xabikip) - *Basque*
+- sgenc - *Turkish*
+- Shukrullo (vodiylik) - *Uzbek*
+- na3shkw - *Japanese*
+- William W. (Nevnt) - *Chinese Traditional*
+- 10935336 - *Chinese Simplified*
+- m0uch0 - *Spanish*
+- Indrek Haav (IndrekHaav) - *Estonian*
+- Irfan Hukama Arsyad (IrfanArsyad) - *Indonesian*
+- Helga Guchshenskaya (guchshenskaya) - *Russian*
+- pedromcsousa - *Portuguese*
+- eamaro - *Portuguese*
+- SmokingCrop - *Dutch*
+- Maciej Lebiest (Szwendacz) - *Polish*
+- syn7ax69 - *German*
+- Jasell - *Swedish*
+- @evandroamaro - *Portuguese*
+
+
+### Full List of Changes
+
+**Released in v22.04**
+
+* Added ability to switch editor types on a per-page basis. ([#3387](https://github.com/BookStackApp/BookStack/pull/3387), [#458](https://github.com/BookStackApp/BookStack/issues/458), [#369](https://github.com/BookStackApp/BookStack/issues/369))
+* Added new recycle bin API endpoints. Thanks to [@Julesdevops](https://github.com/BookStackApp/BookStack/pull/3377). ([#3377](https://github.com/BookStackApp/BookStack/pull/3377), [#3372](https://github.com/BookStackApp/BookStack/issues/3372))
+* Added ability to pass diagrams.net configuration options. ([#3391](https://github.com/BookStackApp/BookStack/pull/3391))
+* Added Uzbek language option to allow translation, not yet active in the interface. ([#3383](https://github.com/BookStackApp/BookStack/issues/3383))
+* Updated translations with latest Crowdin updates. ([#3384](https://github.com/BookStackApp/BookStack/pull/3384), [#3358](https://github.com/BookStackApp/BookStack/pull/3358))
+* Updated database polymorphic relations to simpler morphmap. ([#3395](https://github.com/BookStackApp/BookStack/issues/3395))
+* Updated file handling in many cases to stream data for better efficiency, reduce memory usage and avoid hitting limits. ([#3365](https://github.com/BookStackApp/BookStack/pull/3365), [#2886](https://github.com/BookStackApp/BookStack/issues/2886))
+* Updated URL handling to be more stable in sub-path scenarios. ([#3364](https://github.com/BookStackApp/BookStack/pull/3364), [#2765](https://github.com/BookStackApp/BookStack/issues/2765), [#2058](https://github.com/BookStackApp/BookStack/issues/2058))
+* Updated content update handling to increment updated_at field, even if only tags are changed. ([#3319](https://github.com/BookStackApp/BookStack/issues/3319))
+* Fixed editor Portuguese translation duplication. Thanks to [@evandroamaro](https://github.com/BookStackApp/BookStack/pull/3373). ([#3373](https://github.com/BookStackApp/BookStack/pull/3373))
+* Fixed API issue where tags would not be applied on API shelf update. ([#3370](https://github.com/BookStackApp/BookStack/issues/3370))
+* Fixed development build command lacking Windows/non-bash compatibility. ([#3323](https://github.com/BookStackApp/BookStack/issues/3323))
+
+
+**Released in v22.03.1**
+
+* Fixed issue where `/settings` redirect would lead to wrong location in some scenarios. ([#3356](https://github.com/BookStackApp/BookStack/issues/3356))
+* Fixed non-active prevention of custom HTML head content on settings views. ([#3355](https://github.com/BookStackApp/BookStack/issues/3355))
+* Updated translations with latest Crowdin changes. ([#3354](https://github.com/BookStackApp/BookStack/pull/3354))
+* Updated project PHP dependencies.
+
+### Next Steps
+
+Over the next month I'll probably take a break from big features and instead target v22.05 as a refinement release,
+improving upon existing functionality and features to address some pain points and unfinished corners.
+During this time I'll also look to open up a proposal for some larger-scale changes, specifically in relation to content URLs, to support some future features.
+
+I intend to produce a few more videos for our [YouTube channel](https://www.youtube.com/c/BookStackApp) since these have proved
+very useful so far. In particular, I want to create an install guide for Ubuntu 22.04, showing [the use of our new script](https://www.bookstackapp.com/blog/ubuntu-2204-script/), and produce a more general "Intro to BookStack" video for those new to the project.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@dorographie?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Dorothea OLDANI</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
+  </span></span>
\ No newline at end of file
diff --git a/content/blog/bookstack-support-services.md b/content/blog/bookstack-support-services.md
new file mode 100644 (file)
index 0000000..b2839e2
--- /dev/null
@@ -0,0 +1,56 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "Introducing BookStack Support Services"
+image = "/images/blog-cover-images/dog-woods-jamie-street.jpg"
+author = "Dan Brown"
+slug = "bookstack-support-services"
+draft = false
+date = 2022-03-21T10:30:00Z
++++
+
+Today I'm happy to announce availability of official BookStack support services!
+These services are broken down into a couple of different plans, the detail
+of which can be found on our [new support page](/support/).
+
+### Purpose & Inception of These Services
+
+These services are primarily intended to service business BookStack users that need some level
+of guaranteed, and/or official, support offering to make BookStack viable in their environment.
+Our plans offer an outlet for the IT team of a company to contact directly with an assurance of official support.
+
+Over the last couple of years I have heard multiple accounts of management discounting BookStack due to it's
+lack of obvious income routes, skeptical the project may disappear at any moment.
+Upon that I have received an increasing amount of enquiries regarding provision of support, in addition to requests for deeper
+hands-on-help via video calls. 
+Additionally, when using an open source project within a business it can be difficult to approve funding for a recurring donation
+whereas paying for a clearly described support service can slip through accounting with relative ease.
+It's due to these reasons I saw a demand for such support services.
+
+Thanks to [my wonderful sponsors](https://github.com/sponsors/ssddanbrown), and to my supportive family, I found myself in
+a unique position where I can provide and grow these service offerings as a means to eventually cover
+my own costs to keep working on the project full time.
+
+### Existing Community Support
+
+The introduction of these services does not mean I'll stop providing/offering community support via 
+our existing channels (GitHub, Discord, Reddit); In fact it should mean I can offer more support there.
+By offering these services I intend to cover my costs to keep working on the project as my primary
+career and therefore have more time to dedicate to those community channels.
+
+### Funding Long Term Sustainable Open Source Development
+
+With BookStack in its current state the amount of work required to keep the project
+maintained, with active community management (Discord/GitHub issues/Pull-request handling etc...), is fairly 
+significant. Each feature addition or option supported adds to the weight, and I'm increasingly having to
+discount features due to their potential maintenance affect to keep the project sustainable. 
+It's at a point where it's a challenge to work on much more than ongoing maintenance while being a side-project.
+
+While taking a career break since October last year I've enjoyed being able to spend more time on the project, 
+with feature releases almost monthly, which has opened my eyes to envision this healthier rate of development longer 
+term. These new support services are the next step for walking along that longer term sustainable journey.
+
+---
+  
+<span style="font-size: 0.8em;opacity:0.8;">Header Image Credits: &nbsp;<span>Photo by <a href="https://unsplash.com/@jamie452?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Jamie Street</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
+  </span></span>
\ No newline at end of file
diff --git a/content/blog/contributing-to-open-source.md b/content/blog/contributing-to-open-source.md
new file mode 100644 (file)
index 0000000..4bc1c1c
--- /dev/null
@@ -0,0 +1,177 @@
++++
+categories = ["Community"]
+tags = ["Open Source"]
+title = "Contributing to BookStack (And Open Source)"
+date = 2022-02-12T20:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lions-joel-herzog.jpg"
+slug = "contributing-to-open-source"
+draft = false
++++
+
+A few times recently people have asked how they can get involved
+and help BookStack so I thought I'd formalize my response into this 
+blog post. While the below is my view in regards to BookStack contributions, it
+will likely apply to many open source projects
+
+
+#### Contribute Code
+
+This is one of the most direct ways to get involved with an open source project, while
+potentially being the most difficult and intimidating. I remember making
+my first pull request (PR) to an open source project on GitHub. I put a fair 
+amount of work in to provide a much requested feature, opened the PR with an 
+explanation, and there it sat for 6 months before it was closed without comment
+or reasoning. 
+
+The trouble with contributing code is that every project is different, with a different 
+process, with different maintainers that have different visions and opinions.
+From maintaining BookStack I've come to understand the challenges from a maintainer point of view.
+It can feel really bad to reject someone's work, after they've spent time on something, and it can
+be time consuming to guide someone through getting their contribution into an acceptable state. 
+
+When looking to contribute code to a project, this is the general process I'd advise:
+
+- Look for contributing guidance in the project readme or `contributing.md` file and follow that if existing.
+- Check the license of the project to see if it's something you can even contribute to.
+- Look over a few existing pull requests in the project, both open and closed, to get a feel for any expected conventions and to understand how the maintainers communicate.
+- Search for open and closed issues relating to your planned contribution. It may be that your idea has already been requested but is not something desired by the maintainer. It may be that someone has already started or the idea needs to be fleshed out first.
+- If you're going to invest a non-insignificant amount of time in your contribution, and would feel insulted or upset upon a quick rejection, communicate with the project maintainers (via issues, their discussion forum or via a draft PR) early on to understand if you're on the right track for a contribution that would be accepted, as to avoid disappointment down the line.
+- Be receptive to feedback and be prepared to follow requests from the maintainer. I've seen some cases where people have struggled with this and taken offence to requested changes, or asked for the maintainer to spend time to justify in detail their requests. Be understanding that the maintainer is likely the one that's going to be primarily responsible for the code going forward. A request for change doesn't by default mean your code is bad, but the maintainer may just forsee other cases that need to be handled based upon experience, or they need things done a certain way to ease maintenance.
+
+#### Provide Translations
+
+If you're skilled with fluency in multiple languages, contributing translation text can be a 
+massive help. Translations within BookStack help the project be accessible to a much wider
+audience. Apart from a memorized French speech about "Family Guy" (Or "Les Griffins") I don't 
+know much outside of English but luckily a bunch of wonderful folks in our community
+keep BookStack translated in, at the time of writing, 36 language variants. 
+
+![BookStack Crowdin Page](/images/2022/02/crowdin.png)
+
+You often don't need to have code skills to help with translating. Sometimes you'll need
+to edit text files otherwise it's common for projects to use a platform that provides
+an easier-to-use interface. Within BookStack [we use Crowdin as our translation platform of choice](https://crowdin.com/project/bookstack).
+
+#### Help on Issues & Feature Requests
+
+When maintaining a project like BookStack, GitHub issues can consume a large amount of time.
+Our own issues list is comprised of:
+
+- Support Requests
+- Feature Requests
+- Bug Reports
+
+We utilise different entry forms for these different categories to tag up the issues 
+appropriately and attempt to get the needed information although that's not always provided.
+
+Support requests are probably the easiest to help with if you're familiar with the technologies
+and environments where the project may run. Requesting vital or missing details, guiding the issue
+creator to the relevant documentation, or providing options & next steps to attempt resolution, are all good first
+steps that can help move things forward and save time for the maintainers.
+
+With feature requests, it can be useful to provide perspective and add to the discussion for ideas. 
+Sometimes it's just a case of showing the issue creator alternative paths to their desired outcome. 
+It's quite common, especially by more technical users, that feature requests are opened requesting a certain implementation rather than stating their intended benefit purpose. 
+For example: Requesting "Add a header bar link to page X" instead of "Provide faster access to page X from any location, since this would benefit Y". 
+A request for a specific implementation can keep you blind to better alternatives to reaching the same outcome. If this occurs, it can be very useful to communicate with the issue creator to understand that actual desire behind their implementation request.
+Even if a feature is requested for something that you would not desire, it can be useful to know from a maintainer
+perspective if a request is something that would go unused in your environment and hence may only
+add as extra complexity/clutter. 
+
+Bug reports can be very useful to a maintainer to create a more stable and issue-free platform but 
+they're not always provided in an ideal format, even when we have measures in place to request certain
+details. Upon that, sometimes bug reports are opened for elements that are in reality a matter of opinion or based upon false expectations.
+Validating and confirming re-production of issues can be a massive help and speed up the process for someone to get onto fixing the fundamental issue/bug.
+
+Finally, there's general issue management. Reporting issues that are no longer relevant, or advising of potential duplication, can save time and keep the issues list down.
+
+#### Testing and Bug Reporting
+
+Many open source projects don't have the full QA capabilities of commercial offerings.
+While I attempt to write automated tests where possible, and manually test each logical
+scenario, you can't catch absolutely everything and it's commonplace that I'd be the only
+tester for a new feature being released.
+
+Reporting bugs you come across in production can be very useful, just ensure you're running the 
+latest version (since things may have already been fixed) and provide as much detail as
+possible to replicate the bug (Including environment details such as browser used, operating system etc...).
+
+Testing pre-release developments can be valuable to ensure another use-case has been tested
+before changes are shipped to production, but communicate with the maintainers before attempting
+this since the pre-release environment may be knowingly unstable and you may just end up wasting your own time 
+in addition to the maintainer's. Probably best to find some recent changes, then reach out to see
+if they're ready for testing & feedback.
+
+An alternative form of bug reporting is security issue reporting. Security is of course quite vital
+to most projects. It's quite common for services & projects to have security "bug bounties" where you
+can be rewarded money for discovering and reporting vulnerabilities. 
+In BookStack I've dealt with many submissions via [huntr.dev](https://huntr.dev/) which has led to many issues being reported and fixed, with the reporters receiving significant monetary gain for their discovery
+without being at cost to us. Upon that they have also rewarded me for patching many of the vulnerabilities found. 
+
+![Huntr.dev example BookStack vulnerability report](/images/2022/02/huntr-dev.png)
+
+
+If going down the security route, many open source projects will have their own procedures and classifications when it comes to reporting security vulnerabilities. Look within the project readme or for a `security.md` file like [the one we have in BookStack](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md).
+
+#### Create Guides, Videos, Articles etc...
+
+![List of BookStack videos by community members](/images/2022/02/community-videos.png)
+
+Text and video media can have a great benefit on a project.
+Not only do they help in spreading the word, they can provide 
+a massive morale boost. Seeing people take the time to create videos about BookStack
+creates a great sense of pride to help me continue on the project, while often showcasing
+an alternative viewpoint in terms of thoughts and usage of the platform.
+Upon their emotional and network affect, this kind of media can just generally help
+in terms of providing guidance to new users. Here are two videos as examples:
+
+- ["Install Bookstack on Synology in 2 Minutes" by Geeked](https://youtu.be/_13K1DeZwhk)
+- ["BookStack Installed on Docker and Portainer" by DB Tech](https://youtu.be/NhPw1DvxYZc)
+
+Both of these provide very useful visual guidance relating to specific hosting platforms that I 
+wouldn't cover myself, hence helping improve accessibility to the project for a sizable chunk
+of users.
+
+#### Get Involved on Discord
+
+We [have a discord server](https://discord.gg/ztkBqR2) to host quick and informal discussion for BookStack.
+People often pop in to request support, discuss ideas or talk about ways to do things. 
+Supporting others and being part of the conversation can help build the sense of community within
+the project and help BookStack be something more than just its codebase.
+
+#### Share the Project
+
+Simply sharing the project around can provide a positive affect on a project.
+I [use f5bot](https://f5bot.com/) to track BookStack mentions on Reddit & Hacker News,
+for which I see multiple almost daily which again can provide a great morale boost.
+In addition, links posted to the project is great for SEO, to help us rank up
+in search engine results and therefore reach a wider audience. 
+
+On thing to be careful of is sharing the project in the incorrect context or over-sharing. 
+If spam-like behavior is seen then it can cause resentment towards the project.
+For this reason I strongly limit how often I personally post the project, as a new submission, on Reddit.
+
+#### Contribute Financially
+
+Financial contributions are used differently depending on the project.
+For the majority of BookStack's development I avoided receiving donations to prevent 
+getting money involved in the project. I then changed my stance with the idea that I
+could take donations and redistribute them to the projects we rely upon within BookStack.
+Since I decided to take a career break to focus on BookStack I paused any additional
+"donation forwarding" and instead have been using them to support myself financially.
+However they're used, donations can provide extra options and flexibility to a project or,
+at minimum, indicate your support and appreciation for a project.
+
+![My GitHub Sponsors Profile](/images/2022/02/github-sponsors.png)
+
+[I currently accept donations via GitHub sponsors](https://github.com/sponsors/ssddanbrown/).
+Recently, I altered my higher-level tiers to have rewards of [logo display on our website](https://www.bookstackapp.com/#sponsors)
+and readme. This was to give something back to our larger sponsors while trying to entice
+additional large sponsorships from business that can afford these levels.
+
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@joel_herzog?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Joel Herzog</a> on <a href="https://unsplash.com/s/photos/animal-pack?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></span>
+  
\ No newline at end of file
diff --git a/content/blog/replacing-ga-and-mailchimp.md b/content/blog/replacing-ga-and-mailchimp.md
new file mode 100644 (file)
index 0000000..f8702d6
--- /dev/null
@@ -0,0 +1,102 @@
++++
+title = "Replacing Google Analytics & Mailchimp"
+date = 2021-01-05T18:44:52Z
+categories = ["News"]
+tags = ["News"]
+author = "Dan Brown"
+image = "/images/blog-cover-images/penguin-steffen-triekel.jpg"
+draft = false
++++
+
+On this BookStack site I have been using Google Analytics to track visitor metrics.
+While not crucial to know, it's generally useful to have an idea of the target audience, current popularity and
+be aware of any visitor spikes. For the email updates and email security alerts I've been
+using Mailchimp. This post explains the move to more privacy aware alternatives.
+
+
+### Google Analytics to Plausible
+
+BookStack, being an open source self-hostable platform, will have an audience that's generally quite privacy aware.
+Google is generally seen as an ever-growing threat to privacy on the internet. 
+Google Analytics, as a service to track website visitor metrics, is very popular and well used since it's available
+without paying and provides a very good amount of data hence was my choice for this site until now but I've 
+had a growing unease sending visitor's details to Google without their consent. I did use it without cookie
+usage while choosing to anonymise IP addresses but it would still effectively contribute to Google's 
+own data usage which may be misaligned with the wishes of visitors.
+
+As an alternative, I've set-up a self-hosted instance of [Plausible](https://plausible.io/). 
+I was initially put off with its simplicity, compared to Google Analytics; I mean it has way fewer menus, graphs,
+pages and options so it must be inferior right? But I thought about the questions I want answered from such a service:
+
+* How many visitors are on my site?
+* How many visitors have been on my site?
+* Where have they come from?
+* What pages are being viewed?
+* What country are they visiting from?
+* What kind of devices are being used?
+* When does a traffic spike occur?
+
+Plausible answers all of these, and it does so directly, in an easy and beautiful UI.
+It's simplicity actually makes it better for the questions I need answered since I no longer
+need to navigate through the slow, confusing interface that Google Analytics presents.
+It's all just there in a single view.
+
+![Plausible BookStack Site Analytics](/images/2021/01/plausible_bookstack_site_analytics.png)
+
+Plausible offer paid, hosted plans but I've chosen to self-host the service to keep costs minimal and so that my visitor's data
+is not held by a third-party. That said, I still want to support the project which can be done as a self-hoster via 
+their [GitHub sponsors page](https://github.com/sponsors/plausible). Setup was a straight-forward process using their docker
+configuration and it's been stable so far. I ran it alongside Google Analytics for a few days to check if figures aligned. 
+The numbers were slightly higher (~5 to 10% or so) on the Plausible side which I'd guess may be down to visitors blocking
+Google scripts/domains.
+
+One of my favourite features of Plausible is that it's really easy to make the dashboard public which is something
+I've often wanted to do with Google Analytics in the interest of transparency. The BookStack dashboard can be 
+seen at https://analytics.bookstackapp.com/bookstackapp.com.
+
+### Mailchimp to MailBag
+
+As mentioned in the intro, I've been using Mailchimp to send newsletter updates and security alerts.
+To be honest, It's worked fairly well; Their interface is generally pleasant, it was cost-free for my usage,
+it sent the emails without issue. That said, there were two things I was not a fan of:
+
+1. It's another entity that registrants would have to trust in addition to myself/BookStack.
+2. They added link tracking even when choosing to disable it.
+
+The second point really irked me, especially since you could choose to disable it during campaign setup.
+To be fair, I did not raise it with them but based on this line from their [support page](https://mailchimp.com/help/troubleshooting-click-tracking/):
+
+> We encourage you to leave click tracking on. However, you can disable click tracking in paid accounts after you've sent a few campaigns without any compliance issues.
+
+It appears that user privacy may be a paid feature. 
+
+This tracking was particularly unappealing on our plaintext security updates.
+It's not obvious at all where the links actually lead to:
+
+![Mailchimp Tracked Links Example](/images/2021/01/mailchimp_tracked_links.png)
+
+When looking around for alternatives, I found lots of great options including:
+
+- [Mailcoach](https://mailcoach.app/)
+- [phpList](https://www.phplist.com/)
+- [listmonk](https://listmonk.app/)
+- [Mautic](https://www.mautic.org/)
+
+Unfortunately though many of these lacked an auto-RSS-send feature and/or appeared too complex
+for the focused use-case I had in mind so, with some extra Christmas holiday time, I put together
+my own app which I've called [MailBag](https://github.com/ssddanbrown/mailbag).
+
+![MailBag Home Dashboard ScreenShot](/images/2021/01/mailbag.png)
+
+This is a purposefully very simple app and has lots of limitations, as listed in the [readme](https://github.com/ssddanbrown/mailbag#mailbag),
+but it does what I need it to while providing me an opportunity to work on something
+fresh for a change to sharpen some development skills.
+There is no open/click tracking support; No HTML email support.
+
+Over the next month or so I'll be migrating the existing contact lists from Mailchimp. I'll send out emails to existing
+subscribers as a advisory and to provide an opportunity to opt-out of the migration beforehand. The subscribe links, found
+at the bottom of all posts here, have been updated to the MailBag instance since I'll be running both systems side-by-side for a while.
+
+----
+
+<span style="font-size: 0.9em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@okinapansa?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Steffen Triekels</a> on <a href="https://unsplash.com/t/animals?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-08-2.md b/content/blog/security-release-v21-08-2.md
new file mode 100644 (file)
index 0000000..645bbd7
--- /dev/null
@@ -0,0 +1,36 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.08.2"
+date = 2021-09-04T14:12:36Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-hudsoncrafted.jpg"
+slug = "bookstack-release-v21-08-2"
+draft = false
++++
+
+BookStack v21.08.2 has been released. This security release is intended to cover a couple of XSS
+vulnerabilities, where a malicious user with page edit access could enter script that would execute
+upon page view. You should update as soon as possible if you allow untrusted users to edit content
+in your instance.
+
+In addition, this releases expands the [CSP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+set by BookStack to help avoid any similar vulnerabilities from being effective going forward.
+If you've performed some more advanced customizations on your instance, they may need to be altered
+to work with the built-in CSP system. Feel free to contact me via the channels listed below for any assistance 
+on this.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.08.2)
+
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@hudsoncrafted?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Debby Hudson</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-08-5.md b/content/blog/security-release-v21-08-5.md
new file mode 100644 (file)
index 0000000..92618e9
--- /dev/null
@@ -0,0 +1,39 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.08.5"
+date = 2021-10-08T20:53:08Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-calina.jpg"
+slug = "bookstack-release-v21-08-5"
+draft = false
++++
+
+BookStack v21.08.5 has been released. This is a security release that covers a vulnerability
+which would allow malicious users, who have permission to update or create pages, to load content
+from files stored within the `storage/` or `public/` directories (Such as application logs) via the
+page HTML export system.
+
+If you allow untrusted users to edit page content you should update as soon as possible.
+
+This release also changes the way browser response caching is performed, while logged in, 
+to help prevent navigating back to confidential content after logout.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.08.5)
+
+### Additional Changes
+
+- Added concurrent page editing warnings upon draft save events. Thanks to [@MatthieuParis](https://github.com/BookStackApp/BookStack/pull/2877) ([#2877](https://github.com/BookStackApp/BookStack/pull/2877))
+- Updated translations with the latest changes from Crowdin. ([#2953](https://github.com/BookStackApp/BookStack/pull/2953))
+
+### For more information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack Security Advice](https://github.com/BookStackApp/BookStack#-security) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@calina?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Georg Bommeli</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-10-1.md b/content/blog/security-release-v21-10-1.md
new file mode 100644 (file)
index 0000000..d412df3
--- /dev/null
@@ -0,0 +1,40 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.10.1"
+date = 2021-10-27T11:30:08Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-muhammad-zaqy-al-fattah.jpg"
+slug = "bookstack-release-v21-10-1"
+draft = false
++++
+
+BookStack v21.10.1 has been released. This is a security release that covers a vulnerability
+which would allow malicious users, who have permission to update or create pages, to upload
+content that could then be utilized for phishing or other general malicious intent.
+
+If you allow untrusted users to edit page content you should update as soon as possible.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.10.1)
+
+Thanks to @haxatron on [huntr.dev](https://huntr.dev/) for the discovery and reporting of this issue.
+
+### Full List of Changes
+
+* Fixed image upload vulnerability. Thanks to @haxatron ([#3010](https://github.com/BookStackApp/BookStack/issues/3010))
+* Fixed capitalization for Estonian language option. Thanks to [@IndrekHaav](https://github.com/BookStackApp/BookStack/pull/3008). ([#3008](https://github.com/BookStackApp/BookStack/pull/3008))
+* Updated PHP packages to prevent abandoned warning. ([#3007](https://github.com/BookStackApp/BookStack/issues/3007))
+* Updated translations with latest changes from Crowdin. ([#3006](https://github.com/BookStackApp/BookStack/pull/3006))
+
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@dizzydizz?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Muhammad Zaqy Al Fattah</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-10-2.md b/content/blog/security-release-v21-10-2.md
new file mode 100644 (file)
index 0000000..b11ffd4
--- /dev/null
@@ -0,0 +1,38 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.10.2"
+date = 2021-10-28T15:00:08Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-chepe-nicoli.jpg"
+slug = "bookstack-release-v21-10-2"
+draft = false
++++
+
+BookStack v21.10.2 has been released. This is a security release that builds upon changes
+in v21.10.1 which covers a vulnerability which would allow malicious users, who have
+permission to update or create pages, to upload content that could then be utilized
+for phishing or other general malicious intent.
+
+If you allow untrusted users to edit page content you should update as soon as possible.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.10.2)
+
+
+### Full List of Changes
+
+* Made further fixes to address image upload vulnerability. Thanks again to @haxatron ([#3019](https://github.com/BookStackApp/BookStack/issues/3019))
+* Updated translations with latest changes from Crowdin. ([#3014](https://github.com/BookStackApp/BookStack/pull/3014))
+
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@nicoli_?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Chepe Nicoli</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-10-3.md b/content/blog/security-release-v21-10-3.md
new file mode 100644 (file)
index 0000000..8da1c07
--- /dev/null
@@ -0,0 +1,40 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.10.3"
+date = 2021-11-01T12:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/path-ugne-vasyliute.jpg"
+slug = "bookstack-release-v21-10-3"
+draft = false
++++
+
+BookStack v21.10.3 has been released.
+This is a security release that address a couple of vulnerabilities within the attachment and image
+serving mechanisms. The attachment vulnerability could result in users uploading content to be served
+in a way that can be utilized for phishing. The image serving vulnerability could result in unintended
+file access within your BookStack storage folder.
+
+If you allow untrusted users to login or upload attachments you should update as soon as possible.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.10.3)
+
+
+### Full List of Changes
+
+* Updated AzureAD login library to work with the new Microsoft Graph API. ([#3028](https://github.com/BookStackApp/BookStack/issues/3028))
+* Fixed path image file path traversal vulnerability. Thanks @theworstcomrade for reporting. ([#3030](https://github.com/BookStackApp/BookStack/issues/3030))
+* Prevented HTML attachments being served inline. Thanks @theworstcomrade for reporting. ([#3027](https://github.com/BookStackApp/BookStack/issues/3027))
+* Updated translations from latest Crowdin changes. ([#3023](https://github.com/BookStackApp/BookStack/pull/3023))
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@ugnehenriko?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ugne Vasyliute</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-11-2.md b/content/blog/security-release-v21-11-2.md
new file mode 100644 (file)
index 0000000..012209d
--- /dev/null
@@ -0,0 +1,43 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.11.2"
+date = 2021-11-30T14:15:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-gina-neri.jpg"
+slug = "bookstack-release-v21-11-2"
+draft = false
++++
+
+BookStack v21.11.2 has been released.
+This is a security release that address a couple of vulnerabilities relating to API access
+and page draft related content visibility:
+
+- If the "Public" role was provided API access then the API could be accessed, in certain scenarios,
+  by non-authenticated users even if the "Allow public access" setting was disabled.
+- In some specific scenarios, content related to page drafts (Such as attachments) could be visible
+  to non-owners (Whom would have permission to view the page if saved  as a non-draft at that point).
+
+It's advised to upgrade as soon as possible if the API has been enabled for roles within your instance
+or if draft page content visibility could be a security concern for you.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.11.2)
+
+
+### Full List of Changes
+
+* Fixed issue with greater-than-expected visibility on page-draft-related items. Thanks @haxatron for reporting. ([#3086](https://github.com/BookStackApp/BookStack/issues/3086))
+* Fixed issue where public API access was not limited by system public control in certain conditions. ([#3091](https://github.com/BookStackApp/BookStack/issues/3091))
+* Updated translations from latest Crowdin changes. ([#3076](https://github.com/BookStackApp/BookStack/pull/3076))
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@gneri1713?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Gina Neri</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-11-3.md b/content/blog/security-release-v21-11-3.md
new file mode 100644 (file)
index 0000000..6ac0f96
--- /dev/null
@@ -0,0 +1,41 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.11.3"
+date = 2021-12-15T13:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/door-lock-lucas-santos.jpg"
+slug = "bookstack-release-v21-11-3"
+draft = false
++++
+
+BookStack v21.11.3 has been released.
+This is a security release that helps prevent potential discovery and harvesting of user
+details including name and email address.
+
+It's advised to upgrade as soon as possible if your BookStack instance is public or
+is used by untrusted members.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.11.3)
+
+Thanks to @haxatron for discovering and reporting this vulnerability via huntr.dev.
+
+### Full List of Changes
+
+* Helped prevent discovery and harvesting of user information. Thanks @haxatron for reporting. ([#3108](https://github.com/BookStackApp/BookStack/issues/3108))
+* Updated search API results to include the highlighted preview content. ([#3096](https://github.com/BookStackApp/BookStack/issues/3096))
+* Updated search API results to include item URL. ([#3080](https://github.com/BookStackApp/BookStack/issues/3080))
+* Updated translations with latest Crowdin changes. ([#3093](https://github.com/BookStackApp/BookStack/pull/3093))
+
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@_staticvoid?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Lucas Santos</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v21-12-1.md b/content/blog/security-release-v21-12-1.md
new file mode 100644 (file)
index 0000000..74157fb
--- /dev/null
@@ -0,0 +1,47 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v21.12.1"
+date = 2022-01-06T11:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/lock-jornada-produtora.jpg"
+slug = "bookstack-release-v21-12-1"
+draft = false
++++
+
+BookStack v21.12.1 has been released.
+This is a security release that better enforces permissions on book-sort & 
+chapter-move operations to address scenarios where content could be moved to
+non-permissible locations.
+
+It's advised to upgrade as soon as possible if untrusted users can update books 
+or chapters in your BookStack instance.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v21.12.1)
+
+Thanks again to @haxatron for discovering and reporting this vulnerability via huntr.dev.
+
+### Full List of Changes
+
+* Added timeout and debugging statuses to webhooks. ([#3139](https://github.com/BookStackApp/BookStack/pull/3139))
+* Added new webhook_call_before logical theme system event hook. ([#3138](https://github.com/BookStackApp/BookStack/pull/3138))
+* Updated support for APNG images to retain animation. ([#3136](https://github.com/BookStackApp/BookStack/issues/3136))
+* Updated book sort and chapter move handling to enforce more permissions. ([#3134](https://github.com/BookStackApp/BookStack/issues/3134))
+* Updated item-search/select box to autofocus on search field. ([#3127](https://github.com/BookStackApp/BookStack/issues/3127))
+* Updated webhooks to not stop application on endpoint call failure. ([#3122](https://github.com/BookStackApp/BookStack/issues/3122))
+* Updated translations with latest Crowdin changes. ([#3117](https://github.com/BookStackApp/BookStack/pull/3117))
+* Fixed webhooks list view issue where columns would become to narrow. ([#3135](https://github.com/BookStackApp/BookStack/issues/3135))
+* Fixed linked images showing small in PDF export. ([#3120](https://github.com/BookStackApp/BookStack/issues/3120))
+* Fixed issue where pasting certain code blocks would cause erratic editor behavior. ([#3133](https://github.com/BookStackApp/BookStack/issues/3133))
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@jornadaprodutora?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Jornada Produtora</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/security-release-v22-02-3.md b/content/blog/security-release-v22-02-3.md
new file mode 100644 (file)
index 0000000..143db69
--- /dev/null
@@ -0,0 +1,48 @@
++++
+categories = ["Releases"]
+tags = ["Releases"]
+title = "BookStack Security Release v22.02.3"
+date = 2022-03-07T15:00:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/fence-birds-yudi-m.jpg"
+slug = "bookstack-release-v22-02-3"
+draft = false
++++
+
+BookStack v22.02.3 has been released.
+This is a security release that adds better protections against embedded content
+that could be used in malicious ways. This effectively restricts embedded iframe
+content in an allow-list approach. 
+
+A new `ALLOWED_IFRAME_SOURCES` option has been added to provide configuration of 
+allowed embed/iframe sources within BookStack pages, and this defaults to a couple
+of popular services such as YouTube and Vimeo.
+
+Please see this link for more detail regarding this option:
+- https://www.bookstackapp.com/docs/admin/security/#iframe-src-control
+  - ("Iframe Source Control" section)
+
+It's advised to upgrade as soon as possible if untrusted users can create or update 
+pages within your BookStack instance.
+
+* [Update instructions](https://www.bookstackapp.com/docs/admin/updates)
+* [GitHub release page](https://github.com/BookStackApp/BookStack/releases/tag/v22.02.3)
+
+Thanks to @416e6e61 (Anna) for discovering and reporting this vulnerability via huntr.dev.
+
+### Full List of Changes
+
+* Added iframe allow-list control to prevent a range of malicious uses of untrusted iframe sources. ([#3314](https://github.com/BookStackApp/BookStack/issues/3314))
+* Updated translations with latest Crowdin changes. ([#3312](https://github.com/BookStackApp/BookStack/pull/3312))
+
+
+### For More Information
+
+If you have any questions or comments about this advisory:
+* Open an issue in [the BookStack GitHub repository](BookStackApp/BookStack/issues).
+* Ask on the [BookStack Discord chat](https://discord.gg/ztkBqR2).
+* Follow the [BookStack security policy](https://github.com/BookStackApp/BookStack/blob/development/.github/SECURITY.md) to contact someone privately.
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@yudi_m?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Yudi M</a> on <a href="https://unsplash.com/s/photos/fence?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/services-we-use.md b/content/blog/services-we-use.md
new file mode 100644 (file)
index 0000000..733145e
--- /dev/null
@@ -0,0 +1,166 @@
++++
+categories = ["Community"]
+tags = ["Open Source", "Services"]
+title = "The Services We Use"
+date = 2021-10-15T16:39:32Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/zebra-herd-the_bracketeer.jpg"
+slug = "services-we-use"
+draft = false
++++
+
+Now that [I've got a bit more time](https://danb.me/blog/posts/leaving-my-job-to-focus-on-open-source/)
+to work on BookStack, I thought it'd be good to
+do something a little different on the blog and pay tribute to the services we use
+to help manage the project. Keep in mind that this is not a complete listing
+of projects that we use within BookStack itself, but instead a listing of the services
+and projects that we use from a project & code management point of view. 
+
+
+### GitHub
+
+![GitHub](/images/2021/10/github.png)
+
+[GitHub](https://github.com/BookStackApp/BookStack) provides a lot of the core central management of the project.
+We use GitHub for the following:
+
+- Central location for code management.
+- Issue and feature request tracking.
+- Release planning and management.
+- Code contribution management via Pull Requests.
+- Automated running of our test suite.
+- Sponsorship management.
+
+Originally I hosted the project on GitLab but, after deciding that I'd want to push
+the project further into the open, I decided to move it to GitHub for better visibility.
+Although not open itself, GitHub has provided good facilitation of the project's needs
+and our value from GitHub has grown as they've increased their feature-set with new
+offerings such as [GitHub Actions](https://github.com/BookStackApp/BookStack/actions)
+and [GitHub Sponsors](https://github.com/sponsors/ssddanbrown).
+
+### Plausible
+
+![Plausible](/images/2021/10/plausible.png)
+
+[Plausible](https://plausible.io/) provides [our website analytics](https://analytics.bookstackapp.com/bookstackapp.com) as a privacy friendly alternative
+to the Google Analytics tracking we used to use. I detailed our migration
+in [a blogpost earlier this year](https://www.bookstackapp.com/blog/replacing-ga-and-mailchimp/).
+
+The information it provides is concise and exactly what I may need to know about our website's usage. Plausible is a great open-source offering, with a focused goal which
+it meets superbly. We self-host an instance and then contribute back via GitHub sponsors.
+
+### CrowdIn
+
+![CrowdIn](/images/2021/10/crowdin.png)
+
+[CrowdIn](https://crowdin.com/project/bookstack) provides translation management
+services for BookStack. Previous to using CrowdIn translations would be managed
+via GitHub pull requests which could pose a significant barrier to non-developer
+translators while being tricky for me to manage.
+
+Using CrowdIn's free-for-open-source
+offerings provided a central location to track translations while massively
+improving accessability for translators. CrowdIn the integrates neatly back to GitHub
+to send back all translation changes via a single pull request making my maintainer role
+easier. Overall it's very valuable and I'm grateful to CrowdIn for their free offering.
+
+### StyleCI
+
+![StyleCI](/images/2021/10/styleci.png)
+
+It's good to have consistent code styling within a project to ease the cognitive load.
+I did use [PHP CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) to impose
+a standard but I'd often forget to run it or be hesitant of causing a lot of changes
+which would then complicate pending pull requests.
+
+Instead I turned to [StyleCI](https://styleci.io/), an offering from the great
+open source wizard [Graham Campbell](https://twitter.com/GrahamJCampbell).
+StyleCI will automatically assess code style upon push to GitHub, with blazing speed,
+and report back if there any issues. You can then pull the fixes to be made
+via automated or manual means to get the code back into shape. 
+
+While StyleCI does cost, Open source projects can use it for free. It's a 
+classic example of a focused service that just does exactly what you need it to, 
+making your life easier in the process. 
+
+### Code Climate
+
+![Code Climate](/images/2021/10/code-climate.png)
+
+[Code Climate](https://codeclimate.com/github/BookStackApp/BookStack) is something
+I added to the project out of curiosity a few years ago and have since left active
+after getting a surprising amount of value back. Code Climate provides code quality
+analysis, highlighting potential maintainability issues in code. 
+I don't follow it's guidance religiously but it's proved useful to get a feel
+when particular files might be getting out of control. 
+
+CodeClimate is not open source but it does provide free services to Open Source
+projects. It's another service that was super simple, taking only minutes to set-up,
+while providing a net benefit. 
+
+### Hugo
+
+![Go Hugo](/images/2021/10/gohugo.png)
+
+Not so much a service as an open source project, but [Hugo](https://gohugo.io/) plays a big
+part for our website and this blog. Hugo is a static-site generator that allows 
+us to generate a performant, easy-to-host, static HTML site for our docs, blog
+and homepage from content [we can manage via GitHub](https://github.com/BookStackApp/website).
+
+Hugo has remained great for our usage and has remained super fast even as
+the site and blog have grown. I've sometimes had trouble wrangling with the 
+template & variable syntax but I can usually find a solution and it's infrequent
+I need to make changes. Overall, A great open-source project.
+
+### Mailbag
+
+![Mailbag](/images/2021/10/mailbag.png)
+
+Mailbag is a project I created myself to manage [our mailing lists](https://updates.bookstackapp.com/signup/bookstack-news-and-updates) as an alternative
+to our previous Mailchimp usage.  I detailed our migration
+in [a blogpost earlier this year](https://www.bookstackapp.com/blog/replacing-ga-and-mailchimp/).
+
+It's quite a simple system, focusing on plaintext email content,
+but it's designed to privacy respecting and fast to use. [I provide it under the 
+MIT license via GitHub](https://github.com/ssddanbrown/mailbag).
+
+### Discord
+
+![Discord](/images/2021/10/discord.png)
+
+Although primarily intended for gaming, [Discord](https://discord.com/) has been 
+adopted as the discussion system of choice by many open source projects.
+Personally I was very hesitant about [utilizing it for BookStack](https://discord.com/invite/ztkBqR2)
+but Discord has acted as a very good host,
+providing any required features while doing so with a 
+great user experience. Discord has proved itself as a great alternative to our 
+GitHub issue list as a place to informally discuss the project and to seek/provide
+support.
+
+### OVH
+
+![OVH](/images/2021/10/ovh.png)
+
+I've used many hosting providers over the years but I've landed on using
+[OVH](https://www.ovh.co.uk/) for most of my personal projects thanks to their
+relatively cheap price-point for VPS systems in Europe.
+Their reliability may not be at Google Cloud or AWS levels, with a few recent events
+occuring including [data centre fires](https://www.reuters.com/article/us-france-ovh-fire-idUSKBN2B20NU)
+and [network configuration issues](https://www.theregister.com/2021/10/13/ovh_outage/),
+but I'm happy to sacrifice a little reliability for a better price and
+predictable pricing. We use OVH's VPS offerings to host this website & blog.
+
+
+### Algolia DocSearch
+
+![Aloglia DocSearch](/images/2021/10/docsearch.png)
+
+The BookStack documentation pages are generated as part of the website static-site build.
+While there are options for search in this scenario, using [DocSearch](https://docsearch.algolia.com/) by Algolia has made things very easy which providing a 
+great search experience within our documentation. DocSearch is offered free
+by Aloglia for open source projects.
+
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@the_bracketeer?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Hendrik Cornelissen</a> on <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></span>
\ No newline at end of file
diff --git a/content/blog/ubuntu-2204-script.md b/content/blog/ubuntu-2204-script.md
new file mode 100644 (file)
index 0000000..e1866de
--- /dev/null
@@ -0,0 +1,33 @@
++++
+categories = ["News"]
+tags = ["News"]
+title = "Ubuntu 22.04 BookStack Install Script Now Available"
+date = 2022-04-21T15:30:00Z
+author = "Dan Brown"
+image = "/images/blog-cover-images/jellyfish-ruthlaine-tan.jpg"
+slug = "ubuntu-2204-script"
+draft = false
++++
+
+To support today's release of the next LTS version of Ubuntu, 22.04 (Jammy Jellyfish),
+we have added a new BookStack install script for users of this OS:
+
+[Find script details here](/docs/admin/installation/#ubuntu-2204).
+
+Relative to our previous Ubuntu LTS scripts, this 22.04 script introduces a series of improvements including:
+
+- Setting of file/folder permissions based upon the user running the script (Instead of just applying root permissions).
+- Writing of script command output to a file by default, for easier debugging.
+- Simpler command line output to user with clear indication of progress.
+- Checks to ensure a sudo/privileged user is used to run the script.
+- Checks for existing MySQL/Apache usage to prevent conflicts.
+
+Overall these changes provide a much simpler & stable install experience. This following CLI recording shows the improved process (with some steps sped-up):
+
+
+<script id="asciicast-489055" src="https://asciinema.org/a/489055.js" async></script>
+
+----
+
+<span style="font-size: 0.8em;opacity:0.9;">Header Image Credits: <span>Photo by <a href="https://unsplash.com/@ruthlainezz?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Ruthlaine Tan</a> on <a href="https://unsplash.com/s/photos/jellyfish?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>
+  </span></span>
\ No newline at end of file
index 9b9d2b4d654dcbb60fb78c9115753dfbab9f0641..ce358e045c49e0a6f0ed33338969c4d4b0183e38 100644 (file)
@@ -6,8 +6,7 @@ type = "admin-doc"
 +++
 
 BookStack does not currently have a built-in way to backup and restore but it
-can be done via the command line fairly simply. This process will be impoved in
-the future and will ideally be built-in at some point soon.
+can be done via the command line fairly simply.
 
 Please note the below commands are based on using Ubuntu. If you are using a
 different operating system you may have to alter these commands to suit.
@@ -24,16 +23,16 @@ The easiest way to backup the database is via `mysqldump`:
 
 ```bash
 # Syntax
+## Only specify the `-p` option if the user provided has a password
 mysqldump -u {mysql_user} -p {database_name} > {output_file_name}
-## Only specify the -p if the user provided has a password
 
 
 # Example
 mysqldump -u benny bookstack > bookstack.backup.sql
 ```
 
-If you are using MySQL 5.7 on Ubuntu 16.04 and are using the `root` MySQL
-user you will likely have to run the command above with `sudo`:
+If you are using MySQL on Ubuntu, and are using the `root` MySQL
+user, you will likely have to run the command above with `sudo`:
 
 ```bash
 sudo mysqldump -u root bookstack > bookstack.backup.sql
@@ -59,11 +58,7 @@ The following command will create a compressed archive of the above folders and
 files:
 
 ```bash
-# BookStack v0.13+:
 tar -czvf bookstack-files-backup.tar.gz .env public/uploads storage/uploads
-
-# BookStack v0.12.* and below:
-tar -czvf bookstack-files-backup.tar.gz .env public/uploads
 ```
 
 The resulting file (`bookstack-files-backup.tar.gz`) will contain all your file
@@ -75,7 +70,7 @@ data. Copy this to a safe place, ideally on a different device.
 
 If you are restoring from scratch follow the [installation](/docs/admin/installation)
 instructions first to get a new BookStack instance set-up.
-**Do not run the `php artisan migrate` installation step** when installing BookStack.
+**Do not run the `php artisan migrate` installation step when installing BookStack**.
 You may need to comment this command out if using an installer script. If using
 a docker container, restore the database before running the BookStack container.
 Once you are sure the new instance is set-up follow the instructions below.
@@ -94,17 +89,17 @@ mysql -u {mysql_user} -p {database_name} < {backup_file_name}
 # Example
 mysql -u benny -p bookstack < bookstack.backup.sql
 
-# If using the root user on Ubuntu 16.04 and MySQL 5.7 you may
+# If using the root user on Ubuntu you may
 # have to run the above with root permissions via sudo:
 sudo mysql -u root bookstack < bookstack.backup.sql
 ```
 
-If you are restoring to a new verion of BookStack you will have to run
+If you are restoring to a new version of BookStack you will have to run
 `php artisan migrate` after restore to perform any required updates to the database.
 
 #### Files
 
-To restore the files you simple need to copy them from the backup archive
+To restore the files you simply need to copy them from the backup archive
 back to their original locations.  If you created a compressed `bookstack-files-backup.tar.gz`
 archive as per the backup instructions above you can simply copy that file to
 your BookStack folder then run the following command:
@@ -116,7 +111,6 @@ tar -xvzf bookstack-files-backup.tar.gz
 If you get errors during the above command it may be due to permissions.
 Change permissions so you can write to the restore locations.
 
-After a backup of the files you should re-set the permissions to ensure any write-required
+After a backup of the files you should reset the permissions to ensure any write-required
 locations are writable by the server. The locations required for this can be
-found in the [installation](/docs/admin/installation)
-instructions.
+found in the [installation instructions](/docs/admin/installation).
index 2fbf98cb81a2d7e34d8792cfaff5d8e7ff57857f..fc2b20f3d6068991f4d71c4b833e6387bf36f6fd 100644 (file)
@@ -7,53 +7,144 @@ type = "admin-doc"
 
 BookStack has some command line actions that can help with maintenance and common operations. There are also many commands available from the underlying Laravel framework. To list all available commands you can simply run `php artisan` from your BookStack install folder. Custom BookStack commands are all under the 'bookstack' namespace.
 
-### BookStack Commands
+## BookStack Commands
 
 Below is a listing of the BookStack specific commands. For any you can provide a `-h` option to list details and options for the command.
 
+#### Create an Admin User
+
+Create a new admin user via the command line. Can offer a good last resort if you ever get locked out the system.
+Will use the details provided as options otherwise will request them interactively.
+
 ```bash
-# Create a new admin user
+# Interactive usage
 php artisan bookstack:create-admin
 
-# Delete all activity history from the system
+# Non-interactive usage example
+php artisan bookstack:create-admin --email="barry@example.com" --name="Bazza" --password="hunter2"
+
+# Defining "External Authentication ID" instead of password for LDAP/SAML2/OIDC environments
+php artisan bookstack:create-admin --email="barry.booker@example.com" --name="Bazza" --external-auth-id="bbooker"
+```
+
+#### Copy Shelf Permission
+
+By default shelf permissions will not auto-cascade since a book can be in many shelves.
+This command will copy the permissions of a shelf to all child books.
+This can be done for a single shelf or for all shelves in the system:
+
+```bash
+# Run for all shelves
+php artisan bookstack:copy-shelf-permissions --all
+
+# Run for a single shelf
+php artisan bookstack:copy-shelf-permissions --slug=my_shelf_slug
+```
+
+#### Update System URL
+
+BookStack will store absolute URL paths for some content, such as images, in the database.
+If you change your base URL for BookStack this can be problematic.
+This command will essentially run a find & replace operation on all relevant tables in the database.
+Be sure to take a database backup for running this command.
+
+```bash
+# Searches for <oldUrl> and replaces it with <newUrl>
+php artisan bookstack:update-url <oldUrl> <newUrl>
+
+# Example:
+php artisan bookstack:update-url http://docs.example.com https://demo.bookstackapp.com
+```
+
+#### Reset User MFA Methods
+
+This will reset/clear any existing multi-factor-authentication methods for the given user. If MFA 
+is enforced for one of their roles they'll be prompted to reconfigure MFA upon next login.
+
+```bash
+# Via email address:
+php artisan bookstack:reset-mfa --email=john@example.com
+
+# Via system ID:
+php artisan bookstack:reset-mfa --id=5
+```
+
+#### Delete All Activity History
+
+This will clear all tracked activities in the system. Note: Some areas of the interface rely on this data, including the Audit Log and any "Recent Activity" lists.
+
+```bash
 php artisan bookstack:clear-activity
+```
 
-# Delete all page revisions from the system
+#### Delete Page Revisions
+
+By default this deletes all page revisions apart from page update drafts which share the same system.
+A `-a` flag can be used to also delete these update drafts.
+
+```bash
+# Delete just the page revisions
 php artisan bookstack:clear-revisions
 
 # Delete all page revisions from the system including update drafts
 php artisan bookstack:clear-revisions -a
+```
 
-# Delete all page views from the system
+#### Delete Page Views
+
+Delete tracked content views from the system. These are primarily used to populate any "Recently Used" lists.
+
+```bash
 php artisan bookstack:clear-views
+```
+
+#### Cleanup Unused Images
 
-# Search and remove images that are not used in page content
+Searches and removes images that are not used in page content.
+While this can be done in the "Maintenance" section of the interface, running this via the command-line is often safer to avoid timeouts.
+
+```bash
 php artisan bookstack:cleanup-images
+```
 
-# Generate SQL commands that will upgrade the database to UTF8mb4
-# See https://www.bookstackapp.com/docs/admin/ut8mb4-support/
-php artisan bookstack:db-utf8mb4
+#### Regenerate the Search Index
 
-# Rebuild the search index
-# Useful if manually inserting pages into the system
+This can be useful if manually inserting pages into the system.
+
+```bash
 php artisan bookstack:regenerate-search
+```
 
-# Regenerate access permissions - Used mostly in development
-php artisan bookstack:regenerate-permissions
+#### Regenerate Access Permissions
 
-# Delete all users from the system that are not "admin" or system users
-php artisan bookstack:delete-users
+This is primarily used in development but can be useful if manually adding content via the database.
 
-# Copy the permission settings of a specified, or all, shelf to their child books
-php artisan bookstack:copy-shelf-permissions --all
-php artisan bookstack:copy-shelf-permissions --slug=my_shelf_slug
+```bash
+php artisan bookstack:regenerate-permissions
+```
 
-# Update a URL in the database content of your BookStack instance.
-# Searches for <oldUrl> and replaces it with <newUrl>
-php artisan bookstack:update-url <oldUrl> <newUrl>
-# Example:
-php artisan bookstack:update-url http://docs.example.com https://demo.bookstackapp.com
+#### Regenerate Comment Content
 
-# Regenerate the stored HTML content for comments from their original text content
+Comments are created and stored in Markdown but also rendered to HTML on save.
+This command will regenerate the stored HTML content for all comments using the original Markdown content.
+
+```bash
 php artisan bookstack:regenerate-comment-content
 ```
+
+#### Delete Users
+
+Delete all users from the system that are not original "admin" or system-level users.
+
+```bash
+php artisan bookstack:delete-users
+```
+
+#### Generate UTF8mb4 SQL Upgrade Commands
+
+Generates out the SQL which can be used to upgrade the database to UTF8mb4.
+See [UTF8mb4/Emoji Support](/docs/admin/ut8mb4-support/) for further details.
+
+```bash
+php artisan bookstack:db-utf8mb4
+```
\ No newline at end of file
index 81f8251650248521ed3e099636796daf80be7b70..998cb9bb63c429d4fa7f4e01c5ff701acf5dfd6a 100644 (file)
@@ -5,14 +5,86 @@ date = "2017-01-01"
 type = "admin-doc"
 +++
 
-When using BookStack, Especially when initially setting it up or after updating, you may come across some errors. While we try to reduce these as much as possible and make them helpful sometimes you may come across a bland, non-helpful 'An error has occurred' message. This is to prevent any potentially sensitive information being shown to all users.
 
-_**NOTE: While debugging is enabled it's possible for users to be able to see private credentials used in the BookStack or server configuration. Ensure debugging is only enabled when your instance is not accessible by others. Remember to disable debugging before restoring user access.**_
+When using BookStack, especially when initially setting it up or after updating, you may come across some errors. While we try to reduce these as much as possible and make them helpful sometimes you may come across a bland "An error has occurred" message. This is to prevent any potentially sensitive information being shown to all users.
 
-To enable full error displaying edit the `.env` file, in the application root directory, and find the line `APP_DEBUG=false`. Change this to `APP_DEBUG=true` and the errors will be displayed in full with details of where it occurred. Remember to revert this change once you have found the issue so that the detailed error information is hidden from everyone.
+This page details how you can find out more information about errors you may face and the resolutions to common problems we often see.
 
-On top of the above, An error log can be found at the path `storage/logs/laravel.log`. If the `laravel.log` file does not exist the `storage/logs` directory may not be writable by the server.
+- [Error Log File](#error-log-file)
+- [Debug View](#debug-view)
+- [Common Issues & Resolutions](#common-issues--resolutions)
+    - [Blank White Screen on Access](#blank-white-screen-on-access)
+    - [No Styles and Large Icons on Access](#no-styles-and-large-icons-on-access)
+    - [Failing Database or Auth System Credentials](#failing-database-or-auth-system-credentials)
+    - [Broken Links or No Images After APP_URL Change](#broken-links-or-no-images-after-app_url-change)
+- [Submitting Issues](#submitting-issues)
+
+### Error Log File
+
+On a common installation, an error log can be found at the path `storage/logs/laravel.log`, relative to the top-level folder of your BookStack installation files. 
+For instances installed using our Ubuntu installation scripts, this is commonly located at `/var/www/bookstack/storage/logs/laravel.log`.
+If the `laravel.log` file does not exist the `storage/logs` directory may not be writable by the server.
+
+In some cases an error may be thrown before or after BookStack handles a request. These will typically show as very plain error pages
+without any BookStack header bar or styling. In these cases you'll want to refer to your webserver error logs instead.
+
+- Common Apache error log path on Ubuntu: `/var/log/apache2/error.log`
+- Common Nginx error log path on Ubuntu: `/var/log/nginx/error.log`
+
+You will likely need privileged access to read these files. For example `sudo tail -n 100 /var/log/apache2/error.log`.
+
+If using an alternative hosting method, such as docker container, please refer to the documentation provided by that method/container.
+
+### Debug View
+
+_**NOTE: When debugging is enabled it's possible for visitors to see private credentials used in the BookStack or server configuration. Ensure debugging is only enabled when your instance is not accessible by others. Remember to disable debugging before restoring access.**_
+
+To enable full error displaying edit the `.env` file, in the application root directory, and find the line `APP_DEBUG=false`. Change this to `APP_DEBUG=true` and the errors will be displayed in full with details of where it occurred. Remember to revert this change once you have found the issue so that the detailed error information is hidden from visitors.
+
+### Common Issues & Resolutions
+
+The below lists some of the common issues we see, when it comes to installing & maintaining BookStack, and their usual resolutions.
+
+##### Blank White Screen on Access
+
+A completely blank screen, when attempting to access your BookStack instance, is commonly due to system/folder permission issues.
+Check that webserver user has read/write access to the `bootstrap/cache`, `storage` and `public/uploads` folders within your BookStack install.
+On a common Ubuntu install this can usually be done by running `sudo chown -R www-data:www-data bootstrap/cache storage public/uploads` from within the BookStack install folder.
+
+If you don't believe this is due to permissions, and you have nothing in the `storage/logs/laravel.log` file as detailed above, you'd next want to look at your webserver error logs which can commonly be found within `/var/log/nginx/` or `/var/logs/apache2/`.
+
+##### No Styles and Large Icons on Access
+
+If the page loads, but shows large icons and appears broken, it generally means the system is generating incorrect paths to some required files
+or those files do not exist.
+
+- Check that the `APP_URL` option is set in your `.env` file and ensure it matches the URL you're accessing BookStack on (Including the "https://" or "http://" component).
+- Check that you're using the `release` code branch. If you cloned the project without a branch flag, or downloaded the files from GitHub, you may be using the development branch files which does not include some of these required files.
+
+##### Failing Database or Auth System Credentials
+
+If you've used a password in the `.env` file which you are sure is correct, but authentication fails for that service, it may be due to 
+special characters being handled different in the `.env` configuration. Ensure that any such passwords or config options are wrapped in quotes. For example:
+
+```txt
+DB_PASSWORD="hunter#2@"
+```
+
+##### Broken Links or No Images After APP_URL Change
+
+BookStack will store absolute URL paths for some content, such as images, in the database.
+If you change your base URL for BookStack this can be problematic.
+This command will essentially run a find & replace operation on all relevant tables in the database.
+Be sure to take a database backup for running this command.
+
+```bash
+# Searches for <oldUrl> and replaces it with <newUrl>
+php artisan bookstack:update-url <oldUrl> <newUrl>
+
+# Example:
+php artisan bookstack:update-url http://docs.example.com https://demo.bookstackapp.com
+```
 
 ### Submitting Issues
 
-If you have found the cause of the issue and it is not an issue with your particular setup you can submit it on the [GitHub issues page](https://github.com/BookStackApp/BookStack/issues). Please include as much information as possible when creating an issue with steps with how to replicate it so the bug can be fixed by a developer.
+If you have found the cause of the issue, and it is not an issue with your particular setup, you can submit it on the [GitHub issues page](https://github.com/BookStackApp/BookStack/issues). Please include as much information as possible when creating an issue along with steps detailing how to replicate it so the bug can be fixed by a developer.
diff --git a/content/docs/admin/email-config.md b/content/docs/admin/email-config.md
deleted file mode 100644 (file)
index fb13432..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-+++
-title = "Email Configuration"
-description = "Configuring BookStack to send email"
-date = "2020-03-14"
-type = "admin-doc"
-slug = "email-config"
-+++
-
-BookStack sends out emails for a range of purposes such as email-address confirmation & "forgot password" flows.
-The following mechanisms for sending mail are supported:
-
-1. SMTP
-3. Sendmail (Linux sendmail)
-
-## SMTP
-
-To get up and running with SMTP you will need to add, or set, the following variables in your `.env` file:
-
-```bash
-MAIL_DRIVER=smtp
-
-# Host, Port & Encryption mechanism to use
-MAIL_HOST=smtp.provider.tld
-MAIL_PORT=465
-MAIL_ENCRYPTION=tls
-
-# Authentication details for your SMTP service
-MAIL_USERNAME=user@provider.tld
-MAIL_PASSWORD=onlyifneeded
-
-# The "from" email address for outgoing email
-MAIL_FROM=noreply@yourdomain.tld  
-
-# The "from" name used for outgoing email
-MAIL_FROM_NAME=BookStack
-```
-
-## Sendmail
-
-The `sendmail` drivers uses the sendmail application on the host system. It will call `/usr/sbin/sendmail -bs`.
-
-To enable this option you can set the following in your `.env` file:
-
-```bash
-MAIL_DRIVER=sendmail
-
-# The "from" email address for outgoing email
-MAIL_FROM=noreply@yourdomain.tld  
-
-# The "from" name used for outgoing email
-MAIL_FROM_NAME=BookStack
-```
-
-## Debugging
-
-You can follow the instructions provided in the [debugging documentation page](/docs/admin/debugging/)
-to help gain more details about issues you may come across. Within the Settings > Maintenance area of
-BookStack you can find a "Send a Test Email" action which provides a quick & easy way to send emails
-after changing your configuration. This action will also attempt to capture any errors thrown and display them.
\ No newline at end of file
diff --git a/content/docs/admin/email-webhooks.md b/content/docs/admin/email-webhooks.md
new file mode 100644 (file)
index 0000000..95fb430
--- /dev/null
@@ -0,0 +1,139 @@
++++
+title = "Email & Webhooks"
+date = "2021-12-21"
+type = "admin-doc"
+slug = "email-webhooks"
+aliases = ["email-config"]
++++
+
+Within BookStack email is used in various ways relating to user management & authentication. 
+Outgoing webhooks are available as a mechanism to extend BookStack or notify in an event-driven manner.
+
+- [Email Configuration](#email-configuration)
+- [Outgoing Webhooks](#outgoing-webhooks)
+- [Async Action Handling](#async-action-handling)
+
+---
+
+### Email Configuration
+
+BookStack sends out emails for a range of purposes such as email-address confirmation & "forgot password" flows.
+Both SMTP and Sendmail (Linux Sendmail) are supported email mechanisms.
+
+#### SMTP
+
+To get up and running with SMTP you will need to add, or set, the following variables in your `.env` file:
+
+```bash
+MAIL_DRIVER=smtp
+
+# Host, Port & Encryption mechanism to use
+MAIL_HOST=smtp.provider.tld
+MAIL_PORT=465
+MAIL_ENCRYPTION=tls
+
+# Authentication details for your SMTP service
+MAIL_USERNAME=user@provider.tld
+MAIL_PASSWORD=onlyifneeded
+
+# The "from" email address for outgoing email
+MAIL_FROM=noreply@yourdomain.tld  
+
+# The "from" name used for outgoing email
+MAIL_FROM_NAME=BookStack
+```
+
+#### Sendmail
+
+The `sendmail` drivers uses the sendmail application on the host system. It will call `/usr/sbin/sendmail -bs`.
+
+To enable this option you can set the following in your `.env` file:
+
+```bash
+MAIL_DRIVER=sendmail
+
+# The "from" email address for outgoing email
+MAIL_FROM=noreply@yourdomain.tld  
+
+# The "from" name used for outgoing email
+MAIL_FROM_NAME=BookStack
+```
+
+#### Debugging Email
+
+You can follow the instructions provided in the [debugging documentation page](/docs/admin/debugging/)
+to help gain more details about issues you may come across. Within the "Settings > Maintenance" area of
+BookStack you can find a "Send a Test Email" action which provides a quick & easy way to send emails
+after changing your configuration. This action will also attempt to capture any errors thrown and display them.
+
+---
+
+### Outgoing Webhooks
+
+Webhooks can be configured in the "Settings > Webhooks" area of your BookStack instance.
+An example of the POST data format is shown when creating or editing a webhook.
+
+The webhook data is "Slack Compatible" in respect to having a `text` property containing a human-readable description
+of the event. Services such as [Discord](https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook), [Zulip](https://zulip.com/integrations/doc/slack_incoming) and Teams, upon many others, have options to support this format.
+
+A video guide on BookStack webhooks, including usage with Discord and HomeAssistant, [can be found here](https://www.youtube.com/watch?v=_zIp1ruGpoI).
+
+The running of webhooks can slow down a system due to the required additional processing time.
+See the [async action handling](#async-action-handling) section below to details on running webhooks
+in a background process to improve performance.
+
+---
+
+
+### Async Action Handling
+
+Actions like sending email or triggering webhooks are performed synchronously by default which can
+slow down page loading when those actions are triggered. These actions can instead be processed asynchronously
+so they're handled in the background to prevent slowing down the request. This requires a config change 
+and a queue worker process to handle these background jobs:
+
+#### Config Change
+
+Within your `.env` file add or update (if already existing) the following option then save the file.
+
+```bash
+QUEUE_CONNECTION=database
+```
+
+#### Queue Worker Process
+
+The queue work process can be run via the following command from your BookStack installation directory:
+
+```bash
+php artisan queue:work --sleep=3 --tries=3
+```
+
+Ideally this needs to be ran continuously. The method of doing this may depend on your operating system
+and personal software preferences. On many modern Linux systems systemd is an appropriate method.
+The below unit file example can be used with systemd to run this process. Note, this is suited to 
+Ubuntu 20.04 setups that have used our installation script. Details may need tweaking for other systems.
+
+```bash
+[Unit]
+Description=BookStack Queue Worker
+
+[Service]
+User=www-data
+Group=www-data
+Restart=always
+ExecStart=/usr/bin/php /var/www/bookstack/artisan queue:work --sleep=3 --tries=1 --max-time=3600
+
+[Install]
+WantedBy=multi-user.target
+```
+
+To configure systemd (On a Ubuntu 20.04 system) with the above unit you'd typically:
+
+- Create a new `/etc/systemd/system/bookstack-queue.service` file containing the above content.
+- Run `systemctl daemon-reload` to discover the new service.
+- Run `systemctl enable bookstack-queue.service` to ensure the service starts at (re)boot.
+- Run `systemctl start bookstack-queue.service` to start the queue service.
+
+Note: you may need to run the above commands with `sudo` if not acting as a privileged user. 
+
+You can then use `systemctl status bookstack-queue.service` to check the status of the queue worker. 
index 76c70e6ed04bae8d3789aeeaab7ed90fa6c2e2f1..080488c27df50277cb9e553ba1495eccf61084c2 100644 (file)
@@ -14,9 +14,12 @@ _**Note: Customisation options on this page are not deemed to be stable or offic
 
 ### BookStack API
 
-_**Note: The API is currently in a limited preview stage to ensure the foundations are correct, It will be expanded upon in future releases.**_
+BookStack has a built-in REST API for external interaction and consumption of your BookStack data. API documentation can be found within your BookStack instance at the `/api/docs` URL path. You'll need to have the 'Access system API' role permission to access the API.
 
-BookStack has a built-in REST API for external interaction and consumption of your BookStack data. API documentation can be found within your BookStack instance at the `/api/docs` URL path. You'll need to have the 'Access system API' role permission to access the API or view the API documentation page.
+**Reference Links**
+
+- [API documentation of our demo instance](https://demo.bookstackapp.com/api/docs).
+- [Our "BookStack API Scripts" repo containing examples](https://github.com/BookStackApp/api-scripts).
 
 ---
 
@@ -28,7 +31,9 @@ Within the settings area you'll find a 'Custom HTML head content' setting. You c
 
 ### BookStack Editor Events
 
-Both the TinyMCE based WYSIWYG editor and the CodeMirror based Markdown editor emit events as part of their lifecycle. You can listen for these hook in and alter their configs or to gain a reference to the underlying editor instance. The below code sample shows the events available; Log out the `detail` property of events, as per the below example, to understand what is passed with the events:
+For the core underlying libraries, used in the BookStack page editors, we emit some custom events as part of their lifecycle.
+You can listen for these events to hook in and alter their configs or to gain a reference to the underlying editor instance.
+The below code sample shows the events available; Log out the `detail` property of events, as per the below example, to understand what is passed with the events:
 
 ```html
 <script>
@@ -39,41 +44,31 @@ Both the TinyMCE based WYSIWYG editor and the CodeMirror based Markdown editor e
        // CodeMirror / Markdown-it Markdown editor events
        window.addEventListener('editor-markdown-cm::pre-init', event => console.log('MARKDOWN-CODEMIRROR-PRE_INIT', event.detail));
        window.addEventListener('editor-markdown::setup', event => console.log('MARKDOWN-EDITOR-SETUP', event.detail));
+
+       // Diagrams.net configure event
+       // Reference: https://www.diagrams.net/doc/faq/configure-diagram-editor
+       // If using a custom diagrams.net instance, via the `DRAWIO` option, you will need to ensure
+       // this your URL has the `configure=1` query parameter.
+       window.addEventListener('editor-drawio::configure', event => console.log('DIAGRAMS.NET-CONFIGURE', event.detail));
 </script>
 ```
 
 ---
 
-### Theme System
-
-_**Note: The files that can be override using the theme system are not deemed to be stable. BookStack core files may change on any release causing changes in behaviour to your overrides. Theme overrides are not officially supported in any way.**_
-
-The theme system provides additional customisation options for those that are a bit more adventurous. The theme system enables you to selectively override BookStack UI resources without having to alter the original BookStack code. To get started create a new folder in the `themes` folder of your BookStack install. Edit your `.env` file and add the following:
+### Visual Theme System
 
-```bash
-# Change the value below to match the name of your created folder
-APP_THEME=<theme_folder_name>
-```
+BookStack allows visual customization via the theme system which enables you to extensively customize views, translation text & icons.
+Documentation for this system is contained within [the project repo here](https://github.com/BookStackApp/BookStack/blob/development/dev/docs/visual-theme-system.md).
 
-Files can now be placed in your theme folder to override the following resources:
-
-##### View Files
-
-Content placed in your `themes/<theme_name>/` folder will override the original view files found in the `resources/views` folder. These files are typically [Laravel Blade](https://laravel.com/docs/6.x/blade) files.
-
-##### Icons
+_**Note: The files that can be override using the theme system are not deemed to be stable. BookStack core files may change on any release causing changes in behaviour to your overrides. Theme overrides are not officially supported in any way.**_
 
-SVG files placed in a `themes/<theme_name>/icons` folder will override any icons of the same name within `resources/icons`. You'd typically want to follow the format convention of the existing icons, where no XML deceleration is included and no width & height attributes are set, to ensure optimal compatibility. 
 
-##### Text Content
+---
 
-Folders with PHP translation files placed in a `themes/<theme_name>/lang` folder will override translations defined within the `resources/lang` folder. Custom translations are merged with the original files so you only need to override the select translations you want to override, you don't need to copy the whole original file. Note that you'll need to include the language folder.
+### Logical Theme System
 
-As an example, Say I wanted to change 'Search' to 'Find'; Within a `themes/<theme_name>/lang/en/common.php` file I'd set the following:
+BookStack allows PHP code-based extension via what we call the "Logical Theme System". 
+This works by hooking into specific events where you can then perform custom actions or extension of the underlying framework.
+Documentation for this system is contained within [the project repo here](https://github.com/BookStackApp/BookStack/blob/development/dev/docs/logical-theme-system.md).
 
-```php
-<?php
-return [
-    'search' => 'find',
-];
-```
\ No newline at end of file
+_**Note: Only the API described in the logical-theme-system document is considered stable & supported. Any usage of other application classes is regarded as unstable and unsupported.**_
\ No newline at end of file
index 1ba7398cb94c969e6eae9bd63f4769d16c9b9b3f..4e252c755931fdb4c98616e744f2047ef535f468 100644 (file)
@@ -11,10 +11,12 @@ Below you can find details on how to install BookStack on your own hosting. Ther
 * [Shared Hosting](#shared)
 * [Manual](#manual)
 * [Docker](#docker)
+* [Ubuntu 22.04 Script](#ubuntu-2204)
 * [Ubuntu 20.04 Script](#ubuntu-2004)
 * [Ubuntu 18.04 Script](#ubuntu-1804)
-* [Ubuntu 16.04 Script](#ubuntu-1604)
 * [Community Guides](#community)
+* [Other Hosting Options](#other)
+* [High Availability](#ha)
 
 ---
 
@@ -24,14 +26,18 @@ Below you can find details on how to install BookStack on your own hosting. Ther
 
 BookStack has the following requirements:
 
-* **PHP** >= 7.2
-    * For installation and maintenence, you'll need to be able to run `php` from the command line.
-    * Required Extensions: *OpenSSL, PDO, MBstring, Tokenizer, GD, MySQL, Tidy, SimpleXML & DOM*
-* **MySQL** >= 5.6
-    *  Single Database *(All permissions advised since application manages schema)*
+* **PHP** >= 7.4
+    * For installation and maintenance, you'll need to be able to run `php` from the command line.
+    * Required Extensions: *OpenSSL, PDO, MBstring, Tokenizer, GD, MySQL, SimpleXML & DOM*
+* **MySQL** >= 5.7 or **MariaDB** >= 10.2
+    * For the storage of BookStack content and data.
+    * Single Database *(All permissions advised since application manages schema)*
 * **Git Version Control**
-    * (Not strictly required but helps manage updates)
-* **[Composer](https://getcomposer.org/)**
+    * For application of updates when following our standard process.
+* **[Composer](https://getcomposer.org/)** >= v2.0
+    * For installation and management of our PHP dependencies.
+* **A PHP Compatible Webserver**
+    * For usage with PHP and for serving static files.
 
 ---
 
@@ -64,24 +70,10 @@ git clone https://github.com/BookStackApp/BookStack.git --branch release --singl
 8. Run `php artisan migrate` to update the database.
 9. Done! You can now login using the default admin details `admin@admin.com` with a password of `password`. You should change these details immediately after logging in for the first time.
 
-#### URL Rewrite rules
+#### Webserver Configuration
 
-**Apache**
-```apache
-Options +FollowSymLinks
-RewriteEngine On
-
-RewriteCond %{REQUEST_FILENAME} !-d
-RewriteCond %{REQUEST_FILENAME} !-f
-RewriteRule ^ index.php [L]
-```
-
-**Nginx**
-```nginx
-location / {
-    try_files $uri $uri/ /index.php?$query_string;
-}
-```
+- [Example Apache VirtualHost configuration](https://github.com/BookStackApp/devops/blob/main/config/apache/bookstack.conf)
+- [Example Nginx Server block](https://github.com/BookStackApp/devops/blob/main/config/nginx)
 
 ---
 
@@ -94,7 +86,7 @@ Community docker setups are available for those that would prefer to use a conta
 #### LinuxServer.io
 
 * [GitHub Repository](https://github.com/linuxserver/docker-bookstack)
-* [Docker Hub page](https://hub.docker.com/r/linuxserver/bookstack)
+* [GitHub container package](https://github.com/linuxserver/docker-bookstack/pkgs/container/bookstack)
 
 #### solidnerd
 
@@ -103,13 +95,13 @@ Community docker setups are available for those that would prefer to use a conta
 
 ---
 
-<a name="ubuntu-2004"></a>
+<a name="ubuntu-2204"></a>
 
-## Ubuntu 20.04 Installation Script
+## Ubuntu 22.04 Installation Script
 
-A script to install BookStack on a fresh instance of Ubuntu 20.04 is available. This script is ONLY FOR A FRESH OS, It will install Apache, MySQL 8.0 & PHP-7.4 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
+A script to install BookStack on a fresh instance of Ubuntu 22.04 is available. This script is ONLY FOR A FRESH OS, it will install Apache, MySQL 8.0 & PHP-8.1 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
 
-[Link to installation script](https://github.com/BookStackApp/devops/blob/master/scripts/installation-ubuntu-20.04.sh)
+[Link to installation script](https://github.com/BookStackApp/devops/blob/main/scripts/installation-ubuntu-22.04.sh)
 
 #### Running the Script
 
@@ -117,25 +109,27 @@ A script to install BookStack on a fresh instance of Ubuntu 20.04 is available.
 # Ensure you have read the above information about what this script does before executing these commands.
 
 # Download the script
-wget https://raw.githubusercontent.com/BookStackApp/devops/master/scripts/installation-ubuntu-20.04.sh
+wget https://raw.githubusercontent.com/BookStackApp/devops/main/scripts/installation-ubuntu-22.04.sh
 
 # Make it executable
-chmod a+x installation-ubuntu-20.04.sh
+chmod a+x installation-ubuntu-22.04.sh
 
 # Run the script with admin permissions
-sudo ./installation-ubuntu-20.04.sh
+sudo ./installation-ubuntu-22.04.sh
 ```
 
+The script will output a log file for debugging within your current working directory when running the script.
+Permissions for the BookStack installation files & folders will be set based upon the user used to run the script.
 
 ---
 
-<a name="ubuntu-1804"></a>
+<a name="ubuntu-2004"></a>
 
-## Ubuntu 18.04 Installation Script
+## Ubuntu 20.04 Installation Script
 
-A script to install BookStack on a fresh instance of Ubuntu 18.04 is available. This script is ONLY FOR A FRESH OS, It will install Apache, MySQL 5.7 & PHP-7.2 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
+A script to install BookStack on a fresh instance of Ubuntu 20.04 is available. This script is ONLY FOR A FRESH OS, it will install Apache, MySQL 8.0 & PHP-7.4 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
 
-[Link to installation script](https://github.com/BookStackApp/devops/blob/master/scripts/installation-ubuntu-18.04.sh)
+[Link to installation script](https://github.com/BookStackApp/devops/blob/main/scripts/installation-ubuntu-20.04.sh)
 
 #### Running the Script
 
@@ -143,24 +137,25 @@ A script to install BookStack on a fresh instance of Ubuntu 18.04 is available.
 # Ensure you have read the above information about what this script does before executing these commands.
 
 # Download the script
-wget https://raw.githubusercontent.com/BookStackApp/devops/master/scripts/installation-ubuntu-18.04.sh
+wget https://raw.githubusercontent.com/BookStackApp/devops/main/scripts/installation-ubuntu-20.04.sh
 
 # Make it executable
-chmod a+x installation-ubuntu-18.04.sh
+chmod a+x installation-ubuntu-20.04.sh
 
 # Run the script with admin permissions
-sudo ./installation-ubuntu-18.04.sh
+sudo ./installation-ubuntu-20.04.sh
 ```
 
+
 ---
 
-<a name="ubuntu-1604"></a>
+<a name="ubuntu-1804"></a>
 
-## Ubuntu 16.04 Installation Script
+## Ubuntu 18.04 Installation Script
 
-A script to install BookStack on a fresh instance of Ubuntu 16.04 is available. This script is ONLY FOR A FRESH OS, It will install Nginx, MySQL 5.7 & PHP7 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
+A script to install BookStack on a fresh instance of Ubuntu 18.04 is available. This script is ONLY FOR A FRESH OS, it will install Apache, MySQL 5.7 & PHP-7.4 and could OVERWRITE any existing web setup on the machine. It also does not set up mail settings or configure system security so you will have to do those separately. You can use the script as a reference if you're installing on a non-fresh machine.
 
-[Link to installation script](https://github.com/BookStackApp/devops/blob/master/scripts/installation-ubuntu-16.04.sh)
+[Link to installation script](https://github.com/BookStackApp/devops/blob/main/scripts/installation-ubuntu-18.04.sh)
 
 #### Running the Script
 
@@ -168,13 +163,13 @@ A script to install BookStack on a fresh instance of Ubuntu 16.04 is available.
 # Ensure you have read the above information about what this script does before executing these commands.
 
 # Download the script
-wget https://raw.githubusercontent.com/BookStackApp/devops/master/scripts/installation-ubuntu-16.04.sh
+wget https://raw.githubusercontent.com/BookStackApp/devops/main/scripts/installation-ubuntu-18.04.sh
 
 # Make it executable
-chmod a+x installation-ubuntu-16.04.sh
+chmod a+x installation-ubuntu-18.04.sh
 
 # Run the script with admin permissions
-sudo ./installation-ubuntu-16.04.sh
+sudo ./installation-ubuntu-18.04.sh
 ```
 
 ---
@@ -188,3 +183,45 @@ This is a collection of guides created by awesome members of the BookStack commu
 * [RHEL 8 Install (Oracle/Alma Linux) by @xhark](https://github.com/blogmotion/bm-bookstack-install/) - [french guide](https://blogmotion.fr/internet/bookstack-script-installation-centos-8-18255)
 * [CentOS 7 Install by Deviant Engineer](https://deviant.engineer/2017/02/bookstack-centos7/)
 * [Fedora 27 Install by Jared Busch](https://mangolassi.it/topic/16471/install-bookstack-on-fedora-27/)
+* [Debian 11 Install by Othman Alikhan](https://gist.github.com/OthmanEmpire/322f83a77c15dfd1c91a2afe0b6a6fc2)
+
+---
+
+<a name="other"></a>
+
+## Other Hosting Options
+
+Below are some alternative platforms and services that can be used to host BookStack. <br>
+_**Note: These are not tested, vetted nor supported by the official BookStack project in any manner**_.
+
+- [Cloudron](https://www.cloudron.io/store/com.bookstackapp.cloudronapp.html) - A solution for running apps on your own server.
+- [Uberspace](https://lab.uberspace.de/guide_bookstack.html) - A European based hosting provider.
+- [Home Assistant Community Add-on](https://github.com/hassio-addons/addon-bookstack) - For [Home Assistant](https://www.home-assistant.io/) users.
+- [Stellar Hosted](https://www.stellarhosted.com/bookstack/) - A European based managed hosting provider.
+- [alwaysdata](https://www.alwaysdata.com/en/marketplace/bookstack/) - A European based managed hosting provider.
+- [PikaPods](https://www.pikapods.com/pods?run=bookstack) - Managed open source hosting, EU and US regions available.
+- [YunoHost](https://install-app.yunohost.org/?app=bookstack)) - A Debian-based distribution that automates personal web server installation.
+
+---
+
+<a name="ha"></a>
+
+## High Availability
+
+Some enterprise environments may need to configure a "High Availability" setup of BookStack to include some operational redundancy.
+**This type of setup is not needed for the vast majority of BookStack instances.**
+For a "High Availability" BookStack setup you'll likely need to consider the following:
+
+- High availability is not something we assure to support. There may be scenarios that will not allow availability.
+  - For example: The BookStack upgrade process does not assure availability when ran.
+- Sessions and Cache use the local filesystem by default. 
+  - The database/memcached/redis options allow sharing across instances.
+  - [Cache and session options are documented here](/docs/admin/cache-session-config/).
+- Uploaded files use the local filesystem by default.
+  - You could instead use S3 or an S3 like storage system for these files.
+  - Alternatively you could mount/link the used directories to shared locations.
+  - [Directory locations and storage options are documented here](/docs/admin/upload-config/).
+- The [theme system](/docs/admin/hacking-bookstack/#theme-system) and [error log](/docs/admin/debugging/) also use the local filesystem.
+- A simplistic health-check endpoint can be found at the `/status` URI.
+  - This performs basic checks on subsystems.
+  - This should return a HTTP error status code (>=400) on any failure otherwise a 200 status code.
index aa8e9412f5c65f97221677618843fe0cf10025bd..29556cf5335c369638cc57799d6593174d89b103 100644 (file)
@@ -23,24 +23,42 @@ APP_LANG=fr
 
 The value of the `APP_LANG` variable must be a valid locale code matching one of the following options:
 
-* Brazilian Portuguese - `pt_BR`
+* Arabic - `ar`
+* Bosnian - `bs`
+* Bulgarian - `bg`
+* Catalan - `ca`
 * Chinese (Simplified) - `zh_CN`
 * Chinese (Traditional) - `zh_TW`
+* Croatian - `hr`
+* Czech - `cs`
+* Danish - `da`
 * Dutch - `nl`
 * English - `en`
 * French - `fr`
 * German (Formal) - `de`
 * German (Informal) - `de_informal`
+* Hebrew - `he`
+* Hungarian - `hu`
+* Indonesian - `id`
 * Italian - `it`
 * Japanese - `ja`
-* Korean - `kr`
+* Korean - `ko`
+* Latvian - `lv`
+* Lithuanian - `lt`
+* Norwegian Bokmal - `nb`
+* Persian - `fa`
 * Polish - `pl`
+* Portuguese - `pt_PT`
+* Brazilian Portuguese - `pt_BR`
 * Russian - `ru`
-* Slovakian - `sk`
-* Spanish - `es`
-* Spanish, Argentinian - `es_AR`
+* Slovak - `sk`
+* Slovenian - `sl`
+* Spanish - `es_ES`
+* Argentinian Spanish - `es_AR`
 * Swedish - `sv`
+* Turkish - `tr`
 * Ukrainian - `uk`
+* Vietnamese - `vi`
 
 ### Public User Locale Autodetection
 
@@ -63,8 +81,8 @@ sudo dpkg-reconfigure locales
 ```
 
 For other operating systems this may be different. After installing new locales you may need to restart any running PHP processes.
-For example, On Ubuntu, running PHP7.2:
+For example, On Ubuntu, running PHP 7.4:
 
 ```bash
-sudo systemctl restart php7.2-fpm.service 
-```
\ No newline at end of file
+sudo systemctl restart php7.4-fpm.service 
+```
index 14b28e2ba3665b55beecf6e97bbfc8697fdb0ebf..43ba8c32f2a442386d3d034812e886de391499e3 100644 (file)
@@ -5,7 +5,9 @@ date = "2017-01-21"
 type = "admin-doc"
 +++
 
-BookStack can be configured to allow LDAP based user login. While LDAP login is enabled you cannot log in with the standard user/password login and new user registration is disabled. BookStack will only use the LDAP server for getting user details and for authentication. Data on the LDAP server is not currently editable through BookStack.
+BookStack can be configured to allow LDAP based user login. While LDAP login is enabled you cannot log in with the standard user/password login and new user registration is disabled. BookStack will only use the LDAP server for getting user details and for authentication. Data on the LDAP server is not editable through BookStack.
+
+[A video guide for setting up LDAP can be found here](https://www.youtube.com/watch?v=50qw_LkhwoM).
 
 ### Authentication Setup
 
@@ -23,19 +25,23 @@ LDAP_SERVER=example.com:389
 # LDAP_SERVER=ldaps://example.com:636
 
 # The base DN from where users will be searched within
-LDAP_BASE_DN=ou=People,dc=example,dc=com
+LDAP_BASE_DN="ou=People,dc=example,dc=com"
 
 # The full DN and password of the user used to search the server
-# Can both be left as false to bind anonymously
-LDAP_DN=false
-LDAP_PASS=false
+# Can both be left as 'false' (without quotes) to bind anonymously
+LDAP_DN="cn=serviceaccount,ou=People,dc=example,dc=org"
+LDAP_PASS="my#super#secret#password543"
 
 # A filter to use when searching for users
 # The user-provided user-name used to replace any occurrences of '${user}'
+# If you're setting this option via other means, such as within a docker-compose.yml,
+# you may need escape the $, often using $$ or \$ instead.
+# Note: This option cannot be used with the docker-compose.yml `env_file` option.
 LDAP_USER_FILTER=(&(uid=${user}))
 
 # Set the LDAP version to use when connecting to the server
-LDAP_VERSION=false
+# Should be set to 3 in most cases.
+LDAP_VERSION=3
 
 # Set the property to use as a unique identifier for this user.
 # Stored and used to match LDAP users with existing BookStack users.
@@ -50,8 +56,22 @@ LDAP_EMAIL_ATTRIBUTE=mail
 # Set the property to use for a user's display name. Defaults to 'cn'
 LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 
+# Set the attribute to use for the user's avatar image.
+# Must provide JPEG binary image data.
+# Will be used upon login or registration when the user doesn't
+# already have an avatar image set.
+# Remove this option or set to 'null' to disable LDAP avatar import.
+LDAP_THUMBNAIL_ATTRIBUTE=jpegphoto
+
+# Force TLS to be used for LDAP communication.
+# Use this if you can but your LDAP support will need to support it and
+# you may need to import your certificate to the BookStack host machine.
+# Defaults to 'false'.
+LDAP_START_TLS=false
+
 # If you need to allow untrusted LDAPS certificates, add the below and uncomment (remove the #)
 # Only set this option if debugging or you're absolutely sure it's required for your setup.
+# If using php-fpm, you may want to restart it after changing this option to avoid instability.
 #LDAP_TLS_INSECURE=true
 
 # If you need to debug the details coming from your LDAP server, add the below and uncomment (remove the #)
@@ -59,9 +79,9 @@ LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 #LDAP_DUMP_USER_DETAILS=true
 ```
 
-You will also need to have the php-ldap extension installed on your system. It's recommended to change your `APP_DEBUG` variable to `true` while setting up LDAP to make any errors visible. Remember to change this back after LDAP is functioning.
+You will also need to have the php-ldap extension installed on your system. You can change your `APP_DEBUG` variable to `true` while setting up LDAP to make any errors visible. Note that debug mode can expose sensitive details to visitors so you may want to limit access while configuring and you should remember to change this back after LDAP is functioning.
 
-A user in BookStack will be linked to a LDAP user via a 'uid'. If a LDAP user uid changes it can be updated in BookStack by an admin by changing the 'External Authentication ID' field on the user's profile.
+A user in BookStack will be linked to a LDAP user via a 'uid'. If an LDAP user uid changes it can be updated in BookStack by an admin by changing the 'External Authentication ID' field on the user's profile.
 
 You may find that you cannot log in with your initial Admin account after changing the `AUTH_METHOD` to `ldap`. To get around this set the `AUTH_METHOD` to `standard`, login with your admin account then change it back to `ldap`. You get then edit your profile and add your LDAP uid under the 'External Authentication ID' field. You will then be able to login in with that ID.
 
@@ -74,6 +94,10 @@ depending on your setup and how you manage users in the system. You will still n
 LDAP_USER_FILTER=(&(sAMAccountName=${user}))
 LDAP_VERSION=3
 LDAP_ID_ATTRIBUTE=BIN;objectGUID
+# Change the below to true if your AD server supports TLS and if your
+# BookStack host system will accept the AD provided certificate.
+LDAP_START_TLS=false
+LDAP_THUMBNAIL_ATTRIBUTE=thumbnailPhoto
 ```
 
 ### LDAP Group Sync
@@ -81,9 +105,9 @@ LDAP_ID_ATTRIBUTE=BIN;objectGUID
 BookStack has the ability to sync LDAP user groups with BookStack roles. By default this will match LDAP group names with the BookStack role display names with casing ignored.
 This can be overridden by via the 'External Authentication IDs' field which can be seen when editing a role while LDAP authentication is enabled. This field can be populated with common names (CNs) of accounts *or* groups. If filled, CNs in this field will be used and the role name will be ignored. You can match on multiple CNs by separating them with a comma.
 
-When matching LDAP groups with role names or 'External Authentication IDs' values, BookStack will standardise the names of ldap groups to be lower-cased and spaces will be replaced with hypens. For example, to match a LDAP group named "United Kingdom" an 'External Authentication IDs' value of "united-kingdom" could be used.
+When matching LDAP groups with role names or 'External Authentication IDs' values, BookStack will standardise the names of ldap groups to be lower-cased and spaces will be replaced with hyphens. For example, to match a LDAP group named "United Kingdom" an 'External Authentication IDs' value of "united-kingdom" could be used.
 
-This feature requires the LDAP server to be able to provide user groups when queried. This is enabled by default on ActiveDirectory via the 'memberOf' attribute but other LDAP systems may need to be configured to enable such functionality. If using OpenLDAP you'll need to setup the memberof overlay.
+This feature requires the LDAP server to be able to provide user groups when queried. This is enabled by default on ActiveDirectory via the 'memberOf' attribute but other LDAP systems may need to be configured to enable such functionality. Be aware that the 'memberOf' attribute does not include the user's primary group. If using OpenLDAP you'll need to setup the memberof overlay.
 
 Here are the settings required to be added to your `.env` file to enable group syncing:
 
@@ -95,5 +119,11 @@ LDAP_USER_TO_GROUPS=true
 LDAP_GROUP_ATTRIBUTE="memberOf"
 
 # Remove users from roles that don't match LDAP groups.
+# Note: While this is enabled the "Default Registration Role", editable within the 
+# BookStack settings view, will be considered a matched role and assigned to the user.
 LDAP_REMOVE_FROM_GROUPS=false
+
+# If you need to debug the group details coming from your LDAP server, add the below and uncomment (remove the #).
+# Only set this option if debugging since it will block logins and potentially show private details.
+#LDAP_DUMP_USER_GROUPS=true
 ```
index 871072b33695a4f4a97869d93be1f436fa22a2db..37e1ea3cb5a39db1e8a20ba50e9dea0d62e28162 100644 (file)
@@ -11,7 +11,7 @@ Currently BookStack does not support multiple instances from one installation bu
 
 To start off you will need to create a database for each BookStack instance. You could share a database between instances using table prefixes but using separate databases allows you to handle them separately when it comes to other operations such as backing up.
 
-For the purposes of continuity in below examples this documentation will detail setting up two instances at the domains `admin-docs.example.com` and `user-docs.example.com`. Before proceeding, Ensure you have your domains set up for all instances you want to create.
+For the purposes of continuity in below examples this documentation will detail setting up two instances at the domains `admin-docs.example.com` and `user-docs.example.com`. Before proceeding, ensure you have your domains set up for all instances you want to create.
 
 Once you have created your databases find somewhere to install BookStack to. Create a folder for each installation. Here are the locations I will use:
 ```
@@ -19,7 +19,7 @@ Once you have created your databases find somewhere to install BookStack to. Cre
 /var/www/admin-docs/
 ```
 
-Follow the standard [installation instructions](/docs/admin/installation) for each instance, Starting by cloning BookStack into each folder you created above.
+Follow the standard [installation instructions](/docs/admin/installation) for each instance, starting by cloning BookStack into each folder you created above.
 
 Once you have setup an installation in each folder you will need to configure your webserver. Follow the instructions for your webserver below:
 
@@ -46,7 +46,7 @@ Virtual host configuration is kept in the `/etc/apache2/sites-available` folder.
 
 Change the `/var/www/user-docs` locations in the configuration above to the location you have installed BookStack at. Also change the `user-docs.example.com` domain to the domain you want this instance to be available at. It should be noted that the above configuration enabled `.htaccess` files so be careful on what you add to the `public/.htaccess` file within you BookStack install.
 
-Once created, You will need to enable each site with the command `sudo a2ensite <config-file-name>`. For example: `sudo a2ensite user-docs.conf`. This simply creates a symbolic link to the configuration file in the `/etc/apache/sites-enabled/` folder which is where apache actually reads from.
+Once created, you will need to enable each site with the command `sudo a2ensite <config-file-name>`. For example: `sudo a2ensite user-docs.conf`. This simply creates a symbolic link to the configuration file in the `/etc/apache/sites-enabled/` folder which is where apache actually reads from.
 
 Once this is done restart apache to reload the configuration. `sudo service apache2 restart`. Both instances should now be accessible at the domains you set up.
 
diff --git a/content/docs/admin/oidc-auth.md b/content/docs/admin/oidc-auth.md
new file mode 100644 (file)
index 0000000..4b4150c
--- /dev/null
@@ -0,0 +1,116 @@
++++
+title = "OpenID Connect Authentication"
+description = "How to use an OpenID Connect identity provider as your primary way to access BookStack"
+date = "2021-10-21"
+type = "admin-doc"
++++
+
+OpenID Connect (OIDC) can be used within BookStack as a primary method of authentication.
+This replaces the default email & password authentication mechanism.
+BookStack supports a simple level of auto-discovery to ease endpoint and key management.
+
+When used, BookStack will attempt to match the OIDC user to an existing BookStack user
+based on the "External Authentication ID" value stored against the Bookstack user. 
+If this match cannot be made, BookStack will effectively auto-register that user to 
+provide a seamless access experience. They will be given the default role set under the
+"Default user role after registration" option in the application settings. 
+
+### Requirements & Limitations
+
+Listed below are some considerations to keep in mind in regard to BookStack's OIDC implementation:
+
+- Only RS256 is currently supported as a token signing algorithm, Token encryption is not supported.
+- Discovery covers fetching the auth & token endpoints, in addition to parsing any keys at the JWKS URI,
+  from the `<issuer>/.well-known/openid-configuration` endpoint.
+  - Issuer discovery is not supported.
+- Group/Role handling is not currently supported but feedback, in the form of IDP-provided group examples, is welcome to help future implementation.
+
+### BookStack Configuration
+
+To set up OIDC based authentication add or modify the following variables in your `.env` file:
+
+```bash
+# Set OIDC to be the authentication method
+AUTH_METHOD=oidc
+
+# Set the display name to be shown on the login button.
+# (Login with <name>)
+OIDC_NAME=SSO
+
+# Name of the claims(s) to use for the user's display name.
+# Can have multiple attributes listed, separated with a '|' in which 
+# case those values will be joined with a space.
+# Example: OIDC_DISPLAY_NAME_CLAIMS=given_name|family_name
+OIDC_DISPLAY_NAME_CLAIMS=name
+
+# OAuth Client ID to access the identity provider
+OIDC_CLIENT_ID=abc123
+
+# OAuth Client Secret to access the identity provider
+OIDC_CLIENT_SECRET=def456
+
+# Issuer URL
+# Must start with 'https://'
+OIDC_ISSUER=https://instance.authsystem.example.com
+
+# Enable auto-discovery of endpoints and token keys.
+# As per the standard, expects the service to serve a 
+# `<issuer>/.well-known/openid-configuration` endpoint.
+OIDC_ISSUER_DISCOVER=true
+
+############################################################
+## NOTE: The below are only needed if not using the above ##
+##       auto-discovery option.                           ##
+############################################################
+
+# Path to identity provider token signing public RSA key
+OIDC_PUBLIC_KEY=file:///keys/idp-public-key.pem
+
+# Full URL to the OIDC authorize endpoint
+OIDC_AUTH_ENDPOINT=https://instance.authsystem.example.com/v1/authorize
+
+# Full URL to the OIDC token endpoint
+OIDC_TOKEN_ENDPOINT=https://instance.authsystem.example.com/v1/token
+```
+
+A user in BookStack will be linked to an OIDC provided account via the `sub` claim.
+If the value of this ID changes in the identity provider it can be updated in BookStack, 
+by an admin, by changing the "External Authentication ID" field on the user's profile.
+
+### Callback URL
+
+Should your OIDC provider require a callback URL, the following can be used: `https://example.com/oidc/callback`.
+Change `https://example.com` to be the base URL of your BookStack instance.
+
+### Switching to OIDC with Existing Users
+
+When switching `AUTH_METHOD` from `standard` to `oidc`, BookStack will not 
+link OIDC user accounts to existing BookStack users, where the email address is 
+matching, since the "External Authentication ID" value of the existing BookStack user does 
+not match the unique user ID provided by the OIDC system.
+
+You can overcome this situation by logging into BookStack with an admin account while `AUTH_METHOD=standard`.
+While logged in, change `AUTH_METHOD` to `oidc`.
+This change of authentication method will show an "External Authentication ID" text
+field, below the name and email inputs, when viewing a user account in BookStack.
+Here you can enter the unique user ID that would be provided by your OIDC provider.
+Once saved BookStack will then use this value to match OIDC and BookStack user 
+accounts upon next login attempt.
+
+If you need to update accounts in bulk, you could instead directly update the 
+`external_auth_id` field of the `users` table within your BookStack database.
+
+### Debugging
+
+To help when setting up or configuring BookStack to use your OIDC system, the below
+`.env` option can help provide more insight:
+
+```bash
+# Dump out the details fetched from the identity provider.
+# Only set this option to true if debugging since it will block logins
+# and potentially show private details.
+OIDC_DUMP_USER_DETAILS=false
+```
+
+Further to this, details of any BookStack errors encountered can be found by following
+our [general debugging documentation](/docs/admin/debugging/).
index 5e8da280848a6e8c017ba1ed34230ea3f811f1ff..db373e6e5e8f59c84b63eb4d9116f92dc4613045 100644 (file)
@@ -9,17 +9,17 @@ type = "admin-doc"
 * [.env Options](#env-options)
 * [Revision Limit](#revision-limit)
 * [Custom User Avatar Fetching](#custom-user-avatar-fetching)
-* [Custom Draw.io URL](#custom-drawio-url)
+* [Custom diagrams.net URL](#custom-diagramsnet-url)
 
 ---
 
 ### .env Options
 
 As part of the installation of BookStack you will have a `.env` file containing system options. By default this only contains a few options.
-Within your BookStack install directory you should also have a `.env.example.complete` file which contains every support option available alongside the default value for each.
+Within your BookStack install directory you should also have a `.env.example.complete` file which contains every supported option available alongside the default value for each.
 You can copy options in this file to your own `.env` file as required. Many of the options in the `.env.example.complete` file are detailed in-depth in this documentation.
 
-The `.env` file essential sets environment variables for BookStack to read. Environment variables will be checked if an option is not in the `.env` file which can be useful
+The `.env` file essentially sets environment variables for BookStack to read. Environment variables will be checked if an option is not in the `.env` file which can be useful
 in the creation and use of docker containers.
 
 ---
@@ -62,23 +62,23 @@ The following variables can be used in this setting which will be populated by B
 
 ---
 
-### Custom Draw.io URL
+### Custom diagrams.net URL
 
-BookStack uses [draw.io](https://www.diagrams.net/) to provide users with the ability to create & edit drawings.
-By default BookStack embeds the draw.io interface using the following URL:
+BookStack uses [diagrams.net](https://www.diagrams.net/) (formerly draw.io) to provide users with the ability to create & edit drawings.
+By default BookStack embeds the diagrams.net interface using the following URL:
 
 ```php
-https://www.draw.io/?embed=1&proto=json&spin=1
+https://embed.diagrams.net/?embed=1&proto=json&spin=1&configure=1
 ```
 
 You can instead define your own URL to customise this embed or even use a self-hosted
-instance of draw.io. This can be done by defining an option in your `.env` file like so:
+instance of diagrams.net. This can be done by defining an option in your `.env` file like so:
 
 ```bash
-DRAWIO=https://drawing.example.com/?embed=1&proto=json&spin=1
+DRAWIO=https://drawing.example.com/?embed=1&proto=json&spin=1&configure=1
 ```
 
-**The `embed=1&proto=json&spin=1` query string parameters are required for the integration with BookStack to function. <br> Remember to include these when defining a custom URL.**
+**The `embed=1&proto=json&spin=1` query string parameters are required for the integration with BookStack to function. Remember to include these when defining a custom URL.**
 
-Refer to this draw.io guide to see what options are supported: [draw.io embed URL parameters](https://desk.draw.io/support/solutions/articles/16000042546-what-url-parameters-are-supported-). In particular, the `stealth=1` option might be of interest if you 
-don't want other external services to be used. 
\ No newline at end of file
+Refer to this diagrams.net guide to see what options are supported: [diagrams.net embed URL parameters](https://www.diagrams.net/doc/faq/supported-url-parameters). In particular, the `stealth=1` option might be of interest if you 
+don't want other external services to be used. 
index c21b08ac3d225bf7cc045d2266ba9374c94b6bc3..8bc22a86299ca534c1b9ce48e58af3ca19cc720a 100644 (file)
@@ -1,21 +1,43 @@
 +++
 title = "PDF Rendering"
-description = "Using WKHTMLtoPDF to generate PDF's for better rendering"
+description = "Options to configure PDF rendering within BookStack"
 date = "2017-01-22"
 type = "admin-doc"
 +++
 
-By default BookStack uses [Dompdf](https://github.com/dompdf/dompdf) to export pages as PDF documents. The benefit of using DomPDF is that it doesn't require any additional installation or setup but the rendering capabilities are somewhat limited.
+By default BookStack uses [dompdf](https://github.com/dompdf/dompdf) to export pages as PDF documents. The benefit of using dompdf is that it doesn't require any additional installation or setup but the rendering capabilities are somewhat limited.
 
-As an alternative you can use [wkhtmltopdf](http://wkhtmltopdf.org/) to generate PDF documents instead. wkhtmltopdf uses the Qt WebKit rendering engine to provide a more accurate overall result.
+As an alternative you can use [wkhtmltopdf](http://wkhtmltopdf.org/) to generate PDF documents instead. wkhtmltopdf uses the Qt WebKit rendering engine to provide a more accurate overall result, but comes with additional security concerns to be aware of.
 
 ### Using wkhtmltopdf
 
-Pre-compiled binaries for wkhtmltopdf can be found on the downloads page of [their website](http://wkhtmltopdf.org/downloads.html). BookStack will check for a file named `wkhtmltopdf` at the base folder of a BookStack install. If found it will use that to render PDF's. If that does not exist it will check for a `WKHTMLTOPDF` variable in the `.env` file. You can use this variable to set an alternate location to wkhtmltopdf:
+Pre-compiled binaries for wkhtmltopdf can be found on the downloads page of [their website](http://wkhtmltopdf.org/downloads.html).
+BookStack will check for a file named `wkhtmltopdf` at the base folder of a BookStack install. If found it will use that to render PDF exports. 
+If that does not exist it will check for a `WKHTMLTOPDF` variable in the `.env` file. 
+You can use the below variable in your `.env` file to set an alternate location to wkhtmltopdf:
 
 ```bash
-# In .env file
 WKHTMLTOPDF=/home/user/bins/wkhtmltopdf
 ```
 
-If neither of those exist Dompdf will be used instead.
+If neither of those exist, or if the below mentioned security option is not enabled, the default dompdf renderer will be used instead.
+
+**Note:** As of BookStack v21.08 the `ALLOW_UNTRUSTED_SERVER_FETCHING` must also be set to `true` for wkhtmltopdf to be enabled, without this dompdf will be used instead. 
+This change was made for security since, in many cases, wkhtmltopdf will perform fetches to external URLs which may be defined by users.
+You should only enable the below option in environments where users & visitors are trusted.
+
+```bash
+ALLOW_UNTRUSTED_SERVER_FETCHING=true
+```
+
+[See our security page for more detail regarding this option](/docs/admin/security/#server-side-requests).
+
+### Export Page Size
+
+By default PDF exports are generated at an A4 size. If you'd prefer exports to be generated at "US Letter" standard sizes
+you can specify this within your `.env` like so:
+
+```bash
+# US Letter
+EXPORT_PAGE_SIZE=letter
+```
\ No newline at end of file
index 43436ffcd29d3d0d9d09a4daa76f116924484830..75f2cec1fab889127bdb6e9f21861814a66f4735 100644 (file)
@@ -7,6 +7,8 @@ type = "admin-doc"
 
 BookStack can be configured to utilise a SAML 2.0 based authentication provider as a solution for users to log-in, log-out and self-register within BookStack. This replaces the default email & password authentication mechanism within BookStack. When enabled, BookStack will attempt to match the SAML user to an existing BookStack user based on a stored external id attribute otherwise, if not found, BookStack will effectively auto-register that user to provide a seamless access experience.
 
+[A video guide for setting up SAML2 can be found here](https://www.youtube.com/watch?v=szweYsAow88).
+
 ### BookStack Configuration
 
 To set up SAML 2.0 based authentication add or modify the following variables in your `.env` file:
@@ -26,7 +28,7 @@ SAML2_EMAIL_ATTRIBUTE=email
 SAML2_EXTERNAL_ID_ATTRIBUTE=uid
 
 # Name of the attribute(s) to use for the user's display name
-# Can have mulitple attributes listed, separated with a '|' in which 
+# Can have multiple attributes listed, separated with a '|' in which 
 # case those values will be joined with a space.
 # Example: SAML2_DISPLAY_NAME_ATTRIBUTES=firstName|lastName
 # Defaults to the ID value if not found.
@@ -35,7 +37,7 @@ SAML2_DISPLAY_NAME_ATTRIBUTES=username
 # Identity Provider entityID URL
 SAML2_IDP_ENTITYID=https://example.com/saml2/idp/metadata.php
 
-# Auto-load metatadata from the IDP
+# Auto-load metadata from the IDP
 # Setting this to true negates the need to specify the next three options
 SAML2_AUTOLOAD_METADATA=false
 
@@ -51,6 +53,21 @@ SAML2_IDP_SLO=https://example.com/saml2/idp/SingleLogoutService.php
 # Identity Provider x509 public certificate data.
 # Not required if using the autoload option above.
 SAML2_IDP_x509=<cert_data>
+
+# Identity Provider AuthnContext
+# Setting this to true (The default) and exact AuthnContext of 
+# 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' will be used.
+# Setting this to false will provide no AuthnContext to the IDP.
+# Alternatively you can set this to a space separated list of values. For example:
+# SAML2_IDP_AUTHNCONTEXT="urn:oasis:names:tc:SAML:2.0:ac:classes:Password urn:federation:authentication:windows"
+SAML2_IDP_AUTHNCONTEXT=true
+
+# Service Provider Certificate & Key (Optional)
+# Providing these will provide key data within BookStack's metadata endpoint
+# while implicitly enabling signing on Authn and Logout requests.
+# This is often required to support Single Logout Service in an ADFS environment.
+SAML2_SP_x509=<cert_data>
+SAML2_SP_x509_KEY=<key_data>
 ```
 
 A user in BookStack will be linked to a SAML user via the `SAML2_EXTERNAL_ID_ATTRIBUTE`. If the value of this id changes in the identity provider it can be updated in BookStack by an admin by changing the 'External Authentication ID' field on the user's profile.
@@ -116,6 +133,8 @@ SAML2_USER_TO_GROUPS=true
 # Set the attribute from which BookStack will read groups names from.
 SAML2_GROUP_ATTRIBUTE=groups
 
-# Removed user from roles that don't match SAML groups upon login.
+# Remove the user from roles that don't match SAML groups upon login.
+# Note: While this is enabled the "Default Registration Role", editable within the 
+# BookStack settings view, will be considered a matched role and assigned to the user.
 SAML2_REMOVE_FROM_GROUPS=true
 ```
index 00b3a5071b147e3a3fcb85901b4d87c780e8affb..ce4b172b30b86d87130ed80749e56ba25876f38e 100644 (file)
@@ -9,14 +9,24 @@ Since BookStack can hold important information for users you should be aware of
 Read through the below to ensure you have secured your BookStack instance. Note, The below only
 relates to BookStack itself. The security of the server BookStack is hosted on is not instructed below but should be taken into account.
 
+If you'd like to be notified of new potential security concerns you can sign-up to the [BookStack security mailing list](https://updates.bookstackapp.com/signup/bookstack-security-updates). For reporting security vulnerabilities, please see the ["Security" section](https://github.com/BookStackApp/BookStack/blob/development/readme.md#-security) of the project readme on GitHub.
+
+
 <ul>
     <li><a href="#initial-security-setup">Initial Security Setup</a></li>
+    <li><a href="#mfa">Multi-Factor Authentication</a></li>
     <li><a href="#securing-images">Securing Images</a></li>
     <li><a href="#attachments">Attachments</a></li>
     <li><a href="#user-passwords">User Passwords</a></li>
     <li><a href="#javascript-in-page-content">JavaScript in Page Content</a></li>
     <li><a href="#web-crawler-control">Web Crawler Control</a></li>
     <li><a href="#secure-cookies">Secure Cookies</a></li>
+    <li><a href="#iframe-control">Host Iframe Control</a></li>
+    <li><a href="#iframe-src-control">Iframe Source Control</a></li>
+    <li><a href="#failed-access-logging">Failed Access Logging</a></li>
+    <li><a href="#server-side-requests">Untrusted Server Side Requests</a></li>
+    <li><a href="#csp">Content Security Policy (CSP)</a></li>
+    <li><a href="#mysql-ssl-connection">MySQL SSL connection</a></li>
 </ul>
 
 ---
@@ -31,9 +41,33 @@ opens up a lot of code that does not need to be public. Triple check this if you
 BookStack within the commonly used `/var/www` folder.
 3. Ensure the database user you've used for BookStack has limited permissions for only accessing
 the database used for BookStack data.
-4. Within BookStack, go through the settings to ensure registration and public access settings are as you expect.
-5. Review the user roles in the settings area.
-6. Read the below to further understand the security for images & attachments.
+4. Check that you've set the `APP_URL` option in your `.env` file so that system generated URLs cannot be manipulated.
+5. Within BookStack, go through the settings to ensure registration and public access settings are as you expect.
+6. Review the user roles in the settings area.
+7. Read the below to further understand the security for images & attachments.
+
+---
+
+<a name="mfa"></a>
+
+### Multi-Factor Authentication
+
+Any user can enable multi-factor authentication (MFA) on their account. Upon login they would then need to use an extra proof of identity
+to gain access. BookStack currently supports the following mechanisms:
+
+- TOTP (Time-based One-Time Passwords)
+  - Labelled as "Mobile App" (Google/Microsoft Authenticator etc...).
+  - Uses a SHA1 algorithm internally (Greater algorithms have poor cross-app compatibility).
+- Backup Codes
+  - These are a list of 16 one-time-use codes.
+  - Users will be warned once they have less than 5 codes remaining.
+
+Secrets and values for these options are stored encrypted within the database.
+
+Where required, MFA can be forced upon users via their roles. This can be found via
+a "Requires Multi-Factor Authentication" checkbox seen when editing a role.
+If a user does not already have an MFA method configured, they will be forced to set one up
+upon next login.
 
 ---
 
@@ -109,7 +143,9 @@ These are hashed using the standard Laravel hashing methods which use the Bcrypt
 
 ### JavaScript in Page Content
 
-By default, JavaScript tags within page content is escaped when rendered. This can be turned off by setting `ALLOW_CONTENT_SCRIPTS=true` in your `.env` file. Note that even if you disable this escaping the WYSIWYG editor may still perform it's own JavaScript escaping.
+By default, JavaScript tags within page content is escaped when rendered. This can be turned off by setting `ALLOW_CONTENT_SCRIPTS=true` in your `.env` file. Note that even if you disable this escaping the WYSIWYG editor may still perform it's own JavaScript escaping. This option will also alter the [CSP rules](#csp) set by BookStack.
+
+***This option disables some fundemental cross-site-scripting protections. Only use this option on secure instances, where only very trusted users can edit content***
 
 ---
 
@@ -125,4 +161,134 @@ The rules found in the `/robots.txt` file are automatically controlled via the "
 
 ### Secure Cookies
 
-BookStack uses cookies to track sessions, remember logins and for XSRF protection. When using HTTPS you may want to ensure that cookies are only sent back to the browser if the connection is over HTTPS. This can be enabled by setting `SESSION_SECURE_COOKIE=true` in your `.env` file.
\ No newline at end of file
+BookStack uses cookies to track sessions, remember logins and for XSRF protection. When using HTTPS you may want to ensure that cookies are only sent back to the browser if the connection is over HTTPS. If you have set a https `APP_URL` option in your `.env` this will enabled automatically but it can also be forced on by setting `SESSION_SECURE_COOKIE=true` in your `.env` file.
+
+---
+
+<a name="iframe-control"></a>
+
+### Host Iframe Control
+
+By default BookStack will only allow itself to be embedded within iframes on the same domain as you're hosting on. This is done through a [CSP: frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) header. You can add additional trusted hosts by setting a `ALLOWED_IFRAME_HOSTS` option in your `.env` file like the example below:
+
+```bash
+# Adding a single host
+ALLOWED_IFRAME_HOSTS="https://example.com"
+
+# Multiple hosts can be separated with a space
+ALLOWED_IFRAME_HOSTS="https://a.example.com https://b.example.com"
+```
+
+Note: when this option is used, all cookies will served with `SameSite=None` [(info)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#None) set so that
+a user session can persist within the iframe.
+
+---
+
+<a name="iframe-src-control"></a>
+
+### Iframe Source Control
+
+By default BookStack will only allow certain other hosts to be used as `src` values for embededd iframe/frame content within the application. This is done through a [CSP: frame-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src) header. You can configure the list of trusted sources by setting a `ALLOWED_IFRAME_SOURCES` option in your `.env` file like the examples below:
+
+```bash
+# Adding a single host
+ALLOWED_IFRAME_SOURCES="https://example.com"
+
+# Multiple hosts can be separated with a space
+ALLOWED_IFRAME_SOURCES="https://a.example.com https://b.example.com"
+
+# Allow all sources
+# This opens vulnerability risk and should only be done in secure & trusted environments.
+ALLOWED_IFRAME_SOURCES="*"
+```
+
+By default this option is configured as follows:
+
+```bash
+ALLOWED_IFRAME_SOURCES="https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com"
+```
+
+Note: The source of 'self' will always be automatically added to this CSP rule. In addition, the host used for the diagrams.net integration (If enabled) will be automatically appended to the lists of hosts.
+
+---
+
+<a name="failed-access-logging"></a>
+
+### Failed Access Logging
+
+An option is available to log failed login events to a log file which is useful to identify users having trouble logging in, track malicious login attempts or to use with tools such as Fail2Ban. This works with login attempts using the default email & password login mechanism or attempts via LDAP login. Failed attempts are **not logged** for "one-click" social or SAML2 options.
+
+To enable this you simply need to define the `LOG_FAILED_LOGIN_MESSAGE` option in your `.env` file like so:
+
+```bash
+LOG_FAILED_LOGIN_MESSAGE="Failed login for %u"
+```
+
+The optional "%u" element of the message will be replaced with the username or email provided in the login attempt
+when the message is logged. By default messages will be logged via the php `error_log` function which, in most
+cases, will log to your webserver error log files.
+
+---
+
+<a name="server-side-requests"></a>
+
+### Untrusted Server Side Requests
+
+Some features, such as the PDF exporting, have the option to make http calls to external user-defined locations to do things
+such as load images or styles. This is disabled by default but can be enabled if desired. This is required for using 
+WKHTMLtoPDF as your PDF export renderer.
+This should only be enabled in BookStack environments where BookStack users and viewers are fully trusted.
+
+To enable untrusted server side requests, you need to define the `ALLOW_UNTRUSTED_SERVER_FETCHING` option in your `.env` file like so:
+
+```bash
+ALLOW_UNTRUSTED_SERVER_FETCHING=true
+```
+
+---
+
+<a name="csp"></a>
+
+### Content Security Policy (CSP)
+
+BookStack serves responses with a CSP header to increase protection again malicious content.
+This is especially important in a system such as BookStack where users can create a variety of HTML content, 
+especially so if you allow untrusted users to create content in your instance.
+The CSP rules set by BookStack are as follows:
+
+- `frame-ancestors 'self'`
+  - Restricts what websites can embed BookStack pages via iframes.
+  - See the "[Host Iframe Control](#iframe-control)" section above for details on expanding this rule to other hosts.
+- `frame-source 'self' https://*.diagrams.net https://*.draw.io https://*.youtube.com https://*.youtube-nocookie.com https://*.vimeo.com https://embed.diagrams.net`
+  - Restricts what sources are allowed to load for frames/iframes.
+  - Can be configured via a `ALLOWED_IFRAME_SOURCES` .env option.
+  - May be different depending on other configuration set.
+- `script-src http: https: 'nonce-abc123' 'strict-dynamic'`
+  - Restricts what scripts can be ran on a BookStack-served page.
+  - Will not be set if the `ALLOW_CONTENT_SCRIPTS` .env option is active.
+  - The nonce value used is randomly generated upon each request. It is automatically applied to any "Custom HTML Head Content" scripts.
+- `object-src 'self'`
+  - Restricts which embeddable content can be loaded onto a BookStack-served page.
+  - Will not be set if the `ALLOW_CONTENT_SCRIPTS` .env option is active.
+- `base-uri 'self'`
+  - Restricts what `<base>` tags can be added to a BookStack-served page.
+
+If needed you should be able to set additional CSP headers via your webserver.
+If there's a clash with an existing BookStack CSP header then browsers will generally favour the most restrictive policy.
+
+---
+
+<a name="mysql-ssl-connection"></a>
+
+### MySQL SSL Connection
+
+If your BookStack database is not on the same host as your web server, you may want to ensure the connection is encrypted using SSL between these systems.
+Assuming SSL is configured correctly on your MySQL server, you can enable this by defining the `MYSQL_ATTR_SSL_CA` option in your `.env` file like so:
+
+```bash
+# Path to Certificate Authority (CA) certificate file for your MySQL instance.
+# When this option is used host name identity verification will be performed
+# which checks the hostname, used by the client, against names within the
+# certificate itself (Common Name or Subject Alternative Name).
+MYSQL_ATTR_SSL_CA="/path/to/ca.pem"
+```
\ No newline at end of file
index 52a785f1d1d747db2c11281615c85c855467be61..cd5afc305cff21e7a8fb7466dc909255e7e805e4 100644 (file)
@@ -5,11 +5,27 @@ date = "2018-10-04"
 type = "admin-doc"
 +++
 
-You may want to host BookStack on a "Subdirectory" of your website, For example `https://example.com/bookstack`. To achieve this you will need to make some alterations to your webserver config. The details for setting this up on Apache can be found below. You'll need to follow the BookStack setup section after configuring any webserver.
+You may want to host BookStack on a "Subdirectory" of your website, For example `https://example.com/bookstack`. To achieve this you will need to make some alterations to your webserver config. The details for setting this up on Apache or Nginx can be found below. You'll need to follow the BookStack setup section after configuring any webserver.
 
 If you are using Docker you will likely need to look into setting up reverse proxies instead of following the below.
 
-## Apache Setup
+- [BookStack Setup](#bookstack-setup)
+- [Apache Setup](#apache-setup)
+- [Nginx Setup](#nginx-setup)
+
+---
+
+### BookStack Setup
+
+Within your `.env` file ensure you set the `APP_URL` parameter. This should be the base URL for your BookStack instance without a trailing slash. For example:
+
+```bash
+APP_URL=https://example.com/bookstack
+```
+
+---
+
+### Apache Setup
 
 Before following this, ensure you have apache installed along with PHP & ensure mod-php is enabled. This guide assumes a recent Ubuntu-like system is in use. To set-up the required rules, you will need to have mod-rewrite enabled:
 
@@ -17,7 +33,7 @@ Before following this, ensure you have apache installed along with PHP & ensure
 sudo a2enmod rewrite
 ``` 
 
-First, You will need to choose a folder to install BookStack into. This should a separate directory from where your main website is being served from since you don't want to risk exposing any of the private BookStack files.
+First, You will need to choose a folder to install BookStack into. This should be a separate directory from where your main website is being served from since you don't want to risk exposing any of the private BookStack files.
 By default Apache on Ubuntu serves from the `/var/www/html` directory. In this example, we'll use `/var/www/bookstack` to store our BookStack install. If you use a different path ensure you change that path in the below steps.
 Create this directory and follow the standard [BookStack install steps](/docs/admin/installation) to install BookStack into this folder. Once complete, following our example directory above, you should end up with a `.env` file in the `/var/www/bookstack` folder.
 
@@ -63,19 +79,71 @@ Within the `<VirtualHost>` tags of this file you'll need to add the below additi
 
 On line 6 in the above, beginning with `Alias`, You'll need to change `"/bookstack"` path to be the web 'subdirectory' you want to serve BookStack on. For example, If you wanted to serve BookStack on `https://example.com/docs` this would be `"/docs"`. Any instances of `/var/www/bookstack` in the above will need to be changed to the folder you installed BookStack in. The `/public` part of these paths should remain.
 
-Once the configuration has been updated, you'll need to restart apache. On Ubuntu you can do with the following command:
+Once the configuration has been updated, you'll need to restart apache. On Ubuntu you can do this with the following command:
 
 ```bash
 sudo systemctl restart apache2.service
 ```
 
-Follow the below "BookStack Setup" to add your new URL to your BookStack configuration. Once done you should be able to access your BookStack instance at your desired sub-path.
+Follow the above [BookStack Setup](#bookstack-setup) to add your new URL to your BookStack configuration. Once done you should be able to access your BookStack instance at your desired sub-path.
 
+---
 
-## BookStack Setup
+### Nginx Setup
 
-Within your `.env` file ensure you set the `APP_URL` parameter. This should be the base URL for your BookStack instance without a trailing slash. For example:
+Before following this, ensure you have Nginx installed along with php-fpm. This guide assumes a recent Ubuntu-like system is in use. You may need to alter steps to suit other operating systems.
+There are multiple ways to achieve this approach with Nginx. The below uses multiple Nginx server blocks and proxying to achieve sub-path serving which keeps the 
+BookStack server configuration contained.
+
+First, you will need to choose a folder to install BookStack into. This should be a separate directory from where your main website is being served from since you don't want to risk exposing any of the private BookStack files. Do not install BookStack to a child directory of any other website's web root.
+
+By default Nginx on Ubuntu serves from the `/var/www/html` directory. In this example, we'll use `/var/www/bookstack` to store our BookStack install. If you use a different path ensure you change that path in the below steps.
+Create this directory and follow the standard [BookStack install steps](/docs/admin/installation) to install BookStack into this folder. Once complete, following our example directory above, you should end up with a `.env` file in the `/var/www/bookstack` folder.
+
+The next step is to alter your Nginx configuration to serve any requests to your sub-path from our chosen folder. To do this you'll need to find and edit the Nginx config for your website. By default, this is often found at `/etc/nginx/sites-available/default`. To edit this file you'll likely have to open it with admin permissions (using `sudo`). 
+
+Within your existing config file, or within a new one, add a new server block as per the below:
+
+```nginx
+server {
+  listen 8080;
+  listen [::]:8080;
+
+  server_name localhost;
+
+  root /var/www/bookstack/public;
+  index index.php index.html;
+
+  location / {
+    try_files $uri $uri/ /index.php?$query_string;
+  }
+  
+  location ~ \.php$ {
+    include snippets/fastcgi-php.conf;
+    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
+  }
+}
+```
+
+This server block will host BookStack at `http://localhost:8080`. The port and server name used here are intentional since this is only intended to be directly used locally.
+Next, locate the `server {` block for your existing website. Within this block add the following location block:
+
+```nginx
+location /bookstack/ {
+  proxy_pass http://localhost:8080/;
+  proxy_redirect off;
+}
+```
+
+Tweak the `/bookstack/` part to match the path you want to serve BookStack on. The slashes used within both the `location` and `proxy_pass` lines are important to functionality.
+This block will tell Nginx to handle requests to `/bookstack/` by proxying them to our previously created BookStack `server {` block.
+
+A full [example of this configuration can be seen here](https://github.com/BookStackApp/devops/blob/main/config/nginx/subpath-proxy-config).
+
+Once done save your config files. You can often test your Nginx config is valid by running `sudo nginx -t`. If valid restart Nginx. On Ubuntu this can be done with the following command:
 
 ```bash
-APP_URL=https://example.com/bookstack
-```
\ No newline at end of file
+sudo systemctl restart nginx.service
+```
+
+Follow the above [BookStack Setup](#bookstack-setup) to add your new URL to your BookStack configuration. Once done you should be able to access your BookStack instance at your desired sub-path.
\ No newline at end of file
index e94c75f53f1cc60287cc9901aa151ac6d3f317cc..93cb14697ba4fb03f5841c4bf1f196d81d2f3a95 100644 (file)
@@ -35,7 +35,8 @@ GOOGLE_AUTO_REGISTER=true
 TWITCH_AUTO_REGISTER=true
 ```
 
-This will allow registration using these services even if registrations are disabled. It also allows registration if using LDAP as you main authentication option.
+This will allow registration using these services even if registrations are disabled.
+It also allows registration if using LDAP as your main authentication option.
 
 #### Automatic Email Confirmation
 
@@ -61,14 +62,14 @@ TWITCH_AUTO_CONFIRM_EMAIL=true
     - `https://example.com/login/service/google/callback`
     - `https://example.com/register/service/google/callback`
 6. Add or set the following items in your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your Google API_KEY and API_SECRET
-       GOOGLE_APP_ID={google_app_id}
-       GOOGLE_APP_SECRET={google_app_secret}
-
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
+```bash
+# Replace the below (including '{}' braces) with your Google API_KEY and API_SECRET
+GOOGLE_APP_ID={google_app_id}
+GOOGLE_APP_SECRET={google_app_secret}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 7. All done! Users should now be able to link their social accounts in their account profile pages and also register/login using their Google accounts.
 
 ---
@@ -86,23 +87,27 @@ TWITCH_AUTO_CONFIRM_EMAIL=true
 
 ### Twitter
 
-To create a Twitter application for signing in with you may require a phone number linked to your Twitter account.
+Before creating a Twitter application for signing in, you will need to have signed up and be approved on the [Twitter Developer](https://developer.twitter.com/) site. Part of this will require describing your use of the API. 
 
-1. Go to your [Twitter apps page](https://apps.twitter.com/) and click 'Create New App'.
-2. Enter an application name and description. The website and callback URL can just be your BookStack homepage urls. Then submit the form.
-3. Click into your new application and go the the settings tab and ensure 'Allow this application to be used to Sign in with Twitter' is checked.
-4. If you'd like, set an icon and change any other details.
-5. Click the 'Permissions' tab and in the 'Additional Permissions' section check the box 'Request email addresses from users' then save.
-6. Go to the 'Keys and Access Tokens' tab to find your API key and secret. Add or set these to your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your twitter API_KEY and API_SECRET
-       TWITTER_APP_ID={API_KEY}
-       TWITTER_APP_SECRET={API_SECRET}
+1. Go to your [Twitter Developer Portal](https://developer.twitter.com/en/portal/dashboard), after being approved by twitter as described above. Navigate to 'Projects and Apps' > 'Overview'  and under 'Standalone Apps' click 'Create App'.
+2. Enter an application name and save/continue to the next step.
+3. You'll now be shown some keys and tokens. Copy out the shown 'API key' and 'API secret key' values for the next step.
+4. Within your BookStack `.env` add in extra options for your token and secret like so:
+```bash
+# Replace the below (including '{}' braces) with your twitter API_KEY and API_SECRET
+TWITTER_APP_ID={API_KEY}
+TWITTER_APP_SECRET={API_SECRET}
 
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
-7. All done! Users should now be able to link their Twitter account in their account profile pages and also register/login using their Twitter account.
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
+5. Back within the Twitter developer dashboard, find your new standalone app and click on 'App Settings'  then click on edit within the 'Authentication settings' section.
+6. Enable the '3-legged OAuth' and 'Request email address from users' options.
+7. Enter the following URLs under 'Callback URLs', changing `https://example.com` to your own domain where BookStack is hosted:
+    - `https://example.com/login/service/twitter/callback`
+    - `https://example.com/register/service/twitter/callback`
+8. Fill in any remaining required URLs then click save.
+9. All done! Users should now be able to link their Twitter account in their account profile pages and also register/login using their Twitter account. You may want to review the other options for the Twitter Standalone app such as setting a logo and description.
 
 ---
 
@@ -117,14 +122,14 @@ To create a Twitter application for signing in with you may require a phone numb
     - `https://example.com/login/service/facebook/callback`
     - `https://example.com/register/service/facebook/callback`
 7. Navigate back to the app 'Dashboard' in the sidebar to find your app id and secret. Add or set these to your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your facebook APP_KEY and APP_SECRET
-       FACEBOOK_APP_ID={APP_KEY}
-       FACEBOOK_APP_SECRET={APP_SECRET}
-
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
+```bash
+# Replace the below (including '{}' braces) with your facebook APP_KEY and APP_SECRET
+FACEBOOK_APP_ID={APP_KEY}
+FACEBOOK_APP_SECRET={APP_SECRET}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 7. All done! Users should now be able to link their Facebook account in their account profile pages and also register/login using their Facebook account.
 
 ---
@@ -134,14 +139,14 @@ To create a Twitter application for signing in with you may require a phone numb
 1. Go to the [Slack apps page](https://api.slack.com/apps) and select 'Create New App'.
 2. Enter an app name ('BookStack login' or something custom), select your team then continue.
 3. You should see your client ID and secret. Copy these details and add them as new variables in your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your slack CLIENT_ID and CLIENT_SECRET
-       SLACK_APP_ID={CLIENT_ID}
-       SLACK_APP_SECRET={CLIENT_SECRET}
-
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
+```bash
+# Replace the below (including '{}' braces) with your slack CLIENT_ID and CLIENT_SECRET
+SLACK_APP_ID={CLIENT_ID}
+SLACK_APP_SECRET={CLIENT_SECRET}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 4. In your slack app go to 'OAuth & Permissions' and enter your BookStack base URL into the 'Redirect URL(s)' input then save.
 5. All done! Users should now be able to link their Slack account in their account profile pages and also register/login using their Slack account.
 
@@ -151,23 +156,23 @@ To create a Twitter application for signing in with you may require a phone numb
 
 1. Login to your your azure portal and navigate to the 'Azure Activity Directory' area.
 2. Under 'Manage > App registrations' select 'New application registration'.
-3. Enter a name ('BookStack'). Keep the 'Application type' as 'Web app / API'. Set the 'Sign-on URL' to be the following, replacing 'https://example.com/' with your base BookStack url: 
+3. Enter a name ('BookStack'). Set the 'Redirect URI' to be the following, replacing 'https://example.com/' with your base BookStack url: 
     - `https://example.com/login/service/azure/callback`
-4. Once created, Click on your new application and note the 'Application ID'. This is the APP_ID value for step 9.
-5. Within your application in azure, Navigate to 'Keys' under 'API access'.
-6. Enter any description you want and set a duration ('Never expires' is okay unless you specifically want it to expire). Then click 'Save'.
+4. Once created, View the application 'Overview' page and note the 'Application (client) ID' and 'Directory (tenant) ID' values. These are the APP_ID and TENANT values for step 9.
+5. Within your application in azure, Navigate to 'Certificates & secrets' then choose 'New client secret'.
+6. Enter any description you want and set an expiry duration. Then click 'Save'.
 7. Copy the string of characters under 'Value'. This is the APP_SECRET value for step 9 and is only shown once.
-8. Back under 'App registrations' click on 'Endpoints'. This will show a list of URL's that each contain a unique ID. For example, the OAuth2 token endpoint will look something like this: `https://login.microsoftonline.com/<UniqueID>/oauth2/token`. Copy out the unique ID. This is the TENANT value for step 9.
+8. Navigate to 'API permissions' for your app. You should already have a "Microsoft Graph" > "User.Read" permission assigned. If not choose 'Add a permission'. Find the 'Microsoft Graph' option within this, then select 'Delegated permissions' then find & select the 'User.Read' permission. Then select 'Add permissions' at the bottom of the page.
 9. Copy these details and add them as new variables in your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your azure APP_ID and APP_SECRET and TENANT
-       AZURE_APP_ID={APP_ID}
-       AZURE_APP_SECRET={APP_SECRET}
-       AZURE_TENANT={TENANT}
-
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
+```bash
+# Replace the below (including '{}' braces) with your azure APP_ID and APP_SECRET and TENANT
+AZURE_APP_ID={APP_ID}
+AZURE_APP_SECRET={APP_SECRET}
+AZURE_TENANT={TENANT}
+
+# APP_URL Needs to be set to your BookStack base url, likely already configured
+APP_URL=http://mybookstackurl.com
+```
 10. All done! Users should now be able to link their AzureAD account in their account profile pages and also register/login using their AzureAD account.
 
 ---
@@ -182,16 +187,16 @@ To create a Twitter application for signing in with you may require a phone numb
     - `https://example.com/register/service/okta/callback`
 5. Save and scroll down to the 'Client Credentials' area. Copy the 'Client ID' and 'Client secret' values. These are your 'APP_ID' and 'APP_SECRET' values for step 6.
 6. Copy these details and add them as new variables in your `.env` file like so:
-       ```bash
-       # Replace the below (including '{}' braces) with your okta APP_ID and APP_SECRET and BASE_URL.
-       OKTA_APP_ID={APP_ID}
-       OKTA_APP_SECRET={APP_SECRET}
-       # The base URL is the URL from step 1 but with everything after the domain (okta.com) removed.
-       OKTA_BASE_URL={BASE_URL}
-
-       # APP_URL Needs to be set to your BookStack base url
-       APP_URL=http://mybookstackurl.com
-       ```
+```bash
+# Replace the below (including '{}' braces) with your okta APP_ID and APP_SECRET and BASE_URL.
+OKTA_APP_ID={APP_ID}
+OKTA_APP_SECRET={APP_SECRET}
+# The base URL is the URL from step 1 but with everything after the domain (okta.com) removed.
+OKTA_BASE_URL={BASE_URL}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 7. All set up! Remember to assign the new application you created in Okta to your Okta users otherwise they will not be able to register/login using the service.
 
 ---
@@ -205,22 +210,22 @@ GitLab authentication works for both [gitlab.com](https://gitlab.com) and self-h
 3. Set a name to identify the application, such as 'BookStack Authentication', and in the 'Redirect URI' input add both of the below URLs, Changing `https://example.com` to the base URL of your BookStack instance:
     - `https://example.com/login/service/gitlab/callback`
     - `https://example.com/register/service/gitlab/callback`
-4. Do not select any of the 'Scopes' checkboxes.
+4. Select the checkbox for the `read_user` scope.
 5. Press 'Save application'. You will be shown the application ID and secret which you'll need for the next step.  
 6. Copy the below details and add them as new variables in your `.env` file like so:
-    ```bash
-    # Replace the below (including '{}' braces) with your GitLab Application Id and Secret values.
-    GITLAB_APP_ID={APP_ID}
-    GITLAB_APP_SECRET={APP_SECRET}
+```bash
+# Replace the below (including '{}' braces) with your GitLab Application Id and Secret values.
+GITLAB_APP_ID={APP_ID}
+GITLAB_APP_SECRET={APP_SECRET}
 
-    # APP_URL Needs to be set to your BookStack base url
-    APP_URL=http://mybookstackurl.com
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
 
 
-    # ONLY REQURED FOR SELF-HOSTED GITLAB INSTANCES - REMOVE FOR GITLAB.COM
-    # Set the below URI to match the base URI of your GitLab install
-    GITLAB_BASE_URI=http://my-custom-gitlab.example.com
-    ```
+# ONLY REQURED FOR SELF-HOSTED GITLAB INSTANCES - REMOVE FOR GITLAB.COM
+# The below needs to match the base URI of your GitLab install, including the trailing slash.
+GITLAB_BASE_URI=http://my-custom-gitlab.example.com/
+```
 7. All set up! Users will now be able to use GitLab to sign-in and register.
 
 ---
@@ -236,14 +241,14 @@ To allow twich sign-in you'll first need to create an application from the Twitc
 4. Under the 'Application Category' option select 'Website Integration' then hit 'Register'.
 5. Click the 'New Secret' button and accept the prompt that appears. You should now see both a 'Client ID' and 'Client Secret' value which you'll use in the next step.
 6. Copy the below details and add them as new variables in your `.env` file like so:
-    ```bash
-    # Replace the below (including '{}' braces) with your Twitch Application Id and Secret values.
-    TWITCH_APP_ID={APP_ID}
-    TWITCH_APP_SECRET={APP_SECRET}
-
-    # APP_URL Needs to be set to your BookStack base url
-    APP_URL=http://mybookstackurl.com
-    ```
+```bash
+# Replace the below (including '{}' braces) with your Twitch Application Id and Secret values.
+TWITCH_APP_ID={APP_ID}
+TWITCH_APP_SECRET={APP_SECRET}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 7. All set up! Users will now be able to use Twitch to sign-in and register.
 
 ---
@@ -259,12 +264,12 @@ To allow Discord sign-in you'll first need to create an application on the Disco
     - `https://example.com/login/service/discord/callback`
 5. Back in the 'General Information' section find the 'Client ID' and 'Client Secret' values which you'll use in the next step.
 6. Copy the below details and add them as new variables in your `.env` file like so:
-    ```bash
-    # Replace the below (including '{}' braces) with your Discord Application Id and Secret values.
-    DISCORD_APP_ID={APP_ID}
-    DISCORD_APP_SECRET={APP_SECRET}
-
-    # APP_URL Needs to be set to your BookStack base url
-    APP_URL=http://mybookstackurl.com
-    ```
+```bash
+# Replace the below (including '{}' braces) with your Discord Application Id and Secret values.
+DISCORD_APP_ID={APP_ID}
+DISCORD_APP_SECRET={APP_SECRET}
+
+# APP_URL Needs to be set to your BookStack base url
+APP_URL=http://mybookstackurl.com
+```
 7. All set up! Users will now be able to use Discord to sign-in and register.
index 358b3c7beef862bf03086bd31d3cde03dba96b67..fb9d6d183102e8e746d372bb0a68b8c9f77365d6 100644 (file)
@@ -1,27 +1,30 @@
 +++
 title = "Updating BookStack"
-description = "How to update BookStack to the lastest version"
+description = "How to update BookStack to the latest version"
 date = "2017-01-01"
 type = "admin-doc"
 +++
 
-BookStack is updated regularly and is still in beta although we do try to keep the platform and upgrade path as stable as possible. The latest release can be found on [GitHub here](https://github.com/BookStackApp/BookStack/releases) and detailed information on releases is posted on the [BookStack blog here](/tags/releases/).
+BookStack is updated regularly. We try our best to keep the platform and upgrade path as stable as possible. The latest release can be found on [GitHub here](https://github.com/BookStackApp/BookStack/releases) and detailed information on releases is posted on the [BookStack blog here](/tags/releases/).
 
 **Before updating you should back up the database and any file uploads to prevent potential data loss**. <br>
 Backup and restore documentation can be found [here](/docs/admin/backup-restore).
 
- Updating is currently done via Git version control. To update BookStack you can run the following command in the root directory of the application:
+ Updating is currently done via Git version control. To update BookStack you can run the three following commands in the root directory of the application:
 
 ```bash
-git pull origin release && composer install --no-dev && php artisan migrate
+git pull origin release
+composer install --no-dev
+php artisan migrate
 ```
 
-This command will update the repository that was created in the installation, install the PHP dependencies using `composer` then run then update the database with any required changes.
+This first command will update the repository that was created in the installation. The second will install the PHP dependencies using `composer`. The third will then update the database with any required changes.
 
-In addition, Clearing the cache is also recommended:
+In addition, Clearing the system caches is also recommended:
 
 ```bash
 php artisan cache:clear
+php artisan config:clear
 php artisan view:clear
 ```
 
@@ -31,6 +34,162 @@ Check the below list for the version you are updating to for any additional inst
 
 ## Version Specific Instructions
 
+The below lists things you may need to be aware of when upgrading to a newer version of BookStack. 
+This is primarily a list of breaking changes & security notices.
+Details of updates can be found on [our blog](https://www.bookstackapp.com/blog/) or via 
+the [GitHub releases page](https://github.com/BookStackApp/BookStack/releases).
+
+#### Updating to v22.04 or higher
+
+**Database Changes** - This release makes some significant changes to data within the database which may cause the update to take a little longer than usual to run. Please give the update extra time to complete.
+
+**REST API Page Create/Update Changes** - Create & update page requests now have the potential to change the current editor type for that page, depending on the content type sent in the request, if the API user has permission to change the page editor.
+
+**URL Handling** - The way we handle URLs has changed this release to hopefully address some issues in specific scenarios. These changes have been tested and should not affect existing working environments but there's an increased risk this release for setups with more complex URL handling. Please [raise an issue](https://github.com/BookStackApp/BookStack/issues/new/choose) or jump into our Discord server if you have any issues with URLs after upgrading.
+
+#### Updating to v22.03 or higher
+
+**Webhook Data Changes** - Properties found at the `related_item -> created_by/updated_by/owned_by` path of the webhook data will now be an object instead of an ID integer. If you were using these ids you'd now need to access them within the relevant objects. (For example `related_item.created_by.id`).
+
+#### Updating to v22.02.3 or higher
+
+**Security** - v22.02.3 adds controls to limit external/iframe content on BookStack pages to prevent potential malicious sources being used. See [the added "Iframe Source Control" section on our security page](/docs/admin/security/#iframe-src-control) for more detail regarding the added controls.
+
+#### Updating to v22.02 or higher
+
+**PHP Version Requirement Change** - The minimum required version of PHP has changed from 7.3 to 7.4. This should not be a concern for those that are using common containers or for those that have installed using our install scripts.
+
+#### Updating to v21.12.3 or higher
+
+**Composer Version Requirement Change** - Composer v2.0 or greater is now required to install or update BookStack. 
+You can check your composer version by running `composer -V`. 
+You can often update composer by running `sudo composer self-update` (Or you may be prompted to run `sudo composer self-update --2`).
+If you're using a system-supplied composer package you may need to first uninstall that (eg. `sudo apt remove composer`) then follow the [composer download documentation](https://getcomposer.org/download/) to get the latest version. Take notice of the `sudo mv composer.phar /usr/local/bin/composer` command shown in the documentation to install composer globally for easier usage.
+
+#### Updating to v21.12.1 or higher
+
+**Security** - v21.12.1 better enforces permissions on book-sort & chapter-move operations to address scenarios where content could be moved to non-permissible locations.
+
+#### Updating to v21.11.3 or higher
+
+**Security** - v21.11.3 helps prevent potential discovery and harvesting of user details including name and email address.
+
+#### Updating to v21.11.2 or higher
+
+**Security** - v21.11.2 addresses a couple of vulnerabilities relating to API access
+and page draft related content visibility. If the "Public" role was provided API access then the API could
+be accessed, in certain scenarios, by non-authenticated users even if the "Allow public access" setting was disabled.
+In some specific scenarios, content related to page drafts (Such as attachments) could be visible to non-owners 
+(Whom would have permission to view the page if saved  as a non-draft at that point).
+
+#### Updating to v21.11 or higher
+
+**API Changes** - As of v21.11 any dates in API responses will be formatted as per ISO-8601, with `2019-12-02T20:01:00.283041Z` reflecting an example of this format. You may need to review any of your scripts that utilise dates from API responses.
+
+**Upload Limit** - System file upload limits are now configured using a `FILE_UPLOAD_SIZE_LIMIT` option in your 
+  `.env` file. This value is specified as an integer and represents the max upload size in MegaBytes. This defaults to 50MB. This replaces the old `window.uploadLimit` HTML head option that could be set.
+
+**Search Index Changes** - Changes to search indexing and scoring were made in this release. 
+  It's recommended to run `php artisan bookstack:regenerate-search` to ensure a consistent search experience and take
+  advantage of these changes.
+
+**Logout Endpoints** - Logout endpoints have now changed to be CSRF protected POST endpoints instead of GET endpoints. If you were using these for any external purposes you may now need to implement an alternative workflow.
+
+
+#### Updating to v21.10.3 or higher
+
+**Security** - v21.10.3 looks to address a couple of vulnerabilities within the attachment and image
+serving mechanisms. The attachment vulnerability could result in users uploading content to be served
+in a way that can be utilized for phishing. The image serving vulnerability could result in unintended
+file access within your BookStack storage folder.
+
+#### Updating to v21.10.1/v21.10.2 or higher
+
+**Security** - Both v21.10.1 and v21.10.2 were released to address a vulnerability
+which would allow malicious users, who have permission to update or create pages, to upload
+content that could then be utilized for phishing or other general malicious intent.
+
+#### Updating to v21.08.5 or higher
+
+**Security** - v21.08.5 fixes a vulnerability which would allow malicious users, who have 
+permission to update or create pages, to load content from files stored within
+the `storage/` or `public/` directories (Such as application logs) via the
+page HTML export system. In addition, this release adds stricter cache-control headers to http 
+responses while logged in to prevent back navigation to authorized resources after logout.
+
+#### Updating to v21.08.2 or higher
+
+**Security** - v21.08.2 fixes a couple of XSS security vulnerability scenarios that could be achieved by malicious users that had permission to edit pages.
+  In addition, v21.08.2 introduces more [CSP rules](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to help prevent any future XSS vulnerabilities from taking affect.
+  If you've performed some more advanced customizations on your instance, they may need to be altered to work with the built-in CSP system.
+
+#### Updating to v21.08 or higher
+
+**Config & Administration** - The introduction of multi-factor authentication brings the first use of encryption in the platform.
+  This uses the `APP_KEY` value in your `.env` file. Ensure you have this stored safely since it would be required if you ever
+  restore/migrate your instance to another system.
+
+**Security/Exports** - During this release cycle it was highlighted that server-side request forgery could be achieved via the 
+  PDF export system. External fetching in the default PDF renderer has been disabled by default. The WKHTMLtoPDF renderer will now 
+  not be used if active. Either of these changes can be overridden by setting `ALLOW_UNTRUSTED_SERVER_FETCHING=true` in your `.env` file.
+  This should only be used were only trusted users can create and export content. To support this we've added permissions that allow disabling of exports per role.
+  
+**Security/Authentication** - A slight change was made in relation to how email addresses are confirmed. Email confirmations are now primarily checked at point-of-login rather
+  than being checked on every request. Enabling email confirmation, or email domain restrictions, may no longer take action on unconfirmed users right away in the future.
+
+
+#### Updating to v21.04 or higher
+
+**Requirements Change** - The minimum required PHP version has changed from 7.2 to 7.3. If you originally updated using the Ubuntu 18.04 installation script, the below commands should help you to upgrade to PHP8:
+
+```bash
+sudo add-apt-repository ppa:ondrej/php
+sudo apt update
+sudo apt install -y php8.0 php8.0-fpm php8.0-curl php8.0-mbstring php8.0-ldap php8.0-xml php8.0-zip php8.0-gd php8.0-mysql libapache2-mod-php8.0
+sudo a2dismod php7.2
+sudo a2enmod php8.0
+systemctl restart apache2
+```
+
+**User Reference Changes** - References to users in URLs of profile pages, and within search filters, has been changed to be name (Slugified) based instead of ID based. Old links or saved search filters may no longer work as expected.
+
+#### Updating to v0.31.0 or higher
+
+**Requirements Change** - The minimum required PHP version has changed from 7.2 to 7.2.5. Additionally, the `Tidy` PHP extension is no longer required.
+
+**GitLab Authentication** - The `read_user` scope will now be passed and is required on the "Application" setup within GitLab. Not having this scope may lead to errors when users attempt to authenticate via GitLab.
+
+**Security & IFrame Usage** - By default BookStack will set headers to prevent usage within an iframe. You can set trusted iframe hosts through the `ALLOWED_IFRAME_HOSTS` option in your `.env` file. See the [security page](/docs/admin/security#iframe-control) for more information on this option.
+
+#### Updating to v0.30.6, v0.30.7 or higher
+
+**Security** - v0.30.6 and v0.30.7 both address issues where page content could be visible to those without permission. If a chapter was visible to a user, but all of it's pages were made not visible, then the details of these pages could be visible. Within the BookStack interface, the names of the pages and preview content could be seen. If the parent book was exported then this would include the content of the pages that had been restricted. If using BookStack v0.30.6, then all non-visible page content could be visible in plaintext exports. Please see the blog release pages for more details: [v0.30.6](/blog/beta-release-v0-30-6/), [v0.30.7](/blog/beta-release-v0-30-7/).
+
+#### Updating to v0.30.5 or higher
+
+**Security** - v0.30.5 fixes an potential vulnerability where a user with edit permissions could perform server-side requests using the export system. Additionally it was found that, if using standard email/password authentication, the system host URL could be manipulated on the forgot password form which could allow for email phishing attempts. Ensure you have set the `APP_URL` option in your `.env` file to help prevent this. Please see the [blog release page for more details](/blog/beta-release-v0-30-5/).
+
+#### Updating to v0.30.4 or higher
+
+**Security** - v0.30.4 fixes a couple of XSS vulnerabilities that could be exploited by untrusted users via page content and page link attachments. Please see the [blog release page for more details](/blog/beta-release-v0-30-4/).
+
+#### Updating to v0.30 or higher
+
+**Security** - Possible Privilege Escalation. During the v0.30 release cycle
+it was advised that current privilege escalation situations are not made clear when applying role permissions.
+Any user with a "Manage app settings", "Manage users" or "Manage roles & role permissions" system permission 
+assigned to one of their roles could technically alter their own permissions to gain wider access.
+A clear advisory of these cases has been added in the UI in v0.30
+but admins are advised to review which users have these permissions with the above in mind.
+
+
+**LDAP & SAML Group Matching** - During the v0.30 release cycle it was found that 
+BookStack roles would be matched to LDAP/SAML groups based upon the role display name, which is expected,
+but only those roles with a matching "name" value would be considered. This "name" field was redundant, 
+and has now been removed, but it would store a cleaned version the first-set name of the role.
+All roles will now be considered before being matched on name which may mean that roles which did not sync before, 
+that would have been expected to based on their name, may now start to sync.
+
 
 #### Updating to v0.29.3 or higher
 
@@ -56,7 +215,7 @@ sudo systemctl restart nginx.service
 
 #### Updating to v0.26 or higher
 
-**Internet Explorer Support** - IE11 Support has now been dropped. We *may* support any critical issues for view-only scenarios otherwise please use a modern browser.
+**Internet Explorer Support** - IE11 Support has now been dropped. Please use a modern browser.
 
 **Translations** - Since many interfaces and lines of text have been updated, It may take a little while for some translations to catch-up. Expect to see more English text than usual if you're using a non-English language option.
 
@@ -110,5 +269,5 @@ The v0.13 release contained some new features and updates which change the requi
   Upgrade your PHP version if below 5.6.4.
 * PHP-Tidy extension is now required.
   - On Ubuntu 16.04 this can be installed via `sudo apt install php7.0-tidy`.
-  - On Ubuntu 14.04 (Using the defauly PHP option) this can be installed via `sudo apt-get install php5-tidy`.
+  - On Ubuntu 14.04 (Using the default PHP option) this can be installed via `sudo apt-get install php5-tidy`.
 * Page attachments will be stored in the `storage/uploads` folder (Unless you use Amazon S3). This folder will be created on update. Ensure your webserver has write permissions for this folder.
index 68e1aae22de2fc28b05a90e4cab4a40048dd3dd2..6ef4d05a7beec88648662931d74fc8f92df87a30 100644 (file)
@@ -10,7 +10,6 @@ BookStack allows users to upload both images for content and files as attachment
 * [Storage Options](#storage-options)
 * [Changing Upload Limits](#changing-upload-limits)
 * [File Upload Timeout](#file-upload-timeout)
-* [File Upload Limit](#file-upload-limit)
 
 **For information relating to security for file uploads please refer to the [Security Page](/docs/admin/security).**
 
@@ -117,7 +116,18 @@ STORAGE_ATTACHMENT_TYPE=local_secure
 
 ### Changing Upload Limits
 
-By default, a lot of server software has strict limits on upload sizes which causes errors when users upload new content. This is not configured as part of BookStack but as part of PHP and your web sever software. If you run into problems with upload size limits follow the below details for PHP and whichever web server you use:
+By default, a lot of server software has strict limits on upload sizes which causes errors when users upload new content. BookStack enforces its own limit but there may also be limits configured as part of PHP and your web sever software. If you run into problems with upload size limits follow the below details for BookStack, PHP and whichever web server you use.
+
+#### BookStack
+
+The upload limit in BookStack is configured through an option in your `.env` file. 
+Find or add the follow option then configure to your requirements.
+
+```bash
+# File Upload Limit
+# Maximum file size, in megabytes, that can be uploaded to the system.
+FILE_UPLOAD_SIZE_LIMIT=50
+```
 
 #### PHP
 
@@ -128,11 +138,17 @@ PHP has two main variables which effect upload limits. Find your `php.ini` file
 
 If the values of these variables are low increase them to something sensible that's not too high to cause issues. Unless you need something higher 10MB is a sensible value to enter for these values:
 
-```
+```ini
 post_max_size = 10M
 upload_max_filesize = 10M
 ```
 
+If wanting to upload files over 128MB, you may also need to adjust your PHP memory limit like so:
+
+```ini
+memory_limit = 256M
+```
+
 After updating these values ensure you restart your webserver and also PHP if using PHP-FPM or something similar.
 
 #### NGINX
@@ -176,17 +192,3 @@ To modify this timeout, in BookStack settings, Find the 'Custom HTML head conten
     window.uploadTimeout = 120 * 1000;
 </script>
 ```
-
----
-
-### File Upload Limit
-
-File uploads in BookStack use a JavaScript library which has a default upload size limit of 256MB. To modify this timeout, in BookStack settings, Find the 'Custom HTML head content' setting and add the below code. Note that this does not change any server-side upload limits, Your websever may still impose an upload limit.
-
-```html
-<script>
-    // Set the file upload limit to 1.5GB.
-    // The value is defined in MB. 
-    window.uploadLimit = 1500;
-</script>
-```
\ No newline at end of file
index f77a387dd15a796e78f939227d8d2bf842e580ca..ae1ca0d0c256a49e1be74242c9c894331ce5f762 100644 (file)
@@ -21,7 +21,7 @@ in the admin settings of BookStack.
 body, button, input, select, label, textarea {
   font-family: "Roboto", sans-serif;
 }
-.Codemirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
+.CodeMirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
   font-family: monospace;
 }
 </style>
@@ -35,7 +35,7 @@ Here's an example of using the 'Lato' font from [Google Web Fonts](https://fonts
 body, button, input, select, label, textarea {
   font-family: 'Lato', sans-serif;
 }
-.Codemirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
+.CodeMirror, pre, #markdown-editor-input, .editor-toolbar, .code-base {
   font-family: monospace;
 }
 </style>
index fb556e8bd42efc380c7b98f344ab932f65dde056..ebc7d9de440a4db678c93b300ae82ab702521a28 100644 (file)
@@ -9,7 +9,7 @@ The principles of storing information within BookStack is based of the ideas of
 
 Within a book you can directly create pages or you can first create chapters. Chapters provide an additional level of page grouping to keep pages organised but are optional. All the information you write is held within pages. Although books and chapters do not hold information they can be given a short description to assist with searching and visibility.
 
-Once you start to stack-up books you can start to use Bookshelves to organise your Books. Bookshelves can contain mulitple books and a single book could be placed on multiple Bookshelves. 
+Once you start to stack-up books you can start to use Bookshelves to organise your Books. Bookshelves can contain multiple books and a single book could be placed on multiple Bookshelves. 
 
 ### Default Colour Coding
 
@@ -32,7 +32,7 @@ Bookshelves, Books, chapters and pages have set colour coding in BookStack to en
 
 ### Starting Out
 
-When you start out with your new BookStack instance you can organise things in two ways; You can plan out your book/chapter/page structure or you can let things grow naturally over time. If you know or already have the content which will be going into BookStack then it's probably best to plan early otherwise, if you're starting from scratch, you'll want to let everything find it's own place.
+When you start out with your new BookStack instance you can organise things in two ways; You can plan out your book/chapter/page structure or you can let things grow naturally over time. If you know or already have the content which will be going into BookStack then it's probably best to plan early otherwise, if you're starting from scratch, you'll want to let everything find its own place.
 
-If you decide to grow naturally then you'll likely start with a book for each major category and add pages directly into those books. As you start getting a lot of pages in each book you'll start to use chapters to group those pages. Once a chapter gets too large you may find it better to split it out into it's own book. Within BookStack you can easily move chapters and pages between books and chapters so you shouldn't worry about having to move things around in the future.
+If you decide to grow naturally then you'll likely start with a book for each major category and add pages directly into those books. As you start getting a lot of pages in each book you'll start to use chapters to group those pages. Once a chapter gets too large you may find it better to split it out into its own book. Within BookStack you can easily move chapters and pages between books and chapters so you shouldn't worry about having to move things around in the future.
 
index c90da440e53f601dd14c638d8c39e2192a187d50..2aee73296034c2c7127e0771c902be301c2fb732 100644 (file)
@@ -6,9 +6,20 @@ type = "user-doc"
 +++
 
 If you prefer to write in Markdown, the editor in BookStack can be changed at an instance level
-to use a markdown editor instead of the default WYSIWYG editor. The option to use Markdown is currently **not** a user setting but a global instance setting due to formatting differences between the two editors.
+to use a Markdown editor instead of the default WYSIWYG editor. The option to use Markdown is currently **not** a user setting but a global instance setting due to formatting differences between the two editors.
+
+> Note that shifting to the Markdown editor from the WYSIWYG editor may cause unintended side effects to existing content. This is due to the differences in the way the content is stored in the database.
+
+
+### Switching to the Markdown Editor
+
+To change the editor to Markdown follow the steps below. You'll need to have the "Manage app settings" role permission to be able to follow these steps:
+
+1. Within your BookStack instance, find and click on **Settings** in the navbar.
+2. Scroll down to the **Customization** section.
+3. Find the **Page Editor** setting and select `Markdown` from the dropdown menu.
+4. Save settings.
 
-> Note that shifting to the markdown editor from the WYSIWYG editor may cause unintended side effects to existing content. This is due to the differences in the way the content is stored in the database.
 
 ### Editor Shortcuts
 
index ee2db2ffdcb7530939617c2bc5b2fa0d752698d2..90c80920fb7203435fdb90856200dd2a3b636513 100644 (file)
@@ -9,18 +9,26 @@ Once your BookStack instance starts to grow you will find that you may want to r
 
 Note that to move content you will need to have 'edit' permission for both the content being moved and the parent it's being moved into.
 
-### Moving Single Books & Chapters
+### Moving Single Pages & Chapters
 
-Books and chapters can be moved directly to a new chapter or book. To move a chapter or page in this way go to a page or chapter and select 'Move' in the overflow menu, found on the right-hand side of the top toolbar:
+Pages and chapters can be moved directly to a new chapter or book. To move a chapter or page in this way, go to a page or chapter and select 'Move' in the actions list:
 
-![Page Move Menu](/images/docs/page-move-menu.png)
+<img alt="Page Move Action" src="/images/docs/page-move.png" width="240">
 
 Clicking the 'Move' action will take you to a screen where you can select a new location for your chapter or page. Here you can search for a particular book or chapter using the search bar at the top of the selection screen. Once you select a new parent for your chapter or page press 'Move Page' or 'Move Chapter' and your chapter or page will be moved to the new chapter or book. If you move a chapter all child pages will remain in that chapter and any related activity will now show up under the new parent book.
 
 ### Sorting Books
 
-The 'Book Sort' interface allows you to move multiple pages and chapters with ease in a simple drag and drop interface. To sort a book simply go to the book and select 'Sort' in the overflow menu (3 vertical dots found next to the edit button) at the top right of the page and you will be directed to the sort view:
+The 'Book Sort' interface allows you to move multiple pages and chapters with ease in a simple drag and drop interface. To sort the content of a Book content, open the Book and find the "Sort" option within the actions list.
 
 ![Book Sort](/images/docs/book-sort.png)
 
 Initially, just the book you came from will show on the left. You can add extra books into the sort interface by selecting them on the right. Here you can simply drag and drop chapters and pages around and also between different books. Once you have organised your content press 'Save' and all included books will be re-organised.
+
+### Books & Shelves
+
+A bookshelf can contain multiple books and a book can be on multiple shelves. To add an existing book to a shelf start by opening the shelf you wish to move the book to and then press "Edit". Under the "Add books to this shelf" section find & select the book that you wish to move into the shelf you are currently editing and then choose "Save Shelf". Within that same interface you can alternatively drag books between the two lists, and you can drag books around in the left-hand list to dictate the default ordering of books within that shelf.
+
+![Adding or removing books on a shelf](/images/docs/user/shelf-book-organising.png)
+
+Keep in mind that a book can be on multiple shelves, so adding a book to a new shelf won't cause it to be removed from any others. If you want to remove a book from a shelf you'll need to go into each shelf in the same manner as above but select, or drag them out, from the "Books on this shelf" list before saving.
\ No newline at end of file
diff --git a/content/docs/user/page-permalinks.md b/content/docs/user/page-permalinks.md
new file mode 100644 (file)
index 0000000..1a731c6
--- /dev/null
@@ -0,0 +1,22 @@
++++
+title = "Page Permalinks"
+date = "2020-01-24"
+type = "user-doc"
+slug = "content-permalinks"
++++
+
+The URL for a page within BookStack includes a "Slug" generated based upon the name in addition to a "Slug" generated from the parent book's name. Upon name changes of the book or page, BookStack will use the revision system to attempt resolving when old links are used but it is possible for some actions to cause old page links to no longer lead to the updated content.
+
+The below details how you can find an id-based page link for scenarios where you want to be sure of a stable link.
+
+## Finding the Page Permalink
+
+A quick and easy way to find the page Permalink has been built into BookStack.
+Simply select any block of text within a page and you'll see a small popup box. Within this popup box will be an input containing the page permalink. A copy button next to the input allows you to copy the link with a single click.
+
+<video controls>
+    <source src="/images/2021/01/bookstack-permalink.webm" type="video/webm">
+    <source src="/images/2021/01/bookstack-permalink.mp4" type="video/mp4">
+</video>
+
+By default this link will include a `#` component so that the URL scrolls the page to the specific selected section. If you don't want this, and instead want the link to just lead to the page and not scroll down, you can remove the `#` and everything after it within the link you copied.
index 2fa28cc0b7077cd38a12fcc445ec1a58f57dc33b..9019326428d9848829fd2452a2f771f003a73da8 100644 (file)
@@ -6,7 +6,7 @@ type = "user-doc"
 slug = "reusing-page-content"
 +++
 
-Within BookStack you may find that you want to include the same block of content in multiple places. You could copy and paste that content into multiple pages but then, if that content needs to be changed, you'd have to update it multiple times across different pages. Introduced in BookStack v0.14 is the ability to include other pages and to also include single blocks of content from other pages.
+Within BookStack you may find that you want to include the same block of content in multiple places. You could copy and paste that content into multiple pages but then, if that content needs to be changed, you'd have to update it multiple times across different pages. By using include tags you can include entire other pages and also include single blocks of content from other pages.
 
 Note that the include behaviour is non-recursive so including will only work to a single level. This ensures performance and prevents users from breaking pages by creating include loops.
 
diff --git a/content/docs/user/roles-and-permissions.md b/content/docs/user/roles-and-permissions.md
new file mode 100644 (file)
index 0000000..5d6001e
--- /dev/null
@@ -0,0 +1,41 @@
++++
+title = "Roles and Permissions"
+date = "2021-04-15"
+type = "user-doc"
+slug = "roles-and-permissions"
++++
+
+Within BookStack the abilities of a user is controlled by the roles assigned to them and the permissions provided to those roles. A user can be assigned multiple roles, in which case the permissions will stack and the user will receive any ability if any of the roles is provided that specific ability.
+
+### Managing Roles
+
+Roles can be created and edited by an admin user by navigating to "Settings > Roles" within the BookStack interface. A set of default roles are provided in a fresh BookStack instance for common use-cases.
+When altering a role you'll be able to change the system permissions which provides certain system-wide abilities. 
+In addition, you'll see a range of "Asset Permissions" which are the default abilities provided to members of that role for content within the system. These may be overridden by content-level permissions. Many of the asset permissions have an "Own" option. This provides the members of that role the ability only when they are an owner of an item in the system.
+
+### Assigning Roles
+
+Roles can be assigned to users an admin user editing a user's profile in the "Settings > Users" area of the BookStack interface. Here multiple roles can be assigned to a user, in which case the permissions will stack and the user will receive any ability if any of the roles is provided that specific ability.
+
+Keep in mind that certain mechanisms within BookStack do have the ability to alter the roles of a user. These include:
+
+- The "Default user role after registration" option in the registration settings.
+- LDAP group syncing.
+- SAML2 group syncing.
+
+### Content Level Permissions
+
+Permissions can be overridden at a Shelf, Book, Chapter or Page level where required.
+This can be done using the "Permissions" action when viewing such content, by any user that has a role permission to "Manage all/own permissions".
+When overriding at content-level, an "Enable Custom Permissions" checkbox toggles the custom override on or off. When enabled, a table of roles and their permissions will be shown (Excluding the default "Admin" role which always retains all permissions). Within this table, you can then select the individual permissions you want each role to receive instead of their default role permissions. These custom permissions need to be configured for all roles as desired, permissions cannot currently be overridden on a singular role or per-user basis.
+
+![A view of the page permissions screen showing custom permissions being selected](/images/docs/user/page-permissions.png)
+
+Content level permissions within BookStack have the following behavior:
+
+- Custom permissions applied to books or chapters will auto-cascade to all child content within, unless that content has its own custom permissions.
+- Custom permissions applied to shelves _**will not**_ auto-cascade, due to the many-to-many relation to books, but they can be copied to all child books in a single action where required. 
+
+When custom content level permissions are active and affecting the currently viewed item, you'll see an indicator within the details sidebar section. If you have permission to edit the active content level permissions, this acts as a link which will take you to the relevant permissions view, even if applied to a parent chapter or book.
+
+![Content level permissions indicator for a page showing "Book Permissions Active"](/images/docs/user/permissions-active-indicator.png)
\ No newline at end of file
index 2b06d1e7ca4083f3e05c355a83d8422aed20b850..79a2915d7b158485dc51abb636518b4328d66000 100644 (file)
@@ -103,20 +103,28 @@ Filters are set advanced search features that can be used in your search term. T
     <td colspan="3">User Filters</td>
   </tr>
   <tr>
-    <td>{updated_by:&lt;user_id|me&gt;}</td>
+    <td>{updated_by:&lt;user_slug|me&gt;}</td>
     <td>
-      {updated_by:10} <br>
+      {updated_by:barry} <br>
       {updated_by:me} <br>
     </td>
-    <td>Adds the condition that the content must have been last updated by the user of the given numeric ID. If 'me' is used in place of a numeric ID then it will find content that was last updated by the current logged-in user.</td>
+    <td>Adds the condition that the content must have been last updated by the user of the given slug. If 'me' is used in place of a slug then it will find content that was last updated by the current logged-in user.</td>
   </tr>
   <tr>
-    <td>{created_by:&lt;user_id|me&gt;}</td>
+    <td>{created_by:&lt;user_slug|me&gt;}</td>
     <td>
-      {created_by:10} <br>
+      {created_by:barry} <br>
       {created_by:me} <br>
     </td>
-    <td>Adds the condition that the content must have been created by the user of the given numeric ID. If 'me' is used in place of a numeric ID then it will find content that was created by the current logged-in user.</td>
+    <td>Adds the condition that the content must have been created by the user of the given slug. If 'me' is used in place of a slug then it will find content that was created by the current logged-in user.</td>
+  </tr>
+  <tr>
+    <td>{owned_by:&lt;user_slug|me&gt;}</td>
+    <td>
+      {owned_by:barry} <br>
+      {owned_by:me} <br>
+    </td>
+    <td>Adds the condition that the content must have be actively owned by the user of the given slug. If 'me' is used in place of a slug then it will find content that is owned by the current logged-in user.</td>
   </tr>
   <tr style="font-weight:bold;">
     <td colspan="3">Content Filters</td>
index bf55cdef7c053e4b71d1a996143ff31f0c506907..895f9ba8fff53c68c377842cf4925ef1c39339d9 100644 (file)
@@ -74,5 +74,9 @@ The following shortcuts are available in the WYSIWYG Editor:
       <td><code>Ctrl+9</code> / <code>Cmd+9</code></td>
       <td>Callout <br>(Keep pressing to toggle through styles)</td>
     </tr>
+    <tr>
+      <td><code>Ctrl+Shift+K</code> / <code>Cmd+Shift+K</code></td>
+      <td>Link to BookStack content</td>
+    </tr>
   </tbody>
 </table>
diff --git a/content/support/_index.html b/content/support/_index.html
new file mode 100644 (file)
index 0000000..e553245
--- /dev/null
@@ -0,0 +1,91 @@
+---
+title: "Support for BookStack"
+url: "/support"
+layout: "single"
+type: "about"
+---
+
+
+<p class="text-center padded-vertical">
+    BookStack support is provided at few different tiers depending on 
+    what might suit you or your business. <br>
+    Our official paid support services are provided by <a href="https://www.httpfunctions.com" target="_blank">HTTP Functions Ltd</a>.
+</p>
+
+<div class="price-table">
+    <div class="price-table-offer">
+        <div class="price-table-header">
+            <h3>Community Support</h3>
+            <div class="price">Free</div>
+        </div>
+        <div class="price-table-item">
+            Available via 
+            <a href="https://github.com/BookStackApp/BookStack/issues" target="_blank">GitHub</a>,
+            <a href="https://discord.com/invite/ztkBqR2" target="_blank">Discord</a>
+            or
+            <a href="https://www.reddit.com/r/BookStack/" target="_blank">Reddit</a>
+        </div>
+        <div class="price-table-item">
+            Open use of <a href="/docs">documentation</a> and 
+            <a href="https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g" target="_blank">YouTube</a>
+             guidance
+        </div>
+        <div class="price-table-item price-table-item-negative">
+            No assurance of response, support or resolution
+        </div>
+    </div>
+
+    <div class="price-table-offer">
+        <div class="price-table-header">
+            <h3>Professional Support Plan</h3>
+            <div class="price">£450 /year</div>
+        </div>
+        <div class="price-table-item">
+            Email / Help-desk based support
+        </div>
+        <div class="price-table-item">
+            Covers installation and updating issues
+        </div>
+        <div class="price-table-item">
+            High priority triage and handling of bug and issue reports
+        </div>
+        <div class="price-table-item">
+            Supports development & maintenance of the open source platform
+        </div>
+        <div class="price-table-action">
+            <a href="https://bookstack-support.httpfunctions.com/signup/professional-support">Proceed &raquo;</a>
+        </div>
+    </div>
+    <div class="price-table-offer">
+        <div class="price-table-header">
+            <h3>Enterprise Support Plan</h3>
+            <div class="price">£4,500 /year</div>
+        </div>
+        <div class="price-table-item">
+            Includes everything in the Professional Support Plan
+        </div>
+        <div class="price-table-item price-table-item-highlight">
+            Highest priority handling of support&nbsp;requests
+        </div>
+        <div class="price-table-item price-table-item-highlight">
+            Active assistance for API integrations & platform extension
+        </div>
+        <div class="price-table-item price-table-item-highlight">
+            Feature and road-map discussion with the core project maintainer
+        </div>
+        <div class="price-table-item price-table-item-highlight">
+            Zoom/Teams/Meet/Discord based video support & advice <br>
+            <small><i>(Up to 10 hours per year)</i></small>
+        </div>
+        <div class="price-table-action">
+            <a href="https://bookstack-support.httpfunctions.com/signup/enterprise-support">Proceed &raquo;</a>
+        </div>
+    </div>
+</div>
+
+
+
+<p class="text-center small padded-vertical">
+    VAT is not included in current pricing although may be chargeable in the future. <br>
+    Full terms and conditions of support services can <a href="https://www.httpfunctions.com/docs/bookstack-support-terms.pdf" target="_blank">be found here</a>.
+</p>
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
deleted file mode 100644 (file)
index c77159d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-const gulp = require('gulp');
-const plumber = require('gulp-plumber');
-const rename = require('gulp-rename');
-const cleanCss = require('gulp-clean-css');
-const sass = require('gulp-sass');
-
-gulp.task('styles', function() {
-  return gulp.src(['themes/bookstack/sass/**/*.scss'])
-    .pipe(plumber({
-      errorHandler: function (error) {
-        console.log(error.message);
-        this.emit('end');
-    }}))
-    .pipe(sass())
-    .pipe(gulp.dest('themes/bookstack/static/css/'))
-    .pipe(rename({suffix: '.min'}))
-    .pipe(cleanCss())
-    .pipe(gulp.dest('themes/bookstack/static/css/'));
-});
-
-
-gulp.task('default', gulp.series('styles'));
-
-gulp.task('watch', function() {
-  gulp.watch('themes/bookstack/sass/**/*.scss', gulp.series('styles'));
-});
index d735fa00f1e2250feb7099ab90baede7a234e0c0..ded5f7e1dc2e4f00ba0621b659df14c38a5006e9 100644 (file)
 {
-  "name": "BookStack-Site",
+  "name": "bookstack-site",
   "version": "1.0.0",
-  "lockfileVersion": 1,
+  "lockfileVersion": 2,
   "requires": true,
-  "dependencies": {
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-      "dev": true
-    },
-    "ajv": {
-      "version": "6.10.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
-      "dev": true,
-      "requires": {
-        "fast-deep-equal": "^2.0.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      }
-    },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
-      "dev": true
-    },
-    "ansi-colors": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
-      "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
-      "dev": true,
-      "requires": {
-        "ansi-wrap": "^0.1.0"
-      }
-    },
-    "ansi-cyan": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz",
-      "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=",
-      "dev": true,
-      "requires": {
-        "ansi-wrap": "0.1.0"
-      }
-    },
-    "ansi-gray": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
-      "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
-      "dev": true,
-      "requires": {
-        "ansi-wrap": "0.1.0"
-      }
-    },
-    "ansi-red": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz",
-      "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=",
-      "dev": true,
-      "requires": {
-        "ansi-wrap": "0.1.0"
-      }
-    },
-    "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-      "dev": true
-    },
-    "ansi-styles": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-      "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-      "dev": true
-    },
-    "ansi-wrap": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
-      "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
-      "dev": true
-    },
-    "anymatch": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-      "dev": true,
-      "requires": {
-        "micromatch": "^3.1.4",
-        "normalize-path": "^2.1.1"
-      }
-    },
-    "append-buffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
-      "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
-      "dev": true,
-      "requires": {
-        "buffer-equal": "^1.0.0"
-      }
-    },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "dev": true
-    },
-    "archy": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
-      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
-      "dev": true
-    },
-    "are-we-there-yet": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
-      "dev": true,
-      "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
-    "arr-diff": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-      "dev": true
-    },
-    "arr-filter": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
-      "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
-      "dev": true,
-      "requires": {
-        "make-iterator": "^1.0.0"
-      }
-    },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-      "dev": true
-    },
-    "arr-map": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
-      "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
-      "dev": true,
-      "requires": {
-        "make-iterator": "^1.0.0"
-      }
-    },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
-      "dev": true
-    },
-    "array-each": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
-      "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
-      "dev": true
-    },
-    "array-find-index": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
-      "dev": true
-    },
-    "array-initial": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
-      "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
-      "dev": true,
-      "requires": {
-        "array-slice": "^1.0.0",
-        "is-number": "^4.0.0"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
-          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
-          "dev": true
-        }
-      }
-    },
-    "array-last": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
-      "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
-      "dev": true,
-      "requires": {
-        "is-number": "^4.0.0"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
-          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
-          "dev": true
-        }
-      }
-    },
-    "array-slice": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
-      "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
-      "dev": true
-    },
-    "array-sort": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
-      "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
-      "dev": true,
-      "requires": {
-        "default-compare": "^1.0.0",
-        "get-value": "^2.0.6",
-        "kind-of": "^5.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "array-unique": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-      "dev": true,
-      "requires": {
-        "safer-buffer": "~2.1.0"
-      }
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
-      "dev": true
-    },
-    "async-done": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz",
-      "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.2",
-        "process-nextick-args": "^1.0.7",
-        "stream-exhaust": "^1.0.1"
-      }
-    },
-    "async-each": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
-      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
-      "dev": true
-    },
-    "async-foreach": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
-      "dev": true
-    },
-    "async-settle": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
-      "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
-      "dev": true,
-      "requires": {
-        "async-done": "^1.2.2"
-      }
-    },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true
-    },
-    "aws4": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
-      "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==",
-      "dev": true
-    },
-    "bach": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
-      "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
-      "dev": true,
-      "requires": {
-        "arr-filter": "^1.1.1",
-        "arr-flatten": "^1.0.1",
-        "arr-map": "^2.0.0",
-        "array-each": "^1.0.0",
-        "array-initial": "^1.0.0",
-        "array-last": "^1.1.1",
-        "async-done": "^1.2.2",
-        "async-settle": "^1.0.0",
-        "now-and-later": "^2.0.0"
-      }
-    },
-    "balanced-match": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
-    },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-      "dev": true,
-      "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "dev": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
-    },
-    "binary-extensions": {
-      "version": "1.13.1",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
-      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-      "dev": true
-    },
-    "block-stream": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
-      "dev": true,
-      "requires": {
-        "inherits": "~2.0.0"
-      }
-    },
-    "brace-expansion": {
-      "version": "1.1.11",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^1.0.0",
-        "concat-map": "0.0.1"
-      }
-    },
-    "braces": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-      "dev": true,
-      "requires": {
-        "arr-flatten": "^1.1.0",
-        "array-unique": "^0.3.2",
-        "extend-shallow": "^2.0.1",
-        "fill-range": "^4.0.0",
-        "isobject": "^3.0.1",
-        "repeat-element": "^1.1.2",
-        "snapdragon": "^0.8.1",
-        "snapdragon-node": "^2.0.1",
-        "split-string": "^3.0.2",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "buffer-equal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
-      "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
-      "dev": true
-    },
-    "buffer-from": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
-      "dev": true
-    },
-    "builtin-modules": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
-    },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-      "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      }
-    },
-    "camelcase": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-      "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-      "dev": true
-    },
-    "camelcase-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
-      "dev": true,
-      "requires": {
-        "camelcase": "^2.0.0",
-        "map-obj": "^1.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-          "dev": true
-        }
-      }
-    },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-      "dev": true
-    },
-    "chalk": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-      "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^2.2.1",
-        "escape-string-regexp": "^1.0.2",
-        "has-ansi": "^2.0.0",
-        "strip-ansi": "^3.0.0",
-        "supports-color": "^2.0.0"
-      }
-    },
-    "chokidar": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz",
-      "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==",
-      "dev": true,
-      "requires": {
-        "anymatch": "^2.0.0",
-        "async-each": "^1.0.1",
-        "braces": "^2.3.2",
-        "fsevents": "^1.2.7",
-        "glob-parent": "^3.1.0",
-        "inherits": "^2.0.3",
-        "is-binary-path": "^1.0.0",
-        "is-glob": "^4.0.0",
-        "normalize-path": "^3.0.0",
-        "path-is-absolute": "^1.0.0",
-        "readdirp": "^2.2.1",
-        "upath": "^1.1.1"
-      },
-      "dependencies": {
-        "normalize-path": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-          "dev": true
-        }
-      }
-    },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
-      "dev": true,
-      "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "cliui": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
-      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wrap-ansi": "^2.0.0"
-      }
-    },
-    "clone-buffer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
-      "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
-      "dev": true
-    },
-    "cloneable-readable": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.2.tgz",
-      "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "process-nextick-args": "^2.0.0",
-        "readable-stream": "^2.3.5"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
-    "collection-map": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
-      "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
-      "dev": true,
-      "requires": {
-        "arr-map": "^2.0.2",
-        "for-own": "^1.0.0",
-        "make-iterator": "^1.0.0"
-      }
-    },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
-      "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
-      }
-    },
-    "color-convert": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
-      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
-      "dev": true,
-      "requires": {
-        "color-name": "1.1.3"
-      }
-    },
-    "color-name": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
-      "dev": true
-    },
-    "color-support": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
-      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
-      "dev": true
-    },
-    "combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "requires": {
-        "delayed-stream": "~1.0.0"
-      }
-    },
-    "component-emitter": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
-      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
-      "dev": true
-    },
-    "concat-map": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
-    },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true
-    },
-    "convert-source-map": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
-      "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "~5.1.1"
-      }
-    },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "copy-props": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz",
-      "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==",
-      "dev": true,
-      "requires": {
-        "each-props": "^1.3.0",
-        "is-plain-object": "^2.0.1"
-      }
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
-    },
-    "cross-spawn": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
-      "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
-      "dev": true,
-      "requires": {
-        "lru-cache": "^4.0.1",
-        "which": "^1.2.9"
-      }
-    },
-    "currently-unhandled": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
-      "dev": true,
-      "requires": {
-        "array-find-index": "^1.0.1"
-      }
-    },
-    "d": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
-      "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
-      "dev": true,
-      "requires": {
-        "es5-ext": "^0.10.9"
-      }
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
-    "decamelize": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
-      "dev": true
-    },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
-      "dev": true
-    },
-    "default-compare": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
-      "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
-      "dev": true,
-      "requires": {
-        "kind-of": "^5.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "default-resolution": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
-      "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
-      "dev": true
-    },
-    "define-properties": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
-      "dev": true,
-      "requires": {
-        "object-keys": "^1.0.12"
-      }
-    },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-      "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
-    },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
-      "dev": true
-    },
-    "detect-file": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
-      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
-      "dev": true
-    },
-    "duplexify": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
-      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0",
-        "stream-shift": "^1.0.0"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "each-props": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
-      "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
-      "dev": true,
-      "requires": {
-        "is-plain-object": "^2.0.1",
-        "object.defaults": "^1.1.0"
-      }
-    },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-      "dev": true,
-      "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
-      }
-    },
-    "end-of-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
-      "dev": true,
-      "requires": {
-        "once": "^1.4.0"
-      }
-    },
-    "error-ex": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
-      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
-      "dev": true,
-      "requires": {
-        "is-arrayish": "^0.2.1"
-      }
-    },
-    "es5-ext": {
-      "version": "0.10.50",
-      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.50.tgz",
-      "integrity": "sha512-KMzZTPBkeQV/JcSQhI5/z6d9VWJ3EnQ194USTUwIYZ2ZbpN8+SGXQKt1h68EX44+qt+Fzr8DO17vnxrw7c3agw==",
-      "dev": true,
-      "requires": {
-        "es6-iterator": "~2.0.3",
-        "es6-symbol": "~3.1.1",
-        "next-tick": "^1.0.0"
-      }
-    },
-    "es6-iterator": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
-      "dev": true,
-      "requires": {
-        "d": "1",
-        "es5-ext": "^0.10.35",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "es6-symbol": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
-      "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
-      "dev": true,
-      "requires": {
-        "d": "1",
-        "es5-ext": "~0.10.14"
-      }
-    },
-    "es6-weak-map": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
-      "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
-      "dev": true,
-      "requires": {
-        "d": "1",
-        "es5-ext": "^0.10.14",
-        "es6-iterator": "^2.0.1",
-        "es6-symbol": "^3.1.1"
-      }
-    },
-    "escape-string-regexp": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-      "dev": true
-    },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "expand-tilde": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
-      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
-      "dev": true,
-      "requires": {
-        "homedir-polyfill": "^1.0.1"
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "dev": true
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
-      "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "extglob": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
-      "dev": true,
-      "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "dev": true
-    },
-    "fast-deep-equal": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
-      "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
-      "dev": true
-    },
-    "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
-      "dev": true
-    },
-    "fill-range": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1",
-        "to-regex-range": "^2.1.0"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "find-up": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-      "dev": true,
-      "requires": {
-        "path-exists": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      }
-    },
-    "findup-sync": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
-      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
-      "dev": true,
-      "requires": {
-        "detect-file": "^1.0.0",
-        "is-glob": "^4.0.0",
-        "micromatch": "^3.0.4",
-        "resolve-dir": "^1.0.1"
-      }
-    },
-    "fined": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
-      "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.2",
-        "is-plain-object": "^2.0.3",
-        "object.defaults": "^1.1.0",
-        "object.pick": "^1.2.0",
-        "parse-filepath": "^1.0.1"
-      }
-    },
-    "flagged-respawn": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
-      "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
-      "dev": true
-    },
-    "flush-write-stream": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
-      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.3.6"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "for-in": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
-      "dev": true
-    },
-    "for-own": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.1"
-      }
-    },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
-      "dev": true
-    },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
-      }
-    },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
-      "dev": true,
-      "requires": {
-        "map-cache": "^0.2.2"
-      }
-    },
-    "fs-mkdirp-stream": {
+  "packages": {
+    "": {
+      "name": "bookstack-site",
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
-      "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.11",
-        "through2": "^2.0.3"
+      "license": "MIT",
+      "devDependencies": {
+        "npm-run-all": "^4.1.5",
+        "sass": "^1.49.9"
       }
     },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "fsevents": {
-      "version": "1.2.9",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
-      "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+    "node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
-      "optional": true,
-      "requires": {
-        "nan": "^2.12.1",
-        "node-pre-gyp": "^0.12.0"
-      },
       "dependencies": {
-        "abbrev": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "ansi-regex": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "aproba": {
-          "version": "1.2.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "are-we-there-yet": {
-          "version": "1.1.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "delegates": "^1.0.0",
-            "readable-stream": "^2.0.6"
-          }
-        },
-        "balanced-match": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "brace-expansion": {
-          "version": "1.1.11",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "balanced-match": "^1.0.0",
-            "concat-map": "0.0.1"
-          }
-        },
-        "chownr": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "code-point-at": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "concat-map": {
-          "version": "0.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "console-control-strings": {
-          "version": "1.1.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "core-util-is": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "debug": {
-          "version": "4.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ms": "^2.1.1"
-          }
-        },
-        "deep-extend": {
-          "version": "0.6.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "delegates": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "detect-libc": {
-          "version": "1.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "fs-minipass": {
-          "version": "1.2.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "fs.realpath": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "gauge": {
-          "version": "2.7.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "aproba": "^1.0.3",
-            "console-control-strings": "^1.0.0",
-            "has-unicode": "^2.0.0",
-            "object-assign": "^4.1.0",
-            "signal-exit": "^3.0.0",
-            "string-width": "^1.0.1",
-            "strip-ansi": "^3.0.1",
-            "wide-align": "^1.1.0"
-          }
-        },
-        "glob": {
-          "version": "7.1.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "fs.realpath": "^1.0.0",
-            "inflight": "^1.0.4",
-            "inherits": "2",
-            "minimatch": "^3.0.4",
-            "once": "^1.3.0",
-            "path-is-absolute": "^1.0.0"
-          }
-        },
-        "has-unicode": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "iconv-lite": {
-          "version": "0.4.24",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safer-buffer": ">= 2.1.2 < 3"
-          }
-        },
-        "ignore-walk": {
-          "version": "3.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minimatch": "^3.0.4"
-          }
-        },
-        "inflight": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "once": "^1.3.0",
-            "wrappy": "1"
-          }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "ini": {
-          "version": "1.3.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "number-is-nan": "^1.0.0"
-          }
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "minimatch": {
-          "version": "3.0.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "brace-expansion": "^1.1.7"
-          }
-        },
-        "minimist": {
-          "version": "0.0.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "minipass": {
-          "version": "2.3.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safe-buffer": "^5.1.2",
-            "yallist": "^3.0.0"
-          }
-        },
-        "minizlib": {
-          "version": "1.2.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minipass": "^2.2.1"
-          }
-        },
-        "mkdirp": {
-          "version": "0.5.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "minimist": "0.0.8"
-          }
-        },
-        "ms": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "needle": {
-          "version": "2.3.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "debug": "^4.1.0",
-            "iconv-lite": "^0.4.4",
-            "sax": "^1.2.4"
-          }
-        },
-        "node-pre-gyp": {
-          "version": "0.12.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "detect-libc": "^1.0.2",
-            "mkdirp": "^0.5.1",
-            "needle": "^2.2.1",
-            "nopt": "^4.0.1",
-            "npm-packlist": "^1.1.6",
-            "npmlog": "^4.0.2",
-            "rc": "^1.2.7",
-            "rimraf": "^2.6.1",
-            "semver": "^5.3.0",
-            "tar": "^4"
-          }
-        },
-        "nopt": {
-          "version": "4.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "abbrev": "1",
-            "osenv": "^0.1.4"
-          }
-        },
-        "npm-bundled": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "npm-packlist": {
-          "version": "1.4.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ignore-walk": "^3.0.1",
-            "npm-bundled": "^1.0.1"
-          }
-        },
-        "npmlog": {
-          "version": "4.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "are-we-there-yet": "~1.1.2",
-            "console-control-strings": "~1.1.0",
-            "gauge": "~2.7.3",
-            "set-blocking": "~2.0.0"
-          }
-        },
-        "number-is-nan": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "object-assign": {
-          "version": "4.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "once": {
-          "version": "1.4.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "wrappy": "1"
-          }
-        },
-        "os-homedir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "os-tmpdir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "osenv": {
-          "version": "0.1.5",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "os-homedir": "^1.0.0",
-            "os-tmpdir": "^1.0.0"
-          }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "rc": {
-          "version": "1.2.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "deep-extend": "^0.6.0",
-            "ini": "~1.3.0",
-            "minimist": "^1.2.0",
-            "strip-json-comments": "~2.0.1"
-          },
-          "dependencies": {
-            "minimist": {
-              "version": "1.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "rimraf": {
-          "version": "2.6.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "glob": "^7.1.3"
-          }
-        },
-        "safe-buffer": {
-          "version": "5.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "safer-buffer": {
-          "version": "2.1.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "sax": {
-          "version": "1.2.4",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "semver": {
-          "version": "5.7.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "set-blocking": {
-          "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "signal-exit": {
-          "version": "3.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "string-width": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-regex": "^2.0.0"
-          }
-        },
-        "strip-json-comments": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "tar": {
-          "version": "4.4.8",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "chownr": "^1.1.1",
-            "fs-minipass": "^1.2.5",
-            "minipass": "^2.3.4",
-            "minizlib": "^1.1.1",
-            "mkdirp": "^0.5.0",
-            "safe-buffer": "^5.1.2",
-            "yallist": "^3.0.2"
-          }
-        },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "wide-align": {
-          "version": "1.1.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "string-width": "^1.0.2 || 2"
-          }
-        },
-        "wrappy": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "yallist": {
-          "version": "3.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        }
-      }
-    },
-    "fstream": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
-      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
-        "rimraf": "2"
-      }
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "gaze": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
-      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+    "node_modules/anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
       "dev": true,
-      "requires": {
-        "globule": "^1.0.0"
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
       }
     },
-    "get-caller-file": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
-      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
-      "dev": true
-    },
-    "get-stdin": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-      "dev": true
-    },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
+      "engines": {
+        "node": ">=8"
       }
     },
-    "glob-parent": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
-      "requires": {
-        "is-glob": "^3.1.0",
-        "path-dirname": "^1.0.0"
-      },
       "dependencies": {
-        "is-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^2.1.0"
-          }
-        }
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "glob-stream": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
-      "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
       "dev": true,
-      "requires": {
-        "extend": "^3.0.0",
-        "glob": "^7.1.1",
-        "glob-parent": "^3.1.0",
-        "is-negated-glob": "^1.0.0",
-        "ordered-read-streams": "^1.0.0",
-        "pumpify": "^1.3.5",
-        "readable-stream": "^2.1.5",
-        "remove-trailing-separator": "^1.0.1",
-        "to-absolute-glob": "^2.0.0",
-        "unique-stream": "^2.0.2"
-      },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
-      }
-    },
-    "glob-watcher": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz",
-      "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==",
-      "dev": true,
-      "requires": {
-        "anymatch": "^2.0.0",
-        "async-done": "^1.2.0",
-        "chokidar": "^2.0.0",
-        "is-negated-glob": "^1.0.0",
-        "just-debounce": "^1.0.0",
-        "object.defaults": "^1.1.0"
-      }
-    },
-    "global-modules": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
-      "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
-      "dev": true,
-      "requires": {
-        "global-prefix": "^1.0.1",
-        "is-windows": "^1.0.1",
-        "resolve-dir": "^1.0.0"
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "global-prefix": {
+    "node_modules/call-bind": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
-      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.2",
-        "homedir-polyfill": "^1.0.1",
-        "ini": "^1.3.4",
-        "is-windows": "^1.0.1",
-        "which": "^1.2.14"
-      }
-    },
-    "globule": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
-      "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
-      "dev": true,
-      "requires": {
-        "glob": "~7.1.1",
-        "lodash": "~4.17.10",
-        "minimatch": "~3.0.2"
-      }
-    },
-    "glogg": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz",
-      "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=",
-      "dev": true,
-      "requires": {
-        "sparkles": "^1.0.0"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.1.15",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-      "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
-      "dev": true
-    },
-    "gulp": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.1.tgz",
-      "integrity": "sha512-yDVtVunxrAdsk7rIV/b7lVSBifPN1Eqe6wTjsESGrFcL+MEVzaaeNTkpUuGTUptloSOU+8oJm/lBJbgPV+tMAw==",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
       "dev": true,
-      "requires": {
-        "glob-watcher": "^5.0.3",
-        "gulp-cli": "^2.2.0",
-        "undertaker": "^1.2.1",
-        "vinyl-fs": "^3.0.0"
-      },
       "dependencies": {
-        "fancy-log": {
-          "version": "1.3.3",
-          "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
-          "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
-          "dev": true,
-          "requires": {
-            "ansi-gray": "^0.1.1",
-            "color-support": "^1.1.3",
-            "parse-node-version": "^1.0.0",
-            "time-stamp": "^1.0.0"
-          }
-        },
-        "gulp-cli": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz",
-          "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==",
-          "dev": true,
-          "requires": {
-            "ansi-colors": "^1.0.1",
-            "archy": "^1.0.0",
-            "array-sort": "^1.0.0",
-            "color-support": "^1.1.3",
-            "concat-stream": "^1.6.0",
-            "copy-props": "^2.0.1",
-            "fancy-log": "^1.3.2",
-            "gulplog": "^1.0.0",
-            "interpret": "^1.1.0",
-            "isobject": "^3.0.1",
-            "liftoff": "^3.1.0",
-            "matchdep": "^2.0.0",
-            "mute-stdout": "^1.0.0",
-            "pretty-hrtime": "^1.0.0",
-            "replace-homedir": "^1.0.0",
-            "semver-greatest-satisfied-range": "^1.1.0",
-            "v8flags": "^3.0.1",
-            "yargs": "^7.1.0"
-          }
-        }
-      }
-    },
-    "gulp-clean-css": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz",
-      "integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==",
-      "dev": true,
-      "requires": {
-        "clean-css": "4.2.1",
-        "plugin-error": "1.0.1",
-        "through2": "3.0.1",
-        "vinyl-sourcemaps-apply": "0.2.1"
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
       },
-      "dependencies": {
-        "clean-css": {
-          "version": "4.2.1",
-          "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
-          "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
-          "dev": true,
-          "requires": {
-            "source-map": "~0.6.0"
-          }
-        },
-        "readable-stream": {
-          "version": "3.3.0",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz",
-          "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==",
-          "dev": true,
-          "requires": {
-            "inherits": "^2.0.3",
-            "string_decoder": "^1.1.1",
-            "util-deprecate": "^1.0.1"
-          }
-        },
-        "source-map": {
-          "version": "0.6.1",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-          "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-          "dev": true
-        },
-        "string_decoder": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
-          "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "through2": {
-          "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
-          "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
-          "dev": true,
-          "requires": {
-            "readable-stream": "2 || 3"
-          }
-        }
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "gulp-plumber": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz",
-      "integrity": "sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ==",
+    "node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "fancy-log": "^1.3.2",
-        "plugin-error": "^0.1.2",
-        "through2": "^2.0.3"
-      },
       "dependencies": {
-        "arr-diff": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz",
-          "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.0.1",
-            "array-slice": "^0.2.3"
-          }
-        },
-        "arr-union": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz",
-          "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=",
-          "dev": true
-        },
-        "array-slice": {
-          "version": "0.2.3",
-          "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
-          "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
-          "dev": true
-        },
-        "extend-shallow": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz",
-          "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^1.1.0"
-          }
-        },
-        "fancy-log": {
-          "version": "1.3.3",
-          "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
-          "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
-          "dev": true,
-          "requires": {
-            "ansi-gray": "^0.1.1",
-            "color-support": "^1.1.3",
-            "parse-node-version": "^1.0.0",
-            "time-stamp": "^1.0.0"
-          }
-        },
-        "kind-of": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz",
-          "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=",
-          "dev": true
-        },
-        "plugin-error": {
-          "version": "0.1.2",
-          "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz",
-          "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=",
-          "dev": true,
-          "requires": {
-            "ansi-cyan": "^0.1.1",
-            "ansi-red": "^0.1.1",
-            "arr-diff": "^1.0.1",
-            "arr-union": "^2.0.1",
-            "extend-shallow": "^1.1.2"
-          }
-        }
-      }
-    },
-    "gulp-rename": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.4.0.tgz",
-      "integrity": "sha512-swzbIGb/arEoFK89tPY58vg3Ok1bw+d35PfUNwWqdo7KM4jkmuGA78JiDNqR+JeZFaeeHnRg9N7aihX3YPmsyg==",
-      "dev": true
-    },
-    "gulp-sass": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-4.0.2.tgz",
-      "integrity": "sha512-q8psj4+aDrblJMMtRxihNBdovfzGrXJp1l4JU0Sz4b/Mhsi2DPrKFYCGDwjIWRENs04ELVHxdOJQ7Vs98OFohg==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.3.0",
-        "lodash.clonedeep": "^4.3.2",
-        "node-sass": "^4.8.3",
-        "plugin-error": "^1.0.1",
-        "replace-ext": "^1.0.0",
-        "strip-ansi": "^4.0.0",
-        "through2": "^2.0.0",
-        "vinyl-sourcemaps-apply": "^0.2.0"
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
       },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
-          "dev": true
-        },
-        "ansi-styles": {
-          "version": "3.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
-          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
-          "dev": true,
-          "requires": {
-            "color-convert": "^1.9.0"
-          }
-        },
-        "chalk": {
-          "version": "2.4.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
-          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^3.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
-          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
-          "dev": true,
-          "requires": {
-            "has-flag": "^3.0.0"
-          }
-        }
-      }
-    },
-    "gulplog": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
-      "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
-      "dev": true,
-      "requires": {
-        "glogg": "^1.0.0"
+      "engines": {
+        "node": ">=4"
       }
     },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
-    },
-    "har-validator": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
       "dev": true,
-      "requires": {
-        "ajv": "^6.5.5",
-        "har-schema": "^2.0.0"
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
       }
     },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+    "node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
       "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
+      "dependencies": {
+        "color-name": "1.1.3"
       }
     },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
+    "node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
-      "dev": true,
-      "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
-      }
-    },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+    "node_modules/cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
       "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
-      },
       "dependencies": {
-        "kind-of": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "engines": {
+        "node": ">=4.8"
       }
     },
-    "homedir-polyfill": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
-      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+    "node_modules/define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
       "dev": true,
-      "requires": {
-        "parse-passwd": "^1.0.0"
+      "dependencies": {
+        "object-keys": "^1.0.12"
+      },
+      "engines": {
+        "node": ">= 0.4"
       }
     },
-    "hosted-git-info": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
-      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
-      "dev": true
-    },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
       "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
       }
     },
-    "in-publish": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
-      "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
-      "dev": true
-    },
-    "indent-string": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+    "node_modules/es-abstract": {
+      "version": "1.19.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz",
+      "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==",
       "dev": true,
-      "requires": {
-        "repeating": "^2.0.0"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.1.1",
+        "get-symbol-description": "^1.0.0",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "is-callable": "^1.2.4",
+        "is-negative-zero": "^2.0.2",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.1",
+        "is-string": "^1.0.7",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.2",
+        "string.prototype.trimend": "^1.0.4",
+        "string.prototype.trimstart": "^1.0.4",
+        "unbox-primitive": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+    "node_modules/es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
       "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
+      "dependencies": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-      "dev": true
-    },
-    "ini": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
-      "dev": true
-    },
-    "interpret": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
-      "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
-      "dev": true
-    },
-    "invert-kv": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
-      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
-      "dev": true
-    },
-    "is-absolute": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
-      "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true,
-      "requires": {
-        "is-relative": "^1.0.0",
-        "is-windows": "^1.0.1"
+      "engines": {
+        "node": ">=0.8.0"
       }
     },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
       "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
-    },
-    "is-binary-path": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
       "dev": true,
-      "requires": {
-        "binary-extensions": "^1.0.0"
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "is-builtin-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
-      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^1.0.0"
-      }
-    },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+    "node_modules/get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
       "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
       "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-      "dev": true,
-      "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
       },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-finite": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
-      "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
-      "dev": true,
-      "requires": {
-        "number-is-nan": "^1.0.0"
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-fullwidth-code-point": {
+    "node_modules/get-symbol-description": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
       "dev": true,
-      "requires": {
-        "number-is-nan": "^1.0.0"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
       "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
-    "is-negated-glob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
-      "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+    "node_modules/graceful-fs": {
+      "version": "4.2.9",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+      "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
       "dev": true
     },
-    "is-number": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
       "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
       }
     },
-    "is-plain-object": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
-      "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+    "node_modules/has-bigints": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+      "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
       "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-relative": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
-      "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+    "node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true,
-      "requires": {
-        "is-unc-path": "^1.0.0"
+      "engines": {
+        "node": ">=4"
       }
     },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
-    },
-    "is-unc-path": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
-      "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
       "dev": true,
-      "requires": {
-        "unc-path-regex": "^0.1.2"
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
-    },
-    "is-valid-glob": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
-      "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
-      "dev": true
-    },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-      "dev": true
-    },
-    "isarray": {
+    "node_modules/has-tostringtag": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-      "dev": true
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-      "dev": true
-    },
-    "js-base64": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
-      "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
-      "dev": true
-    },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "dev": true
-    },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
-      "dev": true
-    },
-    "json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+      "dev": true,
+      "dependencies": {
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+    "node_modules/hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+    "node_modules/immutable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
+      "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
       "dev": true
     },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+    "node_modules/internal-slot": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+      "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
       "dev": true,
-      "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
+      "dependencies": {
+        "get-intrinsic": "^1.1.0",
+        "has": "^1.0.3",
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 0.4"
       }
     },
-    "just-debounce": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz",
-      "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=",
-      "dev": true
-    },
-    "kind-of": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
-    "last-run": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
-      "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+    "node_modules/is-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
       "dev": true,
-      "requires": {
-        "default-resolution": "^2.0.0",
-        "es6-weak-map": "^2.0.1"
+      "dependencies": {
+        "has-bigints": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "lazystream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
-      "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
-      "requires": {
-        "readable-stream": "^2.0.5"
-      },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
-    "lcid": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
-      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+    "node_modules/is-boolean-object": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
       "dev": true,
-      "requires": {
-        "invert-kv": "^1.0.0"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "lead": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
-      "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+    "node_modules/is-callable": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+      "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
       "dev": true,
-      "requires": {
-        "flush-write-stream": "^1.0.2"
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "liftoff": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
-      "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+    "node_modules/is-core-module": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
       "dev": true,
-      "requires": {
-        "extend": "^3.0.0",
-        "findup-sync": "^3.0.0",
-        "fined": "^1.0.1",
-        "flagged-respawn": "^1.0.0",
-        "is-plain-object": "^2.0.4",
-        "object.map": "^1.0.0",
-        "rechoir": "^0.6.2",
-        "resolve": "^1.1.7"
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "load-json-file": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+    "node_modules/is-date-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
       "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0",
-        "strip-bom": "^2.0.0"
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-      "dev": true
-    },
-    "lodash.clonedeep": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
-      "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
-      "dev": true
-    },
-    "loud-rejection": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
       "dev": true,
-      "requires": {
-        "currently-unhandled": "^0.4.1",
-        "signal-exit": "^3.0.0"
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
       "dev": true,
-      "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "make-iterator": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
-      "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+    "node_modules/is-negative-zero": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
       "dev": true,
-      "requires": {
-        "kind-of": "^6.0.2"
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
-      "dev": true
-    },
-    "map-obj": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
-      "dev": true
-    },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
       "dev": true,
-      "requires": {
-        "object-visit": "^1.0.0"
+      "engines": {
+        "node": ">=0.12.0"
       }
     },
-    "matchdep": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
-      "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+    "node_modules/is-number-object": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
+      "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
       "dev": true,
-      "requires": {
-        "findup-sync": "^2.0.0",
-        "micromatch": "^3.0.4",
-        "resolve": "^1.4.0",
-        "stack-trace": "0.0.10"
-      },
       "dependencies": {
-        "findup-sync": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
-          "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
-          "dev": true,
-          "requires": {
-            "detect-file": "^1.0.0",
-            "is-glob": "^3.1.0",
-            "micromatch": "^3.0.4",
-            "resolve-dir": "^1.0.1"
-          }
-        },
-        "is-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^2.1.0"
-          }
-        }
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "meow": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
-      "dev": true,
-      "requires": {
-        "camelcase-keys": "^2.0.0",
-        "decamelize": "^1.1.2",
-        "loud-rejection": "^1.0.0",
-        "map-obj": "^1.0.1",
-        "minimist": "^1.1.3",
-        "normalize-package-data": "^2.3.4",
-        "object-assign": "^4.0.1",
-        "read-pkg-up": "^1.0.1",
-        "redent": "^1.0.0",
-        "trim-newlines": "^1.0.0"
-      }
-    },
-    "micromatch": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+    "node_modules/is-regex": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
       "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "braces": "^2.3.1",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "extglob": "^2.0.4",
-        "fragment-cache": "^0.2.1",
-        "kind-of": "^6.0.2",
-        "nanomatch": "^1.2.9",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.2"
-      }
-    },
-    "mime-db": {
-      "version": "1.42.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
-      "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==",
-      "dev": true
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "mime-types": {
-      "version": "2.1.25",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
-      "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
+    "node_modules/is-shared-array-buffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
+      "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==",
       "dev": true,
-      "requires": {
-        "mime-db": "1.42.0"
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+    "node_modules/is-string": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
+      "dependencies": {
+        "has-tostringtag": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "minimist": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-      "dev": true
-    },
-    "mixin-deep": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+    "node_modules/is-symbol": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
       "dev": true,
-      "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
       "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
+        "has-symbols": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "mkdirp": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+    "node_modules/is-weakref": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
-      "requires": {
-        "minimist": "0.0.8"
-      },
       "dependencies": {
-        "minimist": {
-          "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
-        }
+        "call-bind": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "ms": {
+    "node_modules/isexe": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true
-    },
-    "mute-stdout": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
-      "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
       "dev": true
     },
-    "nan": {
-      "version": "2.12.1",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
-      "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
-      "dev": true,
-      "optional": true
-    },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      }
-    },
-    "next-tick": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+    "node_modules/json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "node-gyp": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
-      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
+    "node_modules/load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
       "dev": true,
-      "requires": {
-        "fstream": "^1.0.0",
-        "glob": "^7.0.3",
+      "dependencies": {
         "graceful-fs": "^4.1.2",
-        "mkdirp": "^0.5.0",
-        "nopt": "2 || 3",
-        "npmlog": "0 || 1 || 2 || 3 || 4",
-        "osenv": "0",
-        "request": "^2.87.0",
-        "rimraf": "2",
-        "semver": "~5.3.0",
-        "tar": "^2.0.0",
-        "which": "1"
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
       },
-      "dependencies": {
-        "semver": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
-          "dev": true
-        }
+      "engines": {
+        "node": ">=4"
       }
     },
-    "node-sass": {
-      "version": "4.13.0",
-      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz",
-      "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==",
+    "node_modules/memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
       "dev": true,
-      "requires": {
-        "async-foreach": "^0.1.3",
-        "chalk": "^1.1.1",
-        "cross-spawn": "^3.0.0",
-        "gaze": "^1.0.0",
-        "get-stdin": "^4.0.1",
-        "glob": "^7.0.3",
-        "in-publish": "^2.0.0",
-        "lodash": "^4.17.15",
-        "meow": "^3.7.0",
-        "mkdirp": "^0.5.1",
-        "nan": "^2.13.2",
-        "node-gyp": "^3.8.0",
-        "npmlog": "^4.0.0",
-        "request": "^2.88.0",
-        "sass-graph": "^2.2.4",
-        "stdout-stream": "^1.4.0",
-        "true-case-path": "^1.0.2"
-      },
-      "dependencies": {
-        "nan": {
-          "version": "2.14.0",
-          "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
-          "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
-          "dev": true
-        }
+      "engines": {
+        "node": ">= 0.10.0"
       }
     },
-    "nopt": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
-      "requires": {
-        "abbrev": "1"
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
       }
     },
-    "normalize-package-data": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+    "node_modules/nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "node_modules/normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
-      "requires": {
+      "dependencies": {
         "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^1.0.0",
+        "resolve": "^1.10.0",
         "semver": "2 || 3 || 4 || 5",
         "validate-npm-package-license": "^3.0.1"
       }
     },
-    "normalize-path": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
       "dev": true,
-      "requires": {
-        "remove-trailing-separator": "^1.0.1"
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "now-and-later": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
-      "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
+    "node_modules/npm-run-all": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
       "dev": true,
-      "requires": {
-        "once": "^1.3.2"
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "chalk": "^2.4.1",
+        "cross-spawn": "^6.0.5",
+        "memorystream": "^0.3.1",
+        "minimatch": "^3.0.4",
+        "pidtree": "^0.3.0",
+        "read-pkg": "^3.0.0",
+        "shell-quote": "^1.6.1",
+        "string.prototype.padend": "^3.0.0"
+      },
+      "bin": {
+        "npm-run-all": "bin/npm-run-all/index.js",
+        "run-p": "bin/run-p/index.js",
+        "run-s": "bin/run-s/index.js"
+      },
+      "engines": {
+        "node": ">= 4"
       }
     },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+    "node_modules/object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
       "dev": true,
-      "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
     },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+    "node_modules/object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
       "dev": true,
-      "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
-      },
       "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "object-keys": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
-      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
-      "dev": true
-    },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+    "node_modules/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
       "dev": true,
-      "requires": {
-        "isobject": "^3.0.0"
+      "dependencies": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "object.assign": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
-      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+    "node_modules/path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
       "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "function-bind": "^1.1.1",
-        "has-symbols": "^1.0.0",
-        "object-keys": "^1.0.11"
+      "engines": {
+        "node": ">=4"
       }
     },
-    "object.defaults": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
-      "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
-      "requires": {
-        "array-each": "^1.0.1",
-        "array-slice": "^1.0.0",
-        "for-own": "^1.0.0",
-        "isobject": "^3.0.0"
+      "dependencies": {
+        "pify": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "object.map": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
-      "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true,
-      "requires": {
-        "for-own": "^1.0.0",
-        "make-iterator": "^1.0.0"
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+    "node_modules/pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
       "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
       }
     },
-    "object.reduce": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
-      "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+    "node_modules/pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
       "dev": true,
-      "requires": {
-        "for-own": "^1.0.0",
-        "make-iterator": "^1.0.0"
+      "engines": {
+        "node": ">=4"
       }
     },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+    "node_modules/read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
       "dev": true,
-      "requires": {
-        "wrappy": "1"
+      "dependencies": {
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "ordered-read-streams": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
-      "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
-      "requires": {
-        "readable-stream": "^2.0.1"
-      },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
       }
     },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
-      "dev": true
-    },
-    "os-locale": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
-      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+    "node_modules/resolve": {
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+      "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
       "dev": true,
-      "requires": {
-        "lcid": "^1.0.0"
+      "dependencies": {
+        "is-core-module": "^2.8.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
-    },
-    "osenv": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+    "node_modules/sass": {
+      "version": "1.49.9",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
+      "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==",
       "dev": true,
-      "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
       }
     },
-    "parse-filepath": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
-      "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+    "node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
       "dev": true,
-      "requires": {
-        "is-absolute": "^1.0.0",
-        "map-cache": "^0.2.0",
-        "path-root": "^0.1.1"
+      "bin": {
+        "semver": "bin/semver"
       }
     },
-    "parse-json": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+    "node_modules/shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
-      "requires": {
-        "error-ex": "^1.2.0"
+      "dependencies": {
+        "shebang-regex": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "parse-node-version": {
+    "node_modules/shebang-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.0.tgz",
-      "integrity": "sha512-02GTVHD1u0nWc20n2G7WX/PgdhNFG04j5fi1OkaJzPWLTcf6vh6229Lta1wTmXG/7Dg42tCssgkccVt7qvd8Kg==",
-      "dev": true
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
     },
-    "parse-passwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+    "node_modules/shell-quote": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
+      "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
       "dev": true
     },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
     },
-    "path-dirname": {
+    "node_modules/source-map-js": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
       "dev": true,
-      "requires": {
-        "pinkie-promise": "^2.0.0"
+      "engines": {
+        "node": ">=0.10.0"
       }
     },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-parse": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
-      "dev": true
-    },
-    "path-root": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
-      "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+    "node_modules/spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
       "dev": true,
-      "requires": {
-        "path-root-regex": "^0.1.0"
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "path-root-regex": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
-      "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+    "node_modules/spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "path-type": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-      "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+    "node_modules/spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
       "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "pify": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-      "dev": true
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
+      "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==",
       "dev": true
     },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+    "node_modules/string.prototype.padend": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz",
+      "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==",
       "dev": true,
-      "requires": {
-        "pinkie": "^2.0.0"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "plugin-error": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
-      "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
+    "node_modules/string.prototype.trimend": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+      "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
       "dev": true,
-      "requires": {
-        "ansi-colors": "^1.0.1",
-        "arr-diff": "^4.0.0",
-        "arr-union": "^3.1.0",
-        "extend-shallow": "^3.0.2"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "pretty-hrtime": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
-      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
-      "dev": true
-    },
-    "process-nextick-args": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
-      "dev": true
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
-      "dev": true
-    },
-    "psl": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz",
-      "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==",
-      "dev": true
-    },
-    "pump": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-      "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+    "node_modules/string.prototype.trimstart": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+      "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
       "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "once": "^1.3.1"
+      "dependencies": {
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "pumpify": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
-      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+    "node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
       "dev": true,
-      "requires": {
-        "duplexify": "^3.6.0",
-        "inherits": "^2.0.3",
-        "pump": "^2.0.0"
+      "engines": {
+        "node": ">=4"
       }
     },
-    "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-      "dev": true
-    },
-    "qs": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
-      "dev": true
-    },
-    "read-pkg": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-      "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+    "node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
-      "requires": {
-        "load-json-file": "^1.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^1.0.0"
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
       }
     },
-    "read-pkg-up": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-      "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true,
-      "requires": {
-        "find-up": "^1.0.0",
-        "read-pkg": "^1.0.0"
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "readable-stream": {
-      "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      },
       "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-          "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-          "dev": true
-        }
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
       }
     },
-    "readdirp": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
-      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+    "node_modules/unbox-primitive": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+      "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
       "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.11",
-        "micromatch": "^3.1.10",
-        "readable-stream": "^2.0.2"
-      },
       "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
+        "function-bind": "^1.1.1",
+        "has-bigints": "^1.0.1",
+        "has-symbols": "^1.0.2",
+        "which-boxed-primitive": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "rechoir": {
-      "version": "0.6.2",
-      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
-      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+    "node_modules/validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
-      "requires": {
-        "resolve": "^1.1.6"
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
       }
     },
-    "redent": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+    "node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
       "dev": true,
-      "requires": {
-        "indent-string": "^2.1.0",
-        "strip-indent": "^1.0.1"
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
       }
     },
-    "regex-not": {
+    "node_modules/which-boxed-primitive": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
       "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
+      "dependencies": {
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
       }
-    },
-    "remove-bom-buffer": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
-      "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+    }
+  },
+  "dependencies": {
+    "ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
       "dev": true,
       "requires": {
-        "is-buffer": "^1.1.5",
-        "is-utf8": "^0.2.1"
+        "color-convert": "^1.9.0"
       }
     },
-    "remove-bom-stream": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
-      "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+    "anymatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+      "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
       "dev": true,
       "requires": {
-        "remove-bom-buffer": "^3.0.0",
-        "safe-buffer": "^5.1.0",
-        "through2": "^2.0.3"
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
       }
     },
-    "remove-trailing-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-      "dev": true
-    },
-    "repeat-element": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
-      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
       "dev": true
     },
-    "repeating": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "requires": {
-        "is-finite": "^1.0.0"
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
       }
     },
-    "replace-ext": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
-      "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
-      "dev": true
-    },
-    "replace-homedir": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
-      "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
       "dev": true,
       "requires": {
-        "homedir-polyfill": "^1.0.1",
-        "is-absolute": "^1.0.0",
-        "remove-trailing-separator": "^1.1.0"
+        "fill-range": "^7.0.1"
       }
     },
-    "request": {
-      "version": "2.88.0",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
-      "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
       "dev": true,
       "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.0",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.4.3",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-          "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
-          "dev": true
-        }
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
       }
     },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
-      "dev": true
-    },
-    "resolve": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
-      "integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
+    "chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
       "dev": true,
       "requires": {
-        "path-parse": "^1.0.6"
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
       }
     },
-    "resolve-dir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
-      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
       "dev": true,
       "requires": {
-        "expand-tilde": "^2.0.0",
-        "global-modules": "^1.0.0"
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
       }
     },
-    "resolve-options": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
-      "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+    "color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
       "dev": true,
       "requires": {
-        "value-or-function": "^3.0.0"
+        "color-name": "1.1.3"
       }
     },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+    "color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
-    "rimraf": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+    "cross-spawn": {
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
       "dev": true,
       "requires": {
-        "glob": "^7.1.3"
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
       }
     },
-    "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-      "dev": true
-    },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
       "dev": true,
       "requires": {
-        "ret": "~0.1.10"
+        "object-keys": "^1.0.12"
       }
     },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
-    "sass-graph": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
-      "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.0",
-        "lodash": "^4.0.0",
-        "scss-tokenizer": "^0.2.3",
-        "yargs": "^7.0.0"
+        "is-arrayish": "^0.2.1"
       }
     },
-    "scss-tokenizer": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-      "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
+    "es-abstract": {
+      "version": "1.19.2",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz",
+      "integrity": "sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==",
       "dev": true,
       "requires": {
-        "js-base64": "^2.1.8",
-        "source-map": "^0.4.2"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
-          "dev": true,
-          "requires": {
-            "amdefine": ">=0.0.4"
-          }
-        }
-      }
-    },
-    "semver": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
-    },
-    "semver-greatest-satisfied-range": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz",
-      "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=",
+        "call-bind": "^1.0.2",
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.1.1",
+        "get-symbol-description": "^1.0.0",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3",
+        "internal-slot": "^1.0.3",
+        "is-callable": "^1.2.4",
+        "is-negative-zero": "^2.0.2",
+        "is-regex": "^1.1.4",
+        "is-shared-array-buffer": "^1.0.1",
+        "is-string": "^1.0.7",
+        "is-weakref": "^1.0.2",
+        "object-inspect": "^1.12.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.2",
+        "string.prototype.trimend": "^1.0.4",
+        "string.prototype.trimstart": "^1.0.4",
+        "unbox-primitive": "^1.0.1"
+      }
+    },
+    "es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
       "dev": true,
       "requires": {
-        "sver-compat": "^1.5.0"
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
       }
     },
-    "set-blocking": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+    "escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
-    "set-value": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
+        "to-regex-range": "^5.0.1"
       }
     },
-    "signal-exit": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
       "dev": true,
       "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
       }
     },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+    "get-symbol-description": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+      "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
       "dev": true,
       "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
+        "call-bind": "^1.0.2",
+        "get-intrinsic": "^1.1.1"
       }
     },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.2.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "is-glob": "^4.0.1"
       }
     },
-    "source-map": {
-      "version": "0.5.7",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+    "graceful-fs": {
+      "version": "4.2.9",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+      "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
       "dev": true
     },
-    "source-map-resolve": {
-      "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
-      "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
       "requires": {
-        "atob": "^2.1.1",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
+        "function-bind": "^1.1.1"
       }
     },
-    "source-map-url": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+    "has-bigints": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+      "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
       "dev": true
     },
-    "sparkles": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz",
-      "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=",
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
-    "spdx-correct": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
-      "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+    "has-symbols": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+      "dev": true
+    },
+    "has-tostringtag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+      "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
       "dev": true,
       "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "has-symbols": "^1.0.2"
       }
     },
-    "spdx-exceptions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
-      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+    "hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
-    "spdx-expression-parse": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+    "immutable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
+      "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+      "dev": true
+    },
+    "internal-slot": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+      "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
       "dev": true,
       "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
+        "get-intrinsic": "^1.1.0",
+        "has": "^1.0.3",
+        "side-channel": "^1.0.4"
       }
     },
-    "spdx-license-ids": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
-      "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+    "is-bigint": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+      "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^3.0.0"
+        "has-bigints": "^1.0.1"
       }
     },
-    "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
-      "dev": true,
-      "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
-      }
-    },
-    "stack-trace": {
-      "version": "0.0.10",
-      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
-      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
-      "dev": true
-    },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
       "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
+        "binary-extensions": "^2.0.0"
       }
     },
-    "stdout-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
-      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
+    "is-boolean-object": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+      "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
       "dev": true,
       "requires": {
-        "readable-stream": "^2.0.1"
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "stream-exhaust": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
-      "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
-      "dev": true
-    },
-    "stream-shift": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
-      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+    "is-callable": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+      "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
       "dev": true
     },
-    "string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+    "is-core-module": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
+      "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
       "dev": true,
       "requires": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
+        "has": "^1.0.3"
       }
     },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+    "is-date-object": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+      "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
       "dev": true,
       "requires": {
-        "safe-buffer": "~5.1.0"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
       "dev": true,
       "requires": {
-        "ansi-regex": "^2.0.0"
+        "is-extglob": "^2.1.1"
       }
     },
-    "strip-bom": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-      "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+    "is-negative-zero": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+      "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+      "dev": true
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-number-object": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
+      "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
       "dev": true,
       "requires": {
-        "is-utf8": "^0.2.0"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "strip-indent": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
-      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+    "is-regex": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+      "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
       "dev": true,
       "requires": {
-        "get-stdin": "^4.0.1"
+        "call-bind": "^1.0.2",
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "supports-color": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-      "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+    "is-shared-array-buffer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
+      "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==",
       "dev": true
     },
-    "sver-compat": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
-      "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=",
+    "is-string": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+      "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
       "dev": true,
       "requires": {
-        "es6-iterator": "^2.0.1",
-        "es6-symbol": "^3.1.1"
+        "has-tostringtag": "^1.0.0"
       }
     },
-    "tar": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
-      "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
+    "is-symbol": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+      "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
       "dev": true,
       "requires": {
-        "block-stream": "*",
-        "fstream": "^1.0.12",
-        "inherits": "2"
+        "has-symbols": "^1.0.2"
       }
     },
-    "through2": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
-      "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+    "is-weakref": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+      "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
       "dev": true,
       "requires": {
-        "readable-stream": "^2.1.5",
-        "xtend": "~4.0.1"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.3",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
-          "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~1.0.6",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.0.3",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-          "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
+        "call-bind": "^1.0.2"
       }
     },
-    "through2-filter": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
-      "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
-      "dev": true,
-      "requires": {
-        "through2": "~2.0.0",
-        "xtend": "~4.0.0"
-      }
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
     },
-    "time-stamp": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
-      "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "to-absolute-glob": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
-      "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
       "dev": true,
       "requires": {
-        "is-absolute": "^1.0.0",
-        "is-negated-glob": "^1.0.0"
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
       }
     },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+    "memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+      "dev": true
+    },
+    "minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "brace-expansion": "^1.1.7"
       }
     },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
       "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
       }
     },
-    "to-regex-range": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "npm-run-all": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
       "dev": true,
       "requires": {
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1"
+        "ansi-styles": "^3.2.1",
+        "chalk": "^2.4.1",
+        "cross-spawn": "^6.0.5",
+        "memorystream": "^0.3.1",
+        "minimatch": "^3.0.4",
+        "pidtree": "^0.3.0",
+        "read-pkg": "^3.0.0",
+        "shell-quote": "^1.6.1",
+        "string.prototype.padend": "^3.0.0"
       }
     },
-    "to-through": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
-      "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+    "object-inspect": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+      "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+      "dev": true
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
       "dev": true,
       "requires": {
-        "through2": "^2.0.3"
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
       }
     },
-    "tough-cookie": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
-      "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
       "dev": true,
       "requires": {
-        "psl": "^1.1.24",
-        "punycode": "^1.4.1"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true
-        }
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
       }
     },
-    "trim-newlines": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
-      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
       "dev": true
     },
-    "true-case-path": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
-      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.2"
-      }
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
     },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "pify": "^3.0.0"
       }
     },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
       "dev": true
     },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+    "pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
       "dev": true
     },
-    "unc-path-regex": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
-      "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
       "dev": true
     },
-    "undertaker": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz",
-      "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==",
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
       "dev": true,
       "requires": {
-        "arr-flatten": "^1.0.1",
-        "arr-map": "^2.0.0",
-        "bach": "^1.0.0",
-        "collection-map": "^1.0.0",
-        "es6-weak-map": "^2.0.1",
-        "last-run": "^1.1.0",
-        "object.defaults": "^1.0.0",
-        "object.reduce": "^1.0.0",
-        "undertaker-registry": "^1.0.0"
-      }
-    },
-    "undertaker-registry": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
-      "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
-      "dev": true
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      }
     },
-    "union-value": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^2.0.1"
+        "picomatch": "^2.2.1"
       }
     },
-    "unique-stream": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
-      "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+    "resolve": {
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+      "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
       "dev": true,
       "requires": {
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "through2-filter": "^3.0.0"
+        "is-core-module": "^2.8.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
       }
     },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+    "sass": {
+      "version": "1.49.9",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
+      "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
-      },
-      "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
-          "dev": true,
-          "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
-          },
-          "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-              "dev": true,
-              "requires": {
-                "isarray": "1.0.0"
-              }
-            }
-          }
-        },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        }
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
       }
     },
-    "upath": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
-      "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
       "dev": true
     },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
-        "punycode": "^2.1.0"
+        "shebang-regex": "^1.0.0"
       }
     },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
-      "dev": true
-    },
-    "use": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
-      "dev": true
-    },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
-    "uuid": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
-      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
+    "shell-quote": {
+      "version": "1.7.3",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz",
+      "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
       "dev": true
     },
-    "v8flags": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz",
-      "integrity": "sha512-MtivA7GF24yMPte9Rp/BWGCYQNaUj86zeYxV/x2RRJMKagImbbv3u8iJC57lNhWLPcGLJmHcHmFWkNsplbbLWw==",
+    "side-channel": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+      "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
       "dev": true,
       "requires": {
-        "homedir-polyfill": "^1.0.1"
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
       }
     },
-    "validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "dev": true
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
       "dev": true,
       "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "value-or-function": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
-      "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "vinyl-fs": {
-      "version": "3.0.3",
-      "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
-      "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+    "spdx-license-ids": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
+      "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==",
+      "dev": true
+    },
+    "string.prototype.padend": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz",
+      "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==",
       "dev": true,
       "requires": {
-        "fs-mkdirp-stream": "^1.0.0",
-        "glob-stream": "^6.1.0",
-        "graceful-fs": "^4.0.0",
-        "is-valid-glob": "^1.0.0",
-        "lazystream": "^1.0.0",
-        "lead": "^1.0.0",
-        "object.assign": "^4.0.4",
-        "pumpify": "^1.3.5",
-        "readable-stream": "^2.3.3",
-        "remove-bom-buffer": "^3.0.0",
-        "remove-bom-stream": "^1.2.0",
-        "resolve-options": "^1.1.0",
-        "through2": "^2.0.0",
-        "to-through": "^2.0.0",
-        "value-or-function": "^3.0.0",
-        "vinyl": "^2.0.0",
-        "vinyl-sourcemap": "^1.1.0"
-      },
-      "dependencies": {
-        "clone": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-          "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
-          "dev": true
-        },
-        "clone-stats": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
-          "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
-          "dev": true
-        },
-        "isarray": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-          "dev": true
-        },
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "replace-ext": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
-          "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
-          "dev": true
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        },
-        "vinyl": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz",
-          "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==",
-          "dev": true,
-          "requires": {
-            "clone": "^2.1.1",
-            "clone-buffer": "^1.0.0",
-            "clone-stats": "^1.0.0",
-            "cloneable-readable": "^1.0.0",
-            "remove-trailing-separator": "^1.0.1",
-            "replace-ext": "^1.0.0"
-          }
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.19.1"
       }
     },
-    "vinyl-sourcemap": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
-      "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+    "string.prototype.trimend": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+      "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
       "dev": true,
       "requires": {
-        "append-buffer": "^1.0.2",
-        "convert-source-map": "^1.5.0",
-        "graceful-fs": "^4.1.6",
-        "normalize-path": "^2.1.1",
-        "now-and-later": "^2.0.0",
-        "remove-bom-buffer": "^3.0.0",
-        "vinyl": "^2.0.0"
-      },
-      "dependencies": {
-        "clone": {
-          "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
-          "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
-          "dev": true
-        },
-        "clone-stats": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz",
-          "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=",
-          "dev": true
-        },
-        "replace-ext": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz",
-          "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=",
-          "dev": true
-        },
-        "vinyl": {
-          "version": "2.2.0",
-          "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz",
-          "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==",
-          "dev": true,
-          "requires": {
-            "clone": "^2.1.1",
-            "clone-buffer": "^1.0.0",
-            "clone-stats": "^1.0.0",
-            "cloneable-readable": "^1.0.0",
-            "remove-trailing-separator": "^1.0.1",
-            "replace-ext": "^1.0.0"
-          }
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
       }
     },
-    "vinyl-sourcemaps-apply": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz",
-      "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=",
+    "string.prototype.trimstart": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+      "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
       "dev": true,
       "requires": {
-        "source-map": "^0.5.1"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        }
+        "call-bind": "^1.0.2",
+        "define-properties": "^1.1.3"
       }
     },
-    "which": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "requires": {
-        "isexe": "^2.0.0"
+        "has-flag": "^3.0.0"
       }
     },
-    "which-module": {
+    "supports-preserve-symlinks-flag": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
-      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
       "dev": true
     },
-    "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.2 || 2"
+        "is-number": "^7.0.0"
       }
     },
-    "wrap-ansi": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
-      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+    "unbox-primitive": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+      "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1"
+        "function-bind": "^1.1.1",
+        "has-bigints": "^1.0.1",
+        "has-symbols": "^1.0.2",
+        "which-boxed-primitive": "^1.0.2"
       }
     },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
-    "xtend": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
-      "dev": true
-    },
-    "y18n": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-      "dev": true
-    },
-    "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
-      "dev": true
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
     },
-    "yargs": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
-      "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0",
-        "cliui": "^3.2.0",
-        "decamelize": "^1.1.1",
-        "get-caller-file": "^1.0.1",
-        "os-locale": "^1.4.0",
-        "read-pkg-up": "^1.0.1",
-        "require-directory": "^2.1.1",
-        "require-main-filename": "^1.0.1",
-        "set-blocking": "^2.0.0",
-        "string-width": "^1.0.2",
-        "which-module": "^1.0.0",
-        "y18n": "^3.2.1",
-        "yargs-parser": "^5.0.0"
-      }
-    },
-    "yargs-parser": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
-      "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-boxed-primitive": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+      "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0"
+        "is-bigint": "^1.0.1",
+        "is-boolean-object": "^1.1.0",
+        "is-number-object": "^1.0.4",
+        "is-string": "^1.0.5",
+        "is-symbol": "^1.0.3"
       }
     }
   }
index 6275ac3501786f1c5feddf4bf78f060b89f1d763..ca8b073cb8ff085aa8750b89edde34ec75828ba7 100644 (file)
@@ -1,22 +1,25 @@
 {
-  "name": "BookStack-Site",
+  "name": "bookstack-site",
   "version": "1.0.0",
   "description": "The front-end site for bookstack",
   "main": "gulpfile.js",
   "scripts": {
-    "build": "gulp && hugo",
-    "serve": "hugo serve -DF",
-    "dev": "gulp watch",
+    "build:css:prod": "sass ./themes/bookstack/sass:./themes/bookstack/static/css -s compressed",
+    "build:css:dev": "sass ./themes/bookstack/sass:./themes/bookstack/static/css",
+    "build:css:watch": "sass ./themes/bookstack/sass:./themes/bookstack/static/css --watch",
+    "build:hugo:prod": "hugo",
+    "build:hugo:watch": "hugo serve -DF",
+    "build": "npm-run-all --sequential build:css:prod build:hugo:prod",
+    "serve": "npm-run-all build:hugo:watch",
+    "dev": "npm-run-all --parallel build:hugo:watch build:css:watch",
+    "deploy:server": "rsync -avx --delete --exclude '.git/' --exclude 'node_modules/' ./ sandcat:/var/www/bookstackapp.com/",
+    "deploy": "npm-run-all --sequential build:css:prod build:hugo:prod deploy:server",
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "author": "Dan Brown",
   "license": "MIT",
   "devDependencies": {
-    "gulp": "4.0.1",
-    "gulp-clean-css": "^4.2.0",
-    "gulp-plumber": "1.2.1",
-    "gulp-rename": "1.4.0",
-    "gulp-sass": "^4.0.2"
-  },
-  "dependencies": {}
+    "npm-run-all": "^4.1.5",
+    "sass": "^1.49.9"
+  }
 }
index f472f223002e6f98336b374b94518a8e7e1514fa..ff11b9cd73fc38a96c1bfb79eac64e9fcaea796a 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -14,4 +14,4 @@ This site is built using [Hugo](https://gohugo.io). Images are stored using `git
 
 The theme is custom made with snippets taken from the [hugo capser theme](https://github.com/vjeantet/hugo-theme-casper).
 
-SCSS is used for the styling and is built using gulp. Install NPM dependencies via `npm install` or `yarn` then you can use `npm run-script build` to build the css once or `npm run-script dev` to watch for changes.
+SCSS is used for the styling. Install NPM dependencies via `npm install` or `yarn` then you can use `npm run build` to build the css and site once or `npm run dev` to watch for changes.
diff --git a/static/BingSiteAuth.xml b/static/BingSiteAuth.xml
new file mode 100644 (file)
index 0000000..6047cf4
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<users>
+       <user>8701BA9456F7F02A4AF86C3647EBD360</user>
+</users>
\ No newline at end of file
index ee24ab1a9010298e3f990853b02e761b157b2fd8..802f40a3cf061e46a8c5924783ba62dffba2fdab 100644 (file)
Binary files a/static/images/2017/01/bookstack-includes-popover.webm and b/static/images/2017/01/bookstack-includes-popover.webm differ
diff --git a/static/images/2020/07/bookstack_website_2016_2020.png b/static/images/2020/07/bookstack_website_2016_2020.png
new file mode 100644 (file)
index 0000000..e223135
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b489c0ee31b5af50196d8a8d9eb282af16e1ef0b9e85932225387836f482490
+size 10394
diff --git a/static/images/2020/09/api_chapters.png b/static/images/2020/09/api_chapters.png
new file mode 100644 (file)
index 0000000..1533e0a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3074389ad4180f985fbfd23d36c6d4eeec4535094a63a1e808f8139c5d345b5d
+size 6700
diff --git a/static/images/2020/09/attachment_link_insert.png b/static/images/2020/09/attachment_link_insert.png
new file mode 100644 (file)
index 0000000..f3536fd
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bc574c73e147fce66b2088ef1f0092bc2253a77628716883f17f6942cb7607cb
+size 24812
diff --git a/static/images/2020/09/bookstack_audit_log.png b/static/images/2020/09/bookstack_audit_log.png
new file mode 100644 (file)
index 0000000..f382ae2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bd1720ef2acb708e8908d64edbba8424395f1fc766e2895de1b114d1dc96aada
+size 87085
diff --git a/static/images/2020/09/code_block_session_saving.png b/static/images/2020/09/code_block_session_saving.png
new file mode 100644 (file)
index 0000000..d6e7f6c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cce92f30d4d4fea7401f688ed1ef1b6b2c5cdfb6caa19a4072f077c16e5fa001
+size 22044
diff --git a/static/images/2021/01/audit_log_updates.png b/static/images/2021/01/audit_log_updates.png
new file mode 100644 (file)
index 0000000..d526e4e
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:81be5781c6f96583efedfa1f36b12c430bc20e806bf115840d21fce8c40b2eaf
+size 66858
diff --git a/static/images/2021/01/bookstack-permalink.mp4 b/static/images/2021/01/bookstack-permalink.mp4
new file mode 100644 (file)
index 0000000..c07dd31
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a6dbe2f9a662a0a406507c75776bb77a391a8c7f2ef08a9123ef49ee0e232ab
+size 778953
diff --git a/static/images/2021/01/bookstack-permalink.webm b/static/images/2021/01/bookstack-permalink.webm
new file mode 100644 (file)
index 0000000..42a9239
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:553c0f5e026c00cff43460aa577362408a095e6463923906b0861fcb53f1a0a1
+size 944666
diff --git a/static/images/2021/01/changes_view.png b/static/images/2021/01/changes_view.png
new file mode 100644 (file)
index 0000000..339b284
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f920cda591090e22e01eb138186e3e6d668e85987f03aebe25e7f04839b51e6e
+size 60215
diff --git a/static/images/2021/01/mailbag.png b/static/images/2021/01/mailbag.png
new file mode 100644 (file)
index 0000000..e07fc82
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:edc9d8b00d00d770a1fb41bf03b81bd44c65572d785d851ecb8d015ca6a20dc7
+size 117709
diff --git a/static/images/2021/01/mailchimp_tracked_links.png b/static/images/2021/01/mailchimp_tracked_links.png
new file mode 100644 (file)
index 0000000..6c5c8c3
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e844448bb21d0021e721561481b5dcb5a01517875bf57f18779efd5e1ad68206
+size 37562
diff --git a/static/images/2021/01/ownership_change.png b/static/images/2021/01/ownership_change.png
new file mode 100644 (file)
index 0000000..71f8176
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:74fdf57bbb8104976799f5ae0ad3b9c8284329d190342da23250699dff951f6d
+size 38896
diff --git a/static/images/2021/01/pages_api.png b/static/images/2021/01/pages_api.png
new file mode 100644 (file)
index 0000000..c015d1f
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9e6d22fe1de9703f988bfa747569548d228a53920861ab3b4f2e71455d0b6526
+size 79271
diff --git a/static/images/2021/01/plausible_bookstack_site_analytics.png b/static/images/2021/01/plausible_bookstack_site_analytics.png
new file mode 100644 (file)
index 0000000..5ce180d
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:252c0cb69f6594242a07708de8d100d45b450c9603048598a1da6c757f9a2cd7
+size 89077
diff --git a/static/images/2021/01/recycle_bin.png b/static/images/2021/01/recycle_bin.png
new file mode 100644 (file)
index 0000000..98c87c1
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85922b6943d2cd00f1922edad5e85688a31c55cbed4ad8c1c978663571c40505
+size 77206
diff --git a/static/images/2021/01/recycle_bin_maintenance.png b/static/images/2021/01/recycle_bin_maintenance.png
new file mode 100644 (file)
index 0000000..99afe29
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1b23fc92b54ec1d0672bf2c9dd4650970c1aaeabaf5d0f888928db9104508812
+size 49380
diff --git a/static/images/2021/01/user_list_activity.png b/static/images/2021/01/user_list_activity.png
new file mode 100644 (file)
index 0000000..807fbc4
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1da8eaaafc29e4f8037f52d3196c17f5f7e44fbfe39865c5523625c948e1ebc8
+size 56468
diff --git a/static/images/2021/04/audit_log_filter.png b/static/images/2021/04/audit_log_filter.png
new file mode 100644 (file)
index 0000000..a9aa033
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4213537c657907522248cd324d0586671aa0f1b88bf392361ebecda9e23668da
+size 42352
diff --git a/static/images/2021/04/footer_display.png b/static/images/2021/04/footer_display.png
new file mode 100644 (file)
index 0000000..6a8bfd9
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7565852a9d16a5bee46eef68a94ec8c230b37341df000c52be0628efb244b4a0
+size 22305
diff --git a/static/images/2021/04/footer_settings.png b/static/images/2021/04/footer_settings.png
new file mode 100644 (file)
index 0000000..712ce08
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f28a4d87068e4f6763c8348e626313f719a1c30a1eb65a8b0f79c3ae077fafb
+size 16567
diff --git a/static/images/2021/04/search_owned_by.png b/static/images/2021/04/search_owned_by.png
new file mode 100644 (file)
index 0000000..fd5d16c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0974db7713e022f03ff1b92596ff24ab19412b513ead72b309c5fb1d9d195d93
+size 8232
diff --git a/static/images/2021/04/shelf_sort.png b/static/images/2021/04/shelf_sort.png
new file mode 100644 (file)
index 0000000..26a3f47
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54239721269124c0437ea85fb518d3d6246791108844ee35ea1f08939e620a72
+size 75738
diff --git a/static/images/2021/05/dark-mode-header-2104.png b/static/images/2021/05/dark-mode-header-2104.png
new file mode 100644 (file)
index 0000000..ac6ec72
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa25a09fcfb2eb156ac3b16f0539b26238f8d6c12a0dd0f4f31685250367c7f0
+size 4889
diff --git a/static/images/2021/05/dark-mode-header-2105.png b/static/images/2021/05/dark-mode-header-2105.png
new file mode 100644 (file)
index 0000000..2c30b75
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:91f70373a8259be2d75080a47ac688a0c5bc8afdea33a8be069f4a4b8ae8f178
+size 4899
diff --git a/static/images/2021/05/favourites-action.png b/static/images/2021/05/favourites-action.png
new file mode 100644 (file)
index 0000000..245b516
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:80a0acfeeef2e68df5f48cf2af661cae312c1fc9cf83b6979f5a27da9c1518d8
+size 3974
diff --git a/static/images/2021/05/favourites-home-list.png b/static/images/2021/05/favourites-home-list.png
new file mode 100644 (file)
index 0000000..0dc00cf
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b0a9ded657724a473f7b3cf02e98eb496be0c2f5e4702972efc880f91186e1ce
+size 28777
diff --git a/static/images/2021/05/favourites-profile-menu.png b/static/images/2021/05/favourites-profile-menu.png
new file mode 100644 (file)
index 0000000..8458ced
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:862acc801d205d9a485a2052ad449a5789f2e5293d441fe03153e5fec758b143
+size 8737
diff --git a/static/images/2021/05/header-a11y.png b/static/images/2021/05/header-a11y.png
new file mode 100644 (file)
index 0000000..d564035
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b6be57eeb3635226f1ee319dcd64bad09002c8a5da1e113cd4e90de17e74b29b
+size 44549
diff --git a/static/images/2021/05/next-prev-nav.png b/static/images/2021/05/next-prev-nav.png
new file mode 100644 (file)
index 0000000..4fe3cd2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:662416f146f52913809c268c6a51898ec4bef23aba08af785bff45f3cf445b4d
+size 51019
diff --git a/static/images/2021/05/tags-in-search.png b/static/images/2021/05/tags-in-search.png
new file mode 100644 (file)
index 0000000..ae92051
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1022839178377d95644d00767a69c9bdf94e0a2a0230a0e36ed95c4db503f1a6
+size 27382
diff --git a/static/images/2021/08/export-options-with-markdown.png b/static/images/2021/08/export-options-with-markdown.png
new file mode 100644 (file)
index 0000000..8ddd17d
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9179871beca56fc41b9999dec5390a613ccc0f9cbc735049ad41c90c5aefdf6
+size 8676
diff --git a/static/images/2021/08/mfa-role-permission.png b/static/images/2021/08/mfa-role-permission.png
new file mode 100644 (file)
index 0000000..285ab18
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa9e4f592767aeff6fa3744fd9f01ef41d0e6f7f64be0147b1330dcdf1e5dde4
+size 10105
diff --git a/static/images/2021/08/mfa-verify-view.png b/static/images/2021/08/mfa-verify-view.png
new file mode 100644 (file)
index 0000000..ddacc2b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d9bc386893db4f15f6a943e22e906569dbd2b78b1b9a0593b07470204d8713bb
+size 37003
diff --git a/static/images/2021/08/non-download-attachment-link.png b/static/images/2021/08/non-download-attachment-link.png
new file mode 100644 (file)
index 0000000..069ed8d
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:20dc1faf297b2a996a19f98d3428a0530b4776b7df098e671652f28dbe48c903
+size 18324
diff --git a/static/images/2021/08/skip-to-content-link.png b/static/images/2021/08/skip-to-content-link.png
new file mode 100644 (file)
index 0000000..aa9d81b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c436ef8bcee5ffd417a26ad077c18a49642b9fb765322de889d1a3adff09d4bd
+size 12246
diff --git a/static/images/2021/10/attachment-api-endpoints.png b/static/images/2021/10/attachment-api-endpoints.png
new file mode 100644 (file)
index 0000000..c36b805
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:333bd9a55c76c8462eb1605522b08c8de43312be4eefedeafe03210b7d42ebbb
+size 62479
diff --git a/static/images/2021/10/audit-log-ip.png b/static/images/2021/10/audit-log-ip.png
new file mode 100644 (file)
index 0000000..1f01fd2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:78efde4e4dfed5d1df7a91ba5d7d16ce296afcc651c1dcf1f0f1a0c5927ce8ee
+size 38374
diff --git a/static/images/2021/10/code-climate.png b/static/images/2021/10/code-climate.png
new file mode 100644 (file)
index 0000000..b9afc92
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4125aa41c672a5e76500efaabb9c0feec44e79edf53c6e0ccd0ee8b89d292bf6
+size 23124
diff --git a/static/images/2021/10/crowdin.png b/static/images/2021/10/crowdin.png
new file mode 100644 (file)
index 0000000..cab0fa5
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:766545c33857880e42adca1ff81a7a5f0f8d3fc426da8e2bd00ae486a36c01ab
+size 28292
diff --git a/static/images/2021/10/debug-view.png b/static/images/2021/10/debug-view.png
new file mode 100644 (file)
index 0000000..b8b902c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2663ae5e2c2fe5af98ead3bdf6df5a2a308233f483b143eeebbe729634b91321
+size 65845
diff --git a/static/images/2021/10/discord.png b/static/images/2021/10/discord.png
new file mode 100644 (file)
index 0000000..959a500
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab008e03f50c0cd95383997c8c56ecb8d81a324cceb2f8ff626b511b4325c6df
+size 53195
diff --git a/static/images/2021/10/docsearch.png b/static/images/2021/10/docsearch.png
new file mode 100644 (file)
index 0000000..4723e37
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ba61bd7338f912088daf38912db201d8551e03b5e35b6cfa10ba69d24d825ce3
+size 118383
diff --git a/static/images/2021/10/editor-conflicts.png b/static/images/2021/10/editor-conflicts.png
new file mode 100644 (file)
index 0000000..983288a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a4079783f519ae55e100bd2106effe2035b5b68f503e7c3f60d531332b49ff84
+size 28109
diff --git a/static/images/2021/10/github.png b/static/images/2021/10/github.png
new file mode 100644 (file)
index 0000000..26f6529
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d9be553a243bbb19b3bd7153569432366fb6f15d7886dd760707f5257342382
+size 49079
diff --git a/static/images/2021/10/gohugo.png b/static/images/2021/10/gohugo.png
new file mode 100644 (file)
index 0000000..be131ce
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fbcabada6c6a5cae5f3a5bd619cb498f1cd3ec16d8fa6be6f19114dd81fdd198
+size 21043
diff --git a/static/images/2021/10/mailbag.png b/static/images/2021/10/mailbag.png
new file mode 100644 (file)
index 0000000..b3977fc
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:612004650ade65805ce2682feb6aa3376da29e51acce14838ddb9a907e70e6b6
+size 22467
diff --git a/static/images/2021/10/oidc.png b/static/images/2021/10/oidc.png
new file mode 100644 (file)
index 0000000..8e700c2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:19f580cb4ee8310d8fc1807a7e7fa3362ff08257b7675b2722ebe55e9f01ee8c
+size 10427
diff --git a/static/images/2021/10/ovh.png b/static/images/2021/10/ovh.png
new file mode 100644 (file)
index 0000000..21f0487
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e7cf2273fe8c08a575b67070fadecf86835e5d8b76110381b5e1b0b2e5f52ef
+size 65092
diff --git a/static/images/2021/10/plausible.png b/static/images/2021/10/plausible.png
new file mode 100644 (file)
index 0000000..ba01d9c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:51b6ea7afff628da1191a3e99449bdbd7161c1f1ad4fc796fd45ba727d63240d
+size 25155
diff --git a/static/images/2021/10/styleci.png b/static/images/2021/10/styleci.png
new file mode 100644 (file)
index 0000000..ccf26ae
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d6e41c894708dedfb9ec17f104720a84aa315399bb094ec0f21488bb768a6335
+size 21735
diff --git a/static/images/2021/10/totp-url.png b/static/images/2021/10/totp-url.png
new file mode 100644 (file)
index 0000000..0d656c3
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0ee16a2ff2ebac89ebade4ca44712c694642b8067401a7101977efc3025c8708
+size 41388
diff --git a/static/images/2021/11/search-api.png b/static/images/2021/11/search-api.png
new file mode 100644 (file)
index 0000000..b089b63
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9c462218f60a696808420e80e021236712cc4e73835e6ca6f2939f1c00e54333
+size 69212
diff --git a/static/images/2021/11/search-results.png b/static/images/2021/11/search-results.png
new file mode 100644 (file)
index 0000000..c74ac57
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea17fc80bef96b871bb585f82a06d72df3bc502315c3776ca5a29018318223cc
+size 71066
diff --git a/static/images/2021/11/tags-page.png b/static/images/2021/11/tags-page.png
new file mode 100644 (file)
index 0000000..8ebd049
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85013c6e0c5f4f2abe9c3716b8c839ff9bb1d2d663dab684247c523d589fe37f
+size 31732
diff --git a/static/images/2021/12/2021_development_rate.png b/static/images/2021/12/2021_development_rate.png
new file mode 100644 (file)
index 0000000..59fb293
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07c43404590fcc3c25f3ec8a5ecb1b2aba4f34c1dcb67ea4ada6e74f5fdede36
+size 8934
diff --git a/static/images/2021/12/audit_log_search.png b/static/images/2021/12/audit_log_search.png
new file mode 100644 (file)
index 0000000..cd6c438
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2056ad446986c4676641b23dbcc2a232b36ac0794fab529c9db93874b5d67660
+size 37600
diff --git a/static/images/2021/12/company_sponsors.png b/static/images/2021/12/company_sponsors.png
new file mode 100644 (file)
index 0000000..27a2979
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1d1cb00296ca4a873661b1f451f159f34f005379c16c6e682591aad656defa8b
+size 55237
diff --git a/static/images/2021/12/copy_book.png b/static/images/2021/12/copy_book.png
new file mode 100644 (file)
index 0000000..7cc0c06
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fc6eb1b4cd7f916891503545d46e033f0d8b474343fc979f7f319c8ba7c193e6
+size 36133
diff --git a/static/images/2021/12/copy_role.png b/static/images/2021/12/copy_role.png
new file mode 100644 (file)
index 0000000..41e338b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f4fdfb46db2b1f6712ece2b63645a33ea7502198b7bb8c1688eb7f4ca929bb37
+size 16811
diff --git a/static/images/2021/12/site_stats.png b/static/images/2021/12/site_stats.png
new file mode 100644 (file)
index 0000000..662d740
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9ca157fc3f7bd39b18843376dd315239442346caca31064ef1b4eb62f0ad95b6
+size 74323
diff --git a/static/images/2021/12/sponsor_progress.png b/static/images/2021/12/sponsor_progress.png
new file mode 100644 (file)
index 0000000..0322255
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:858a066713e8944cd74066e24328b1962c2673c42222e88962ffb0444e78391c
+size 21119
diff --git a/static/images/2021/12/webhook_form.png b/static/images/2021/12/webhook_form.png
new file mode 100644 (file)
index 0000000..870d9b4
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a6dab6dc875237992f3406b748b08d62cdde590a93b9db8a00e73005f0fb657f
+size 47850
diff --git a/static/images/2021/12/webhooks_list.png b/static/images/2021/12/webhooks_list.png
new file mode 100644 (file)
index 0000000..d0c7549
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6febc43feede386a14acb2cca65c7721e1cd8119076e278c9b4e3e9187b67463
+size 30570
diff --git a/static/images/2022/02/api-user-endpoints.png b/static/images/2022/02/api-user-endpoints.png
new file mode 100644 (file)
index 0000000..09c09cf
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:73cc4b5b79defa720ecf1df9b258273e094871257139d48f23ad1839471ff189
+size 33975
diff --git a/static/images/2022/02/collapse-blocks-qa.png b/static/images/2022/02/collapse-blocks-qa.png
new file mode 100644 (file)
index 0000000..53b010d
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45db82f614395eb6cf28abe7ba0f95e52e4112b776009cd6c0a1aeec6c47e4dd
+size 35046
diff --git a/static/images/2022/02/collapsible-blocks.mp4 b/static/images/2022/02/collapsible-blocks.mp4
new file mode 100644 (file)
index 0000000..345993a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6cfa42f22ec9db718102e4c33c75e132f044310e5cfbcee6f64301471d8569a5
+size 3088274
diff --git a/static/images/2022/02/community-videos.png b/static/images/2022/02/community-videos.png
new file mode 100644 (file)
index 0000000..9029134
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3ab4d969a90503850593d9c5483116bf80d674d5beece89a274cd37407633a8d
+size 63925
diff --git a/static/images/2022/02/crowdin.png b/static/images/2022/02/crowdin.png
new file mode 100644 (file)
index 0000000..758c712
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:74c7e269705b7fd4aaeabe6c05dce5af052a421b1cded953c756e134f94d3463
+size 46636
diff --git a/static/images/2022/02/editor-toolbar.png b/static/images/2022/02/editor-toolbar.png
new file mode 100644 (file)
index 0000000..5cb791a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d5d9b2a3e950233f94c629330f5e917a4428091c4cf8f7eaaf195d779310dc05
+size 5961
diff --git a/static/images/2022/02/editor-translations.png b/static/images/2022/02/editor-translations.png
new file mode 100644 (file)
index 0000000..4a87f81
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f96aabddb3d735819eab416d8a07d85c69de63123d89d193b7b07e6b549c6c4b
+size 24992
diff --git a/static/images/2022/02/github-sponsors.png b/static/images/2022/02/github-sponsors.png
new file mode 100644 (file)
index 0000000..8dc1750
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:40c4fc951a174301fca61a1726ad02ecb66f417928b084a47d56f9319bdf6e4f
+size 108492
diff --git a/static/images/2022/02/huntr-dev.png b/static/images/2022/02/huntr-dev.png
new file mode 100644 (file)
index 0000000..659d7ac
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26714404f201c7f92d51e276d6b5eb855782c2fffcd95bfa6c7b08ef991a0293
+size 65204
diff --git a/static/images/2022/02/recently-updated-enhancements.png b/static/images/2022/02/recently-updated-enhancements.png
new file mode 100644 (file)
index 0000000..2393b12
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e227d71e9ad7d6fe8430c901e630f7a466d683db9b98d626e14c252537ee6095
+size 32442
diff --git a/static/images/2022/02/user-create-language.png b/static/images/2022/02/user-create-language.png
new file mode 100644 (file)
index 0000000..a1aaca8
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d57b6e33ebf2ea4aca6d35cc4ffd16fc1da498031be1cf38b2c60c4a82ef0e4b
+size 18698
diff --git a/static/images/2022/02/webhook-debug-help.png b/static/images/2022/02/webhook-debug-help.png
new file mode 100644 (file)
index 0000000..5ec5ac3
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b08f547e052428ba454d4cb6f76cad7b5d0261760f0a850d2f3672755d96d386
+size 17609
diff --git a/static/images/2022/03/link_selector.png b/static/images/2022/03/link_selector.png
new file mode 100644 (file)
index 0000000..86f226a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97f58f397967db2b03726f98dc8d2255dff16b9dd1389de7ec9abc838f604af3
+size 41500
diff --git a/static/images/2022/03/link_toolbar.png b/static/images/2022/03/link_toolbar.png
new file mode 100644 (file)
index 0000000..eead3e8
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ee1c8ff6906b245788f2c099268bc0f1fd970044eb3b4561968a4ad381d0809b
+size 23725
diff --git a/static/images/2022/03/settings_view.png b/static/images/2022/03/settings_view.png
new file mode 100644 (file)
index 0000000..72ce314
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:acbb9b4073c446fce57fe6467f212cc4eb38a7ec941a7d9249c2194d2f9d5569
+size 41949
diff --git a/static/images/2022/03/task_lists.png b/static/images/2022/03/task_lists.png
new file mode 100644 (file)
index 0000000..e67ce35
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52128891092b18b2f26b7975a8225816386f00ca74c38d7f402ed4e608ddcc44
+size 32403
diff --git a/static/images/2022/04/bookstack_editor_switch.mp4 b/static/images/2022/04/bookstack_editor_switch.mp4
new file mode 100644 (file)
index 0000000..106d6e2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b530a595c1d1d5d2ca3ce50fb0060ea57724dfa554da582d201fe4b842c9e6eb
+size 737100
diff --git a/static/images/2022/04/bookstack_traffic_hn_6m.png b/static/images/2022/04/bookstack_traffic_hn_6m.png
new file mode 100644 (file)
index 0000000..3af601d
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6e1918466b0b110666429b362c4cc293b54e6792fa0bbd2e86afffe67bf7cce4
+size 14094
diff --git a/static/images/2022/04/bookstack_traffic_hn_week.png b/static/images/2022/04/bookstack_traffic_hn_week.png
new file mode 100644 (file)
index 0000000..b694e47
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:645f7730e666c02b9c90c5b40aed9bc5b2353974b61877254e9722af9e4d1ca8
+size 16194
diff --git a/static/images/2022/04/default_editor_option.png b/static/images/2022/04/default_editor_option.png
new file mode 100644 (file)
index 0000000..f0c8ad6
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72aff9c7a59f1a8cb8a5b28be6ef9a81e3a0efc15fc3776caaaccd19b6da9348
+size 9586
diff --git a/static/images/2022/04/edit_switch_warning.png b/static/images/2022/04/edit_switch_warning.png
new file mode 100644 (file)
index 0000000..d352b25
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7a088e877f24583b1fa2996b3f1aee048c556fd6df145c6808d76ba89d75e4ef
+size 31036
diff --git a/static/images/2022/04/editor_switch_dropdown.png b/static/images/2022/04/editor_switch_dropdown.png
new file mode 100644 (file)
index 0000000..b893d25
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af0693e0318647a68c023c1c1355f74cbda85d63400636a83bbc5b8077f45883
+size 15045
diff --git a/static/images/2022/04/recycle_bin_endpoints.png b/static/images/2022/04/recycle_bin_endpoints.png
new file mode 100644 (file)
index 0000000..bffce13
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:63a4aac0f15817da3d05d4690faee54db5391eb7a627e09fcec2f5b1a2611f08
+size 71095
diff --git a/static/images/2022/04/star_growth.png b/static/images/2022/04/star_growth.png
new file mode 100644 (file)
index 0000000..898fa0b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b1265102678e1d1f49c9d1ea4fc21dd84920fe569c75550c05081204846baa58
+size 45472
diff --git a/static/images/2022/04/stars_per_month.png b/static/images/2022/04/stars_per_month.png
new file mode 100644 (file)
index 0000000..5ec54db
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:46684d96c4729378374d2b3bf6e81de1c93ef3629b3596ec26a33ca99a62a25c
+size 43041
diff --git a/static/images/about/books-view.png b/static/images/about/books-view.png
new file mode 100644 (file)
index 0000000..5431aeb
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:04b4b25fbbc5dd3d2331a026286dc53635dd77814f6e55cd6d824469712ed0bf
+size 108881
diff --git a/static/images/blog-cover-images/autumn-road-sebastian_unrau.jpg b/static/images/blog-cover-images/autumn-road-sebastian_unrau.jpg
new file mode 100644 (file)
index 0000000..afff63c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0b496c3f553200004243623ca8394c2d1992b490786177c64969ffb1e7a49742
+size 367014
diff --git a/static/images/blog-cover-images/blossom-tegethoff.jpg b/static/images/blog-cover-images/blossom-tegethoff.jpg
new file mode 100644 (file)
index 0000000..aad7f5e
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:735ab985f0af8d765c0f9bbcdd3720528f34133171c5688b60eb6b6260f96ac5
+size 336990
diff --git a/static/images/blog-cover-images/book-collection-fredmarriage.jpg b/static/images/blog-cover-images/book-collection-fredmarriage.jpg
new file mode 100644 (file)
index 0000000..5877db5
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6423e3dcc3a2403fafc49f25e48be41a8ed0f892f4ed1136b4f8399899670dc1
+size 177848
diff --git a/static/images/blog-cover-images/cat-pounce-dorothea-oldani.jpg b/static/images/blog-cover-images/cat-pounce-dorothea-oldani.jpg
new file mode 100644 (file)
index 0000000..ddb6958
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4e21d67097a698386288063f1c2c6a23e6db600d6546fe65042f3e49093d8340
+size 384799
diff --git a/static/images/blog-cover-images/dog-woods-jamie-street.jpg b/static/images/blog-cover-images/dog-woods-jamie-street.jpg
new file mode 100644 (file)
index 0000000..d66cba2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:04e2cdd20c445d3424252ef6427d42dbe8ae7d68965d066225baf6adcede3630
+size 304804
diff --git a/static/images/blog-cover-images/door-lock-lucas-santos.jpg b/static/images/blog-cover-images/door-lock-lucas-santos.jpg
new file mode 100644 (file)
index 0000000..f5819b2
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:96a99d9bfc19320a4bb43b8ecd370218f3d4d5e3cc48a9a34685a7721eaf8750
+size 353236
diff --git a/static/images/blog-cover-images/fence-birds-yudi-m.jpg b/static/images/blog-cover-images/fence-birds-yudi-m.jpg
new file mode 100644 (file)
index 0000000..88c7413
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58237ccff11be0b9c13fa8a14661110b64a8f7723fb500a32fb38761438c605c
+size 434550
diff --git a/static/images/blog-cover-images/gate-benofthenorth.jpg b/static/images/blog-cover-images/gate-benofthenorth.jpg
new file mode 100644 (file)
index 0000000..119ce29
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2119a79a98e2729999e928bb9719fedfc2bf163b8b11d132ea7b7013a66cb322
+size 362499
diff --git a/static/images/blog-cover-images/gate-masaaki-komori.jpg b/static/images/blog-cover-images/gate-masaaki-komori.jpg
new file mode 100644 (file)
index 0000000..18782ab
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0786d328fe87139a692de1b40d077ae3b1d8469d39844af1a30f2281f2d970d8
+size 219025
diff --git a/static/images/blog-cover-images/jellyfish-ruthlaine-tan.jpg b/static/images/blog-cover-images/jellyfish-ruthlaine-tan.jpg
new file mode 100644 (file)
index 0000000..3e89783
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:df7dc58c439255a1ea5a005889a6c343fd11a23daf505ac7892940a2a00062a3
+size 291232
diff --git a/static/images/blog-cover-images/library-priscilla-du-preez.jpg b/static/images/blog-cover-images/library-priscilla-du-preez.jpg
new file mode 100644 (file)
index 0000000..6a494cb
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:55b67d6e5c51ba73ca653c75ff99e02c8169f7beb4569e185de84f311ea0c2c1
+size 371061
diff --git a/static/images/blog-cover-images/lighthouse-dimitry_b.jpg b/static/images/blog-cover-images/lighthouse-dimitry_b.jpg
new file mode 100644 (file)
index 0000000..96716ae
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8cbe2b7d5d9751e455aaaa98a37b2586656a222005a83de838a7c11f3444330c
+size 199326
diff --git a/static/images/blog-cover-images/lions-joel-herzog.jpg b/static/images/blog-cover-images/lions-joel-herzog.jpg
new file mode 100644 (file)
index 0000000..bbe7883
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:613c956ebb853e85fc4c887dc933459e57f5226a6b6f2f7df481c9e668c7a09a
+size 335793
diff --git a/static/images/blog-cover-images/lock-aubrey-odom.jpg b/static/images/blog-cover-images/lock-aubrey-odom.jpg
new file mode 100644 (file)
index 0000000..0aecc86
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a4c3399a06bd59e44791dc6920e2bf03b197b38c98eb4ee3078a1d512b13abb
+size 290647
diff --git a/static/images/blog-cover-images/lock-calina.jpg b/static/images/blog-cover-images/lock-calina.jpg
new file mode 100644 (file)
index 0000000..9112131
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:66b2e64322c2e686e72d6e6e7e1f338db21d8804f05d532ad66561250c8c918c
+size 252640
diff --git a/static/images/blog-cover-images/lock-chepe-nicoli.jpg b/static/images/blog-cover-images/lock-chepe-nicoli.jpg
new file mode 100644 (file)
index 0000000..3460e86
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b540c3bb128eb280e0053ada8bb9abbf2e3653f50d184758fa49608152ab8e85
+size 191492
diff --git a/static/images/blog-cover-images/lock-door-waldemar-brandt.jpg b/static/images/blog-cover-images/lock-door-waldemar-brandt.jpg
new file mode 100644 (file)
index 0000000..3de0d4b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8549305cb408ff9e41d2e306af415cdee444f3e6a20a6106d33a4fe271ba5cc1
+size 266321
diff --git a/static/images/blog-cover-images/lock-gina-neri.jpg b/static/images/blog-cover-images/lock-gina-neri.jpg
new file mode 100644 (file)
index 0000000..b479234
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:584b1ff0a22537a0ca55d3006469ce0f95eead35768ee3d2a300c373216453de
+size 349938
diff --git a/static/images/blog-cover-images/lock-hudsoncrafted.jpg b/static/images/blog-cover-images/lock-hudsoncrafted.jpg
new file mode 100644 (file)
index 0000000..0e69354
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9c85e48dcea4b855529298a076d24d14193aefd62ff3f2dbd5323fbc777434c1
+size 143436
diff --git a/static/images/blog-cover-images/lock-jon-moore.jpg b/static/images/blog-cover-images/lock-jon-moore.jpg
new file mode 100644 (file)
index 0000000..e08ad89
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d1c2fb75a1de5130909f25156a4536e4d10236a0d437c9095be424361f3f09d
+size 205167
diff --git a/static/images/blog-cover-images/lock-jornada-produtora.jpg b/static/images/blog-cover-images/lock-jornada-produtora.jpg
new file mode 100644 (file)
index 0000000..6fc7ccc
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6948a66c3f88dcf650d5974d75b0bc6f63c6fb95d15a02c27ac0054759178396
+size 169913
diff --git a/static/images/blog-cover-images/lock-markus-spiske.jpg b/static/images/blog-cover-images/lock-markus-spiske.jpg
new file mode 100644 (file)
index 0000000..27b6488
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7fa663885b33525b59b78d885b71e35580fa2582282a1f4ad8267d4087317682
+size 346463
diff --git a/static/images/blog-cover-images/lock-muhammad-zaqy-al-fattah.jpg b/static/images/blog-cover-images/lock-muhammad-zaqy-al-fattah.jpg
new file mode 100644 (file)
index 0000000..14daf3c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a367ed7340bd51f6183f31ce9eed59a2376f670b03476f464a8d9950283d2b1
+size 270194
diff --git a/static/images/blog-cover-images/locks-marcos-mayer.jpg b/static/images/blog-cover-images/locks-marcos-mayer.jpg
new file mode 100644 (file)
index 0000000..e55e9c8
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:72b5a3f9c818564a0cc384e861fd408cf4430316299af5e9aade8df7964e5f90
+size 393265
diff --git a/static/images/blog-cover-images/mountain-stars-martin-jernberg.jpg b/static/images/blog-cover-images/mountain-stars-martin-jernberg.jpg
new file mode 100644 (file)
index 0000000..07f6ee6
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8e8ee8b03d2df0a775279bb1aedfd4e16cb5202a270a34e77a907b29c069f32c
+size 406991
diff --git a/static/images/blog-cover-images/mountains-jerry-zhang.jpg b/static/images/blog-cover-images/mountains-jerry-zhang.jpg
new file mode 100644 (file)
index 0000000..36a6348
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7c2f27a21da04c1d6885a6cbe9c4320125938277375293048d1e2acc15f20fa1
+size 238249
diff --git a/static/images/blog-cover-images/mountains-jonatan-pie.jpg b/static/images/blog-cover-images/mountains-jonatan-pie.jpg
new file mode 100644 (file)
index 0000000..7499714
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec5006d0f29fcf44978f348e63f38a93c2a5a9e13d6659aea4243ad784ecb80a
+size 541537
diff --git a/static/images/blog-cover-images/path-ugne-vasyliute.jpg b/static/images/blog-cover-images/path-ugne-vasyliute.jpg
new file mode 100644 (file)
index 0000000..20e6e31
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c52acfb71c49bb41af38181034684ef770aec6f8ec941ec91bf2fa0893b74501
+size 364470
diff --git a/static/images/blog-cover-images/penguin-steffen-triekel.jpg b/static/images/blog-cover-images/penguin-steffen-triekel.jpg
new file mode 100644 (file)
index 0000000..c800146
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de1fb734a90d6d92f2011ebc8df1e7e5b108186997e8f5901996c1cef5280f10
+size 264457
diff --git a/static/images/blog-cover-images/reindeer-joe-green.jpg b/static/images/blog-cover-images/reindeer-joe-green.jpg
new file mode 100644 (file)
index 0000000..480d845
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c20b91a30d370986f5cf59371508974f2a72e9e66bd26646dc62f9e982e47484
+size 359880
diff --git a/static/images/blog-cover-images/robin-alfred-kenneally.jpg b/static/images/blog-cover-images/robin-alfred-kenneally.jpg
new file mode 100644 (file)
index 0000000..30a6966
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0dbc48ee50857fde05599b384682ddbe8f59618d0576fdab33e5d0aa361f0edc
+size 287245
diff --git a/static/images/blog-cover-images/spring-arno-smit.jpg b/static/images/blog-cover-images/spring-arno-smit.jpg
new file mode 100644 (file)
index 0000000..9195d5a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:51d23c1b22e5ac9120c2c86d3b7893eea8aec3ca88f27c0b3cbcd4dc47f11e6b
+size 379280
diff --git a/static/images/blog-cover-images/spring-bird-brian-breeden.jpg b/static/images/blog-cover-images/spring-bird-brian-breeden.jpg
new file mode 100644 (file)
index 0000000..bd74a67
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3fed990b34989b2e06f2d1b5c6ccf81cf01e5d2c6e8505aa730833bbce91c7e5
+size 206905
diff --git a/static/images/blog-cover-images/winter-fox-birger-strahl.jpg b/static/images/blog-cover-images/winter-fox-birger-strahl.jpg
new file mode 100644 (file)
index 0000000..c13171a
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:84b90df595beeb66f6ae542484edc43f58eae6b350ccf8b8915106cd247c5089
+size 519154
diff --git a/static/images/blog-cover-images/zebra-herd-the_bracketeer.jpg b/static/images/blog-cover-images/zebra-herd-the_bracketeer.jpg
new file mode 100644 (file)
index 0000000..ec0c9e6
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f1390f36e5a4a81b8e4e551365e601530f16efa8cffbb962939097a3eaa711ad
+size 375624
index 2b63f125095c53aab5d57f26c39c9e072ac706a3..11bf506521e8893ce9fc557459b228b06d971a5e 100644 (file)
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:85ddf4d244c54bb01e133f3d1a705a23f15945c3e7719e3efc56688dad7e020f
-size 7872
+oid sha256:daba7b9e8d7d8b653464ae9d6a115ac2b70847fa3567995392fee8521bc306c2
+size 33079
diff --git a/static/images/docs/book-sort.png b/static/images/docs/book-sort.png
new file mode 100644 (file)
index 0000000..9acdbcc
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3bf432f11043ea23c6bb9f01c0341cdce20149ff53c916290707fd24c27cce93
+size 43243
diff --git a/static/images/docs/page-move.png b/static/images/docs/page-move.png
new file mode 100644 (file)
index 0000000..f8a7d8c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3225ec6fc2ad9897856a8b3ba55205843d05620be34806892955c4a4fd08d472
+size 24486
diff --git a/static/images/docs/user/page-permissions.png b/static/images/docs/user/page-permissions.png
new file mode 100644 (file)
index 0000000..bb82af0
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bec0e337ff26552233fe384075fa9a3f359d4cb961d701cc62990dea30914b64
+size 20087
diff --git a/static/images/docs/user/permissions-active-indicator.png b/static/images/docs/user/permissions-active-indicator.png
new file mode 100644 (file)
index 0000000..51ff98c
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f972c1118858ac242c75a12e96eaa434087dea9293bcff58012ff2ec65a094ae
+size 7357
diff --git a/static/images/docs/user/shelf-book-organising.png b/static/images/docs/user/shelf-book-organising.png
new file mode 100644 (file)
index 0000000..bcdddb3
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e562562a302a315b1fd1b596db9f407b2e4cba5f34b3e9612d962ad391b190cb
+size 85196
diff --git a/static/images/sponsors/cloudabove.svg b/static/images/sponsors/cloudabove.svg
new file mode 100644 (file)
index 0000000..3fa3b7c
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="194px" height="70px" viewBox="0 0 149 53" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 55.2 (78181) - https://sketchapp.com -->
+    <title>Artboard Copy</title>
+    <desc>Created with Sketch.</desc>
+    <g id="Artboard-Copy" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="Group" fill="#000" fill-rule="nonzero">
+            <path d="M95.8938,24.5612 C97.3254,24.5612 98.7249,24.1319 99.9153,23.3278 C101.106,22.5236 102.033,21.3806 102.581,20.0433 C103.129,18.7061 103.272,17.2346 102.993,15.8149 C102.714,14.3953 102.024,13.0912 101.012,12.0677 C99.9998,11.0442 98.7101,10.34721 97.3059,10.06482 C95.9018,9.78243 94.4464,9.92737 93.1237,10.48128 C91.8011,11.0352 90.6706,11.9732 89.8752,13.1767 C89.0798,14.3803 88.6553,15.7952 88.6553,17.2427 C88.6553,19.1837 89.4179,21.0451 90.7754,22.4176 C92.1329,23.7901 93.974,24.5612 95.8938,24.5612 Z M95.8938,12.2071 C96.8788,12.2071 97.8418,12.5025 98.6608,13.0558 C99.4798,13.6091 100.118,14.3955 100.495,15.3157 C100.872,16.2358 100.971,17.2483 100.779,18.2251 C100.586,19.2019 100.112,20.0991 99.4155,20.8034 C98.719,21.5076 97.8315,21.9872 96.8654,22.1815 C95.8993,22.3758 94.8979,22.2761 93.9878,21.8949 C93.0777,21.5138 92.2999,20.8684 91.7526,20.0403 C91.2054,19.2122 90.9133,18.2386 90.9133,17.2427 C90.9133,16.5814 91.0421,15.9266 91.2924,15.3157 C91.5427,14.7047 91.9095,14.1496 92.372,13.682 C93.306,12.7377 94.5729,12.2071 95.8938,12.2071 Z" id="Shape"></path>
+            <path d="M139.238,17.909 L139.238,17.6577 L139.238,3.1737 C139.242,3.02368 139.215,2.87449 139.159,2.73542 C139.104,2.59635 139.02,2.47039 138.914,2.36539 C138.809,2.2568 138.683,2.17157 138.543,2.11514 C138.404,2.05872 138.254,2.03234 138.104,2.0377 C137.957,2.03536 137.812,2.06321 137.676,2.11955 C137.541,2.17589 137.418,2.25953 137.315,2.36539 C137.209,2.47039 137.126,2.59635 137.07,2.73542 C137.014,2.87449 136.987,3.02368 136.991,3.1737 L136.991,10.27372 C136.993,10.55759 136.901,10.83405 136.732,11.0602 C136.656,11.1656 136.542,11.237 136.415,11.2594 C136.288,11.2818 136.158,11.2535 136.051,11.1803 C134.736,10.28164 133.166,9.84481 131.581,9.9371 C129.996,10.02938 128.486,10.64567 127.282,11.6911 C126.078,12.7365 125.247,14.1531 124.917,15.7228 C124.587,17.2924 124.777,18.9282 125.456,20.3783 C126.136,21.8283 127.269,23.0122 128.679,23.7478 C130.09,24.4834 131.701,24.7298 133.263,24.4492 C134.826,24.1685 136.254,23.3763 137.328,22.1946 C138.402,21.0128 139.061,19.507 139.206,17.909 L139.238,17.909 Z M132.032,22.2782129 C131.047,22.2782129 130.084,21.9829 129.265,21.4296 C128.446,20.8763 127.808,20.0898 127.431,19.1697 C127.054,18.2496 126.955,17.2371 127.147,16.2603 C127.339,15.2835 127.814,14.3862 128.51,13.682 C129.207,12.9778 130.094,12.4982 131.06,12.3039 C132.027,12.1096 133.028,12.2093 133.938,12.5904 C134.848,12.9715 135.626,13.617 136.173,14.4451 C136.721,15.2732 137.013006,16.2467 137.013006,17.2427 C137.014,17.9043 136.886,18.5598 136.636,19.1714 C136.387,19.783 136.02,20.3387 135.557,20.8065 C135.094,21.2744 134.545,21.6453 133.94,21.8978 C133.335,22.1504 132.687,22.2797 132.032,22.2782129 Z" id="Shape"></path>
+            <path d="M121.499,17.2427 L121.499,11.0821 C121.501,10.93125 121.472,10.78157 121.415,10.64246 C121.357,10.50334 121.272,10.3778 121.164,10.27375 C121.061,10.16789 120.938,10.08425 120.803,10.02791 C120.667,9.97158 120.522,9.94373 120.375,9.94592499 C120.088,9.94592499 119.814,10.06114 119.611,10.26599 C119.408,10.47084 119.295,10.74867 119.295,11.0384 L119.295,17.199 C119.295,18.5345 118.77,19.8153 117.836,20.7597 C116.902,21.704 115.635,22.2346 114.314,22.2346 C112.993,22.2346 111.726,21.704 110.792,20.7597 C109.858,19.8153 109.334,18.5345 109.334,17.199 L109.334,11.0821 C109.334,10.78077 109.215,10.49183 109.005,10.27879 C108.794,10.06574 108.508,9.94592499 108.21,9.94592499 C107.912,9.94592499 107.626,10.06574 107.416,10.27879 C107.205,10.49183 107.086,10.78077 107.086,11.0821 L107.086,17.2427 C107.086,18.2038 107.274,19.1554 107.637,20.0434 C108.001,20.9313 108.534,21.7381 109.207,22.4176 C109.879,23.0972 110.677,23.6363 111.555,24.0041 C112.433,24.3719 113.374,24.5612 114.325,24.5612 C115.275,24.5612 116.217,24.3719 117.095,24.0041 C117.973,23.6363 118.771,23.0972 119.443,22.4176 C120.115,21.7381 120.649,20.9313 121.012,20.0434 C121.376,19.1554 121.563,18.2038 121.563,17.2427 L121.499,17.2427 Z" id="Path"></path>
+            <path d="M71.7042,24.5613283 C72.906,24.5684 74.0905,24.2725 75.1506,23.7003 C76.2108,23.1281 77.1131,22.2977 77.7759,21.2842 C77.8575,21.1594 77.914,21.0196 77.9421,20.8728 C77.9702,20.7259 77.9695,20.5749 77.9399,20.4283 C77.9103,20.2817 77.8524,20.1425 77.7696,20.0185 C77.6868,19.8946 77.5807,19.7883 77.4572,19.7058 C77.3338,19.6234 77.1955,19.5663 77.0502,19.5378 C76.905,19.5094 76.7556,19.5101 76.6106,19.5401 C76.4657,19.57 76.328,19.6285 76.2054,19.7122 C76.0828,19.796 75.9777,19.9033 75.8961,20.0281 C75.41,20.7535 74.744,21.3371 73.9647,21.7205 C73.1855,22.1039 72.3203,22.2736 71.4558,22.2127 C70.1608,22.1275 68.9499,21.5348 68.0808,20.5605 C67.2116,19.5863 66.7528,18.3076 66.8021,16.9965 C66.8514,15.6855 67.4048,14.4456 68.3446,13.5409 C69.2844,12.6362 70.5363,12.1381 71.8339,12.1525 C72.6567,12.1612 73.4646,12.3763 74.1849,12.7786 C74.9053,13.1809 75.5155,13.7577 75.9609,14.4573 C76.1214,14.7122 76.3754,14.8923 76.6672,14.9578 C76.9589,15.0234 77.2645,14.9691 77.5166,14.8068 C77.7688,14.6446 77.9469,14.3877 78.0117,14.0928 C78.0765,13.7978 78.0228,13.4888 77.8624,13.2339 C77.1852,12.1677 76.2458,11.2975 75.1361,10.70853 C74.0264,10.11956 72.7845,9.83197 71.532,9.87392 C70.2794,9.91586 69.059,10.2859 67.9903,10.94782 C66.9216,11.6097 66.0413,12.5409 65.4353,13.65 C64.8294,14.7592 64.5187,16.0085 64.5336,17.2755 C64.5485,18.5425 64.8886,19.7839 65.5204,20.8782 C66.1523,21.9725 67.0544,22.8822 68.1383,23.5182 C69.2223,24.1542 70.451,24.4948 71.7042,24.5066 L71.7042,24.5613283 Z" id="Path"></path>
+            <path d="M83.1885,24.5613319 C83.3351,24.5635 83.4806,24.5357 83.6162,24.4793 C83.7518,24.423 83.8746,24.3394 83.9772,24.2335 C84.0833,24.1285 84.1668,24.0025 84.2225,23.8635 C84.2783,23.7244 84.3051,23.5752 84.3013,23.4252 L84.3013,3.40313 C84.3088,3.11589 84.204,2.83725 84.0096,2.62759 C83.9072,2.50968 83.7808,2.41548 83.6391,2.35139 C83.4974,2.2873 83.3438,2.25484 83.1885,2.25601146 C83.0315,2.25325 82.8757,2.28495 82.7321,2.34909 C82.5884,2.41323 82.4603,2.50827 82.3567,2.62759 C82.178,2.84399 82.0889,3.12188 82.1082,3.40313 L82.1082,23.4252 C82.1044,23.5752 82.1312,23.7244 82.187,23.8635 C82.2427,24.0025 82.3262,24.1285 82.4323,24.2335 C82.6372,24.4311 82.9057,24.5474 83.1885,24.5613319 Z" id="Path"></path>
+            <path d="M106.395,37.3631 C104.963,37.3631 103.564,37.7923 102.373,38.5965 C101.183,39.4006 100.255,40.5436 99.7075,41.8809 C99.1596,43.2182 99.0163,44.6897 99.2956,46.1093 C99.5749,47.529 100.264,48.833 101.277,49.8565 C102.289,50.88 103.579,51.577 104.983,51.8594 C106.387,52.1418 107.842,51.9969 109.165,51.4429 C110.488,50.889 111.618,49.951 112.414,48.7475 C113.209,47.544 113.633,46.129 113.633,44.6816 C113.633,43.7205 113.446,42.7688 113.082,41.8809 C112.719,40.993 112.186,40.1862 111.513,39.5066 C110.841,38.827 110.043,38.2879 109.165,37.9202 C108.287,37.5524 107.346,37.3631 106.395,37.3631 Z M106.395,49.7171113 C105.409,49.7171113 104.446,49.4215 103.627,48.8677 C102.808,48.3139 102.169,47.5268 101.793,46.606 C101.416,45.6853 101.318,44.6722 101.511,43.6951 C101.704,42.718 102.179,41.8208 102.877,41.117 C103.575,40.4132 104.463,39.9345 105.43,39.7414 C106.397,39.5484 107.399,39.6496 108.308,40.0324 C109.218,40.4153 109.995,41.0624 110.541,41.8919 C111.087,42.7214 111.378,43.6961 111.375023,44.6925 C111.375023,45.3532 111.247,46.0075 110.996,46.6178 C110.746,47.2282 110.379,47.7826 109.916,48.2493 C109.453,48.716 108.904,49.0859 108.3,49.3378 C107.696,49.5896 107.049,49.7185 106.395,49.7171113 Z" id="Shape"></path>
+            <path d="M89.2927,37.363 C87.8485,37.3638 86.4376,37.8012 85.2413,38.6192 C85.1346,38.6923 85.0039,38.7207 84.8769,38.6983 C84.7499,38.6759 84.6365,38.6044 84.5606,38.499 C84.3909,38.2729 84.2998,37.9965 84.3014,37.7126 L84.3014,30.5689 C84.3052,30.4189 84.2783,30.2697 84.2226,30.1306 C84.1668,29.9915 84.0833,29.8656 83.9773,29.7606 C83.8775,29.6488 83.7559,29.559 83.6202,29.497 C83.4845,29.435 83.3375,29.402 83.1886,29.4001 C83.0384,29.3947 82.8888,29.4211 82.7493,29.4775 C82.6097,29.534 82.4834,29.6192 82.3783,29.7278 C82.2723,29.8328 82.1887,29.9587 82.133,30.0978 C82.0772,30.2369 82.0504,30.3861 82.0542,30.5361 L82.0542,45.3478 C82.1799,46.763 82.7097,48.1108 83.5792,49.2271 C84.4487,50.3434 85.6204,51.1801 86.9516,51.6354 C88.2829,52.0907 89.7164,52.1449 91.0776,51.7915 C92.4388,51.4381 93.669,50.6922 94.6186,49.6448 C95.5682,48.5973 96.1961,47.2933 96.4261,45.8916 C96.656,44.4898 96.4779,43.0507 95.9136,41.7494 C95.3493,40.4481 94.4231,39.3406 93.2476,38.5619 C92.0721,37.7831 90.698,37.3666 89.2927,37.363 Z M89.2927,49.7171113 C88.3072,49.7171113 87.3438,49.4215 86.5245,48.8677 C85.7053,48.3139 85.0669,47.5268 84.6903,46.606 C84.3136,45.6853 84.2156,44.6722 84.4087,43.6951 C84.6017,42.718 85.0771,41.8208 85.7748,41.117 C86.4724,40.4132 87.3608,39.9345 88.3277,39.7414 C89.2945,39.5483 90.2963,39.6496 91.2061,40.0324 C92.116,40.4152 92.8931,41.0623 93.4391,41.8919 C93.985,42.7214 94.2753,43.696 94.2732113,44.6924 C94.2732113,45.3532 94.1443,46.0075 93.8939,46.6178 C93.6434,47.2281 93.2764,47.7825 92.8138,48.2493 C92.3512,48.716 91.802,49.0859 91.1978,49.3377 C90.5936,49.5896 89.9462,49.7185 89.2927,49.7171113 Z" id="Shape"></path>
+            <path d="M71.7799,37.3630994 C70.5967,37.3626 69.4315,37.6554 68.3862,38.2158 C67.341,38.7762 66.4475,39.587 65.784,40.5775 C65.1205,41.5679 64.7073,42.7076 64.5804,43.897 C64.4536,45.0863 64.617,46.2889 65.0564,47.3996 C65.4958,48.5102 66.1978,49.4951 67.1009,50.2678 C68.004,51.0406 69.0807,51.5778 70.2367,51.8324 C71.3928,52.087 72.5929,52.0511 73.7321,51.7281 C74.8713,51.405 75.9147,50.8046 76.7712,49.9793 L76.7712,50.6019 C76.7689,50.7501 76.7964,50.8972 76.8521,51.0343 C76.9079,51.1714 76.9906,51.2955 77.0953,51.3993 C77.2017,51.5068 77.3281,51.592 77.4671,51.6501 C77.6061,51.7082 77.7551,51.738 77.9056,51.7379 C78.0529,51.7388 78.1989,51.7092 78.3345,51.651 C78.4702,51.5928 78.5926,51.5071 78.6942,51.3993 C78.8022,51.2981 78.8871,51.1743 78.9431,51.0366 C78.9991,50.8989 79.0247,50.7506 79.0184,50.6019 L79.0184,44.7471 L79.0184,44.6925 C79.0198,43.7305 78.8336,42.7776 78.4705,41.8885 C78.1073,40.9993 77.5744,40.1912 76.9021,39.5105 C76.2298,38.8297 75.4313,38.2897 74.5524,37.9212 C73.6735,37.5527 72.7314,37.3630994 71.7799,37.3630994 Z M71.7799,49.7171113 C70.7944,49.7171113 69.831,49.4215 69.0117,48.8677 C68.1925,48.3139 67.5541,47.5268 67.1775,46.606 C66.8008,45.6853 66.7028,44.6722 66.8959,43.6951 C67.0889,42.718 67.5643,41.8208 68.262,41.117 C68.9596,40.4132 69.848,39.9345 70.8149,39.7414 C71.7817,39.5484 72.7835,39.6496 73.6933,40.0324 C74.6032,40.4153 75.3803,41.0624 75.9263,41.8919 C76.4722,42.7214 76.7625,43.6961 76.7604113,44.6925 C76.7604113,45.3532 76.6315,46.0075 76.3811,46.6178 C76.1306,47.2282 75.7636,47.7826 75.301,48.2493 C74.8384,48.716 74.2892,49.0859 73.685,49.3378 C73.0808,49.5896 72.4334,49.7185 71.7799,49.7171113 Z" id="Shape"></path>
+            <path d="M129.763,37.6689 C129.619,37.6095 129.463,37.5845 129.308,37.5959 C129.152,37.6072 129.001,37.6547 128.867,37.7344 C128.613,37.8622 128.419,38.0859 128.327,38.3571 L126.792,42.7263 L125.95,45.0966 L125.798,45.5335 L124.718,48.6466 C124.623,48.9462 124.436,49.2076 124.185,49.393 C123.933,49.5785 123.63,49.6784 123.319,49.6784 C123.008,49.6784 122.705,49.5785 122.453,49.393 C122.202,49.2076 122.015,48.9462 121.92,48.6466 C121.92,48.4719 121.261,46.7569 120.84,45.5226 L120.688,45.0857 L119.846,42.7154 L118.311,38.3461 C118.216,38.0802 118.028,37.8587 117.782,37.7235 C117.645,37.6465 117.492,37.6008 117.335,37.5894 C117.179,37.5781 117.021,37.6015 116.875,37.658 C116.723,37.7037 116.585,37.785 116.47,37.895 C116.356,38.0051 116.268,38.141 116.216,38.2915 C116.11,38.5542 116.11,38.848 116.216,39.1107 L119.9,49.5205 C120.152,50.2357 120.615,50.8561 121.225,51.299 C121.835,51.7418 122.565,51.986 123.316,51.999 C124.067,52.0121 124.804,51.7934 125.429,51.372 C126.054,50.9506 126.537,50.3467 126.814,49.6406 L130.541,39.1217 C130.646,38.8589 130.646,38.5652 130.541,38.3024 C130.478,38.1374 130.372,37.9921 130.236,37.881 C130.099,37.7699 129.936,37.6968 129.763,37.6689 Z" id="Path"></path>
+            <path d="M139.94,37.4067 C138.097,37.6111 136.402,38.5228 135.204,39.9537 C134.007,41.3846 133.397,43.2257 133.503,45.0972 C133.608,46.9688 134.42,48.7282 135.77,50.0126 C137.121,51.2969 138.907,52.0083 140.762,52.0000718 C142.56,52.0052 144.296,51.3268 145.623,50.0994 C145.824,49.9586 145.971,49.7535 146.041,49.5173 C146.112,49.2811 146.101,49.0278 146.011,48.7986 C145.92,48.5694 145.756,48.3778 145.545,48.255 C145.333,48.1322 145.087,48.0855 144.845,48.1223 C144.574,48.1203 144.312,48.2217 144.111,48.4063 C143.467,48.997 142.681,49.4066 141.832,49.5943 C140.983,49.782 140.099,49.7411 139.271,49.4758 C138.442,49.2105 137.696,48.73 137.109,48.0823 C136.521,47.4346 136.112,46.6424 135.921,45.7848 L146.844,45.7848 C147.141,45.7909 147.428,45.6805 147.646,45.4767 C147.863,45.273 147.994,44.9918 148.011014,44.6925 C148.013,43.6591 147.799,42.6369 147.382,41.6933 C146.965,40.7497 146.356,39.906 145.593,39.2177 C144.831,38.5295 143.933,38.0122 142.959,37.7001 C141.984,37.388 140.956,37.288 139.94,37.4067 Z M144.975,43.534615 L136.548,43.534615 C136.474,43.5352 136.4,43.5178 136.334,43.4838 C136.268,43.4498 136.211,43.4002 136.167,43.3392 C136.124,43.2782 136.096,43.2076 136.085,43.1332 C136.074,43.0589 136.081,42.983 136.105,42.912 C136.459,41.9522 137.095,41.1247 137.927,40.5405 C138.759,39.9563 139.748,39.6432 140.762,39.6432 C141.775,39.6432 142.764,39.9563 143.596,40.5405 C144.428,41.1247 145.064,41.9522 145.418,42.912 C145.442,42.983 145.449,43.0589 145.438,43.1332 C145.428,43.2076 145.399,43.2782 145.356,43.3392 C145.312,43.4002 145.255,43.4498 145.189,43.4838 C145.123,43.5178 145.049,43.5352 144.975,43.534615 Z" id="Shape"></path>
+        </g>
+        <g id="Group2" fill="#6757ff" fill-rule="nonzero">
+            <path d="M53.1875,7.07278 C51.2924,4.76556 48.8237,3.01026 46.0352,1.987319 C43.2467,0.9643805 40.2391,0.710716 37.3216,1.252402 C34.404,1.794088 31.6818,3.11158 29.4347,5.06944 C27.1877,7.02729 25.4968,9.55487 24.5361,12.3923 C24.3545,12.907 24.3702,13.4718 24.5802,13.9753 C24.7902,14.4788 25.1792,14.8844 25.6705,15.1122 C25.9468,15.2336 26.2448,15.2967 26.5461,15.2977 C26.8474,15.2986 27.1457,15.2374 27.4228,15.1178 C27.6999,14.9982 27.95,14.8226 28.1577,14.6019 C28.3654,14.3812 28.5263,14.12 28.6307,13.8342 C29.5862,11.018 31.5009,8.63703 34.0326,7.11647 C35.9271,5.97308 38.0923,5.36918 40.2987,5.36877 C42.9177,5.35881 45.4722,6.18949 47.5943,7.74118 C49.7165,9.29287 51.2968,11.4855 52.1077,14.0033 C52.9185,16.5211 52.9181,19.2341 52.1065,21.7517 C51.2948,24.2692 49.7138,26.4613 47.5912,28.0124 C45.4911,29.5658 42.9549,30.4001 40.3527,30.3936 C38.3929,30.3922 36.4612,29.9224 34.7151,29.0226 C32.969,28.1228 31.458,26.8185 30.3053,25.2161 L29.6679,24.3968 C28.0956,22.4603 26.1162,20.9025 23.8735,19.8368 C21.6308,18.7711 19.1814,18.2243 16.7034,18.2362 C13.2971,18.2808 9.98339,19.3641 7.19601,21.3442 C4.40864,23.3243 2.27742,26.1091 1.08125,29.3341 C-0.344327,33.1497 -0.301919,37.3673 1.20009,41.1529 C2.11621,43.5726 3.5704,45.7469 5.45024,47.5077 C7.33009,49.2686 9.58523,50.5688 12.0414,51.3078 C14.4975,52.0469 17.0888,52.205 19.6151,51.77 C22.1413,51.335 24.5347,50.3185 26.6104,48.7991 C28.2073,47.6346 29.5864,46.1921 30.6834,44.539 C30.839,44.3041 30.946,44.0398 30.998,43.762 C31.05,43.4842 31.0459,43.1986 30.9859,42.9224 C30.9303,42.636 30.8185,42.3637 30.657,42.1217 C30.4956,41.8797 30.2878,41.6728 30.046,41.5133 C29.5476,41.2222 28.9588,41.1325 28.3977,41.2622 C27.8366,41.3918 27.3447,41.7313 27.021,42.2124 C26.0124,43.6932 24.7007,44.9375 23.1748,45.8607 C21.2632,47.0222 19.0742,47.6341 16.8439,47.6303 C13.9264,47.6327 11.1017,46.5943 8.86708,44.698 C6.63245,42.8017 5.13123,40.169 4.62776,37.2636 C4.12429,34.3582 4.65086,31.3664 6.11474,28.8149 C7.57862,26.2634 9.88594,24.316 12.6304,23.3154 C13.963,22.8065 15.3762,22.5474 16.8006,22.5508 C18.7633,22.5603 20.6958,23.0392 22.4403,23.9486 C24.1847,24.858 25.6914,26.1719 26.8373,27.783 L27.4747,28.6022 C30.1755,31.9199 34.0256,34.0719 38.2409,34.6198 C42.4561,35.1678 46.7195,34.0705 50.1625,31.5515 C50.3786,31.3985 50.5838,31.2347 50.7891,31.0709 C50.9673,30.9231 51.1835,30.8299 51.4123,30.8022 C51.6411,30.7745 51.873,30.8134 52.0807,30.9144 C52.2883,31.0154 52.4632,31.1742 52.5846,31.3722 C52.706,31.5702 52.769,31.7991 52.7662,32.0321 L52.7662,32.611 C52.7428,33.1426 52.9121,33.6644 53.2423,34.0787 C53.5725,34.493 54.0409,34.7712 54.5596,34.8612 C54.8934,34.9122 55.2344,34.8835 55.5552,34.7772 C55.876,34.671 56.1678,34.4903 56.407,34.2495 C56.6099,34.0428 56.7698,33.7971 56.8774,33.527 C56.9851,33.2569 57.0382,32.9677 57.0339639,32.6765 L57.0339639,16.6305 C56.7772,13.115 55.4314,9.77071 53.1875,7.07278 Z" id="Path"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/static/images/sponsors/diagramsnet.png b/static/images/sponsors/diagramsnet.png
new file mode 100644 (file)
index 0000000..218d7b8
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8e4d70edcc50f71c3d32332198e354b8693abed2fad62836b477ba5f3ad159d
+size 12261
diff --git a/static/images/sponsors/stellarhosted.png b/static/images/sponsors/stellarhosted.png
new file mode 100644 (file)
index 0000000..7f14d3b
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fd5559c2d0670696b371e83116196cb74e3d702fec9fa6b12cfb9fd45edfed78
+size 16164
index ac36e0622776b9ea3aff3dd3e2b43b1cdf833eed..71173d4f24d245fa18d3273c2bd3a6fa6aca6cc8 100644 (file)
@@ -1,2 +1,16 @@
 +++
+title = "{{ replace .Name "-" " " | title }}"
+date = {{ .Date }}
+categories = ["News"]
+tags = ["News"]
+author = "Dan Brown"
+image = "/images/blog-cover-images/"
+draft = true
 +++
+
+
+# Title
+
+----
+
+<span style="font-size: 0.9em;opacity:0.9;">Header Image Credits: {REPLACE THIS}</span>
\ No newline at end of file
index 7f85b146323e890844ee1642df835ea4f4e0238a..9c258f272dcefdb43b19716ad22c504b8797caad 100644 (file)
@@ -1,30 +1,21 @@
 {{ partial "header.html" . }}
 
-
-<div class="header">
+<div class="shaded primary padded-vertical">
   <div class="container">
-    <div class="row">
-      <div class="col-sm-6">
-        {{ if eq .Type "admin-doc" }}
-          <h2 class="thin-margin">Admin Documentation</h2>
-        {{end}}
-        {{ if eq .Type "user-doc" }}
-          <h2 class="thin-margin">User Documentation</h2>
-        {{end}}
-      </div>
-      <div class="col-sm-6">
-        <div class="float right float-none-sm">
-          <input type="text" placeholder="Search docs" class="doc-search-input">
-        </div>
-      </div>
-    </div>
+    {{ if eq .Type "admin-doc" }}
+      <div class="docs-section-title">Admin Documentation</div>
+    {{ else if eq .Type "user-doc" }}
+      <div class="docs-section-title">User Documentation</div>
+    {{else}}
+      <div class="docs-section-title">Documentation</div>
+    {{end}}
   </div>
 </div>
 
 <div class="container">
 
   <div class="row">
-    <div class="col-sm-2 sidebar">
+    <aside class="col-md-2 sidebar">
       <div class="sidebar-inner">
         {{ if eq .Type "admin-doc" }}
         {{partial "menu_admin_docs"}}
         {{partial "menu_user_docs"}}
         {{end}}
       </div>
-    </div>
+      <div class="sidebar-inner mobile">
+        <ul>
+          <li><a href="/docs">&laquo; All Documentation</a></li>
+        </ul>
+      </div>
+    </aside>
 
-    <div class="col-sm-8 col-sm-offset-1 docs-content">
+    <main class="col-md-8 col-md-offset-1 docs-content">
 
       <h1>{{.Title}}</h1>
 
 
       <div class="text-center">
         <a class="edit-link" target="_blank"
-        href="https://github.com/BookStackApp/website/blob/master/content/{{ .File.Path }}">
+        href="https://github.com/BookStackApp/website/blob/main/content/{{ .File.Path }}">
           <span class="icon small">{{partial "icon/edit.svg"}}</span>
           Edit this Page
         </a>
       </div>
 
-    </div>
+    </main>
   </div>
 
 </div>
diff --git a/themes/bookstack/layouts/about/list.html b/themes/bookstack/layouts/about/list.html
new file mode 100644 (file)
index 0000000..d89a968
--- /dev/null
@@ -0,0 +1,37 @@
+{{ partial "header.html" . }}
+
+{{ $baseurl := .Site.BaseURL }}
+
+<div class="no-cover"> 
+
+       <div class="container">
+               <h1>"About BookStack" Pages</h1>
+               <h2 class="page-description">
+                   {{if .Params.description}}
+                       {{.Params.description}}
+                   {{end}}
+               </h2>
+       </div>
+</div>
+
+<div class="container">
+       {{ $paginator := .Paginator }}
+
+       {{ range $paginator.Pages }}
+       {{ $baseurl := .Site.BaseURL }}
+       <article class="post {{ .Section }}">
+           <div class="post-header">
+               <h5 class="post-title"><a href="{{ .RelPermalink }}">{{ .Title }}</a></h5>
+           </div>
+       </article>
+
+       {{ end }}
+
+       <div class="row">
+               <div class="col-md-4">
+                       {{ partial "pagination" $paginator }}
+               </div>
+       </div>
+</div>
+
+{{ partial "footer.html" . }}
\ No newline at end of file
diff --git a/themes/bookstack/layouts/about/single.html b/themes/bookstack/layouts/about/single.html
new file mode 100644 (file)
index 0000000..812fd37
--- /dev/null
@@ -0,0 +1,19 @@
+{{ partial "header.html" . }}
+
+<div class="about-page">
+  <div class="shaded primary">
+    <div class="container small hero padded-vertical">
+      <h1 class="text-center">{{.Title}}</h1>
+        {{ if .Params.image }}
+          <!-- <img class="screenshot" src="{{.Params.image}}" alt="{{.Title}}"> -->
+        {{ end }}   
+    </div>
+  </div>
+  
+  
+  <div class="container small padded-vertical">
+    {{.Content}}
+  </div>
+</div>
+
+{{ partial "footer.html" . }}
index 720aedba2386ded34822ca57e35c7b54ce0630f5..d89881cdf1c8eb5022f87eae63fb4d0cd1a30178 100644 (file)
@@ -9,35 +9,36 @@
 
   <div class="row">
 
-    <div class="col-md-6 col-md-offset-1 col-sm-7 post-content">
-
-      <h1>{{.Title}}</h1>
-
-      <p class="post-date text-muted" datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" | safeHTML }}">
-          {{$author := index .Site.Data.authors (or .Params.author .Site.Params.author)}}
-          {{$authorname := or $author.name .Site.Params.author }}
-          {{$authorthumbnail := or $author.thumbnail .Site.Params.author }}
-          <img class="post-avatar no-border" width="32" src="{{$authorthumbnail}}" alt="{{$authorname}}"> {{$authorname}} posted on the {{ .Date.Format "2" }}{{ if in (slice 1 21 31) .Date.Day}}st{{ else if in (slice 2 22) .Date.Day}}nd{{ else if in (slice 3 23) .Date.Day}}rd{{ else }}th{{ end }} of {{ .Date.Format "January 2006" }}
-      </p>
-
-      {{.Content}}
-
-      <div class="footer-content">
-        {{ partial "mailchimp.html" . }}
-
-        <div class="text-muted">
-          Want to let me know what you think of BookStack or this post? <br>
-          You find me on twitter <a href="https://twitter.com/ssddanbrown">@ssddanbrown</a>. <br>
-          You can <a href="https://github.com/BookStackApp/BookStack/issues">open a suggestion or issue on GitHub</a>.
+    <div class="col-md-9">
+
+      <main class="post-content">
+        <h1>{{.Title}}</h1>
+
+        <p class="post-date text-muted" datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" | safeHTML }}">
+            {{$author := index .Site.Data.authors (or .Params.author .Site.Params.author)}}
+            {{$authorname := or $author.name .Site.Params.author }}
+            {{$authorthumbnail := or $author.thumbnail .Site.Params.author }}
+            <img class="post-avatar no-border" width="32" src="{{$authorthumbnail}}" alt="{{$authorname}}"> {{$authorname}} posted on the {{ .Date.Format "2" }}{{ if in (slice 1 21 31) .Date.Day}}st{{ else if in (slice 2 22) .Date.Day}}nd{{ else if in (slice 3 23) .Date.Day}}rd{{ else }}th{{ end }} of {{ .Date.Format "January 2006" }}
+        </p>
+  
+        {{.Content}}
+  
+        <div class="footer-content">
+          {{ partial "signup.html" . }}
+  
+          <div class="text-muted">
+            Want to let me know what you think of BookStack or this post? <br>
+            You can find me on twitter <a href="https://twitter.com/ssddanbrown">@ssddanbrown</a> or on the <a href="https://discord.gg/ztkBqR2">BookStack Discord server</a>. <br>
+            You can <a href="https://github.com/BookStackApp/BookStack/issues">open a suggestion or issue on GitHub</a>.
+          </div>
+  
+          <br>
+  
         </div>
-
-        <br>
-        <br>
-
-      </div>
+      </main>
     </div>
 
-    <div class="col-md-3 col-md-offset-1 col-sm-4 col-sm-offset-1 blog-sidebar-post-list">
+    <aside class="col-md-3 blog-sidebar-post-list">
       <h4>Latest Posts</h4>
       <div class="recent-posts">
         {{ range first 8 ( where .Site.RegularPages "Section" "blog") }}
@@ -49,7 +50,7 @@
           </a>
         {{ end }}
       </div>
-    </div>
+    </aside>
 
   </div>
 
index 2ba325d9ca60d009dc5a8c4496f4316ed880bf76..c6c6a1ef03b48094511de73627389f12797798e7 100644 (file)
@@ -3,13 +3,21 @@
        <div class="container">
                <div class="row hero">
                    <div class="col-md-4 spaced">
-                       <h2>Simple &amp; Free <br>Wiki Software</h2>
+                       <h1>Simple &amp; Free <br>Wiki Software</h1>
                        <p>BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information.</p>
                        <p>
                          <a href="https://github.com/BookStackApp/BookStack">GitHub</a> &nbsp;-&nbsp;
                          <a href="https://demo.bookstackapp.com">Demo</a> &nbsp;-&nbsp;
                          <a href="/docs/admin/installation">Install</a>
                        </p>
+
+                               <p class="nomargin">
+                                       <br>
+                                       Latest Blog Post: <br>
+                                       {{ range first 1 ( where .Site.Pages "Section" "blog") }}
+                                                       <a href="{{.Permalink}}">{{ .Title }}</a>
+                                       {{ end }}
+                               </p>
                    </div>
                    <div class="col-md-8 screenshot-container-parent">
                        <div class="screenshot-container">
@@ -18,7 +26,7 @@
                                        <div class="window-option" style="background-color: orange;"></div>
                                        <div class="window-option"  style="background-color: green;"></div>
                                </div>
-                               <img class="screenshot" src="images/bookstack-hero-screenshot.jpg" alt="BookStack ScreenShot">
+                               <img class="screenshot" src="images/bookstack-hero-screenshot.webp" alt="BookStack ScreenShot">
                        </div>
                    </div>
                </div>
 
 
   <div class="container md-margin-top">
-        <h2 id="features">Features</h2>
+        <h2 id="features" class="nomargin margin-bottom">Features</h2>
         <div class="row">
             <div class="col-sm-4">
-                <h4><span class="icon">{{partial "icon/code.svg"}}</span>Free &amp; Open Source</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/code.svg"}}</span>Free &amp; Open Source</h4>
                 <p>BookStack is fully free and open, MIT licensed. The source is available on GitHub. There is no cost to downloading and installing your own instance of bookstack.
                 </p>
                 <p>
                 </p>
             </div>
             <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/laptop_chromebook.svg"}}</span>Easy, Simple Interface</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/laptop_chromebook.svg"}}</span>Easy, Simple Interface</h4>
                 <p>
                     Simplicity has been the top priority when building BookStack. The page editor has a simple WYSIWYG interface and all content is broken into three simple real world groups:
                 </p>
                 <p>
-                    <span class="icon book">{{partial "icon/book.svg"}}</span>Books &nbsp;&nbsp;<span class="icon chapter">{{partial "icon/collections_bookmark.svg"}}</span>Chapters&nbsp;&nbsp; <span class="icon page">{{partial "icon/description.svg"}}</span>Pages
+                                       <span class="icon book" aria-hidden="true">{{partial "icon/book.svg"}}</span>Books
+                                       &nbsp;&nbsp;<span class="icon chapter" aria-hidden="true">{{partial "icon/collections_bookmark.svg"}}</span>Chapters&nbsp;&nbsp;
+                                       <span class="icon page" aria-hidden="true">{{partial "icon/description.svg"}}</span>Pages
                 </p>
             </div>
             <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/search.svg"}}</span>Searchable and Connected</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/search.svg"}}</span>Searchable and Connected</h4>
                 <p>
                     The content in BookStack is fully searchable. You are able to search at book level or across all books, chapters &amp; pages. The ability to link directly to any paragraph allows you to keep your documentation connected.
                 </p>
         </p>
         <div class="row">
             <div class="col-sm-4">
-                <h4><span class="icon">{{partial "icon/build.svg"}}</span>Configurable</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/build.svg"}}</span>Configurable</h4>
                 <p>
-                    Configuration options allow you to set-up BookStack to suit your use case. You can change the name, Logo and registration options. You can also change whether the whole system is publicly viewable or not.
+                    Configuration options allow you to set-up BookStack to suit your use case. You can change the name, logo and registration options. You can also change whether the whole system is publicly viewable or not.
                 </p>
             </div>
             <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/storage.svg"}}</span>Simple Requirements</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/storage.svg"}}</span>Simple Requirements</h4>
                 <p>
                     BookStack is built using PHP, on top of the Laravel framework and it uses MySQL to store data. Performance has been kept in mind and BookStack can run happily on a $5 Digital Ocean VPS.
                 </p>
             </div>
-            <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/directions_boat.svg"}}</span>Powerful Features</h4>
+            <div class="col-sm-4">
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/drawing.svg"}}</span>Built-In diagrams.net</h4>
                 <p>
-                    On top of the powerful search and linking there is also cross-book sorting, page revisions and image management. A full role and permission system allow you to lock down content and actions as required.
+                    The page editor within BookStack has <a href="https://www.diagrams.net/" target="_blank">diagrams.net</a> 
+                    drawing capability built-in, allowing the quick and easy creation of diagrams within your documentation.
                 </p>
             </div>
         </div>
         </p>
         <div class="row">
             <div class="col-sm-4">
-                <h4><span class="icon">{{partial "icon/language.svg"}}</span>Multi-lingual</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/language.svg"}}</span>Multi-Lingual</h4>
                 <p>
-                    BookStack users can set their preferred language. Thanks to great community contributors, Current languages built into BookStack include EN, FR, DE, ES, IT, JA, NL, PL, RU and more.
+                    BookStack users can set their preferred language. Thanks to great community contributors, current languages built into BookStack include EN, FR, DE, ES, IT, JA, NL, PL, RU and <a href="https://crowdin.com/project/bookstack" target="_blank">many more</a>.
                 </p>
             </div>
             <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/edit.svg"}}</span>Optional Markdown Editor</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/edit.svg"}}</span>Optional Markdown Editor</h4>
                 <p>
                    If you prefer to write in Markdown then BookStack supports you. A markdown editor is provided and includes a live-preview as you write your documentation.
                 </p>
             </div>
             <div class="col-sm-4" >
-                <h4><span class="icon">{{partial "icon/lock.svg"}}</span>Integrated Authentication</h4>
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/lock.svg"}}</span>Integrated Authentication</h4>
                 <p>
-                       As well as the default email/password login social providers such as GitHub, Google, Slack, AzureAD and more can be used. Okta and LDAP options are available for enterprise environments.
+                       As well as the default email/password login social providers such as GitHub, Google, Slack, AzureAD and more can be used. Okta, SAML2 and LDAP options are available for enterprise environments.
+                </p>
+            </div>
+        </div>
+        <p>
+            <br>
+        </p>
+        <div class="row">
+               <div class="col-sm-4" >
+                   <h4><span class="icon" aria-hidden="true">{{partial "icon/directions_boat.svg"}}</span>Powerful Features</h4>
+                   <p>
+                       On top of the powerful search and linking there is also cross-book sorting, page revisions and image management. A full role and permission system allow you to lock down content and actions as required.
+                   </p>
+               </div>
+            <div class="col-sm-4" >
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/mfa.svg"}}</span>Multi-Factor Authentication</h4>
+                <p>
+                       MFA is built in & can be enforced at a per-role level where desired.
+                       MFA options include TOTP (Google/Microsoft Authenticator, Authy, etc...) and static backup codes.
+                </p>
+            </div>
+            <div class="col-sm-4" >
+                <h4><span class="icon" aria-hidden="true">{{partial "icon/dark-mode.svg"}}</span>Dark & Light Modes</h4>
+                <p>
+                   BookStack provides it's user interface in both a light theme and a dark theme, saving the eyes
+                   of those that prefer to work in the shadows. This is configurable at a user level.
                 </p>
             </div>
         </div>
     </div>
+
+
+       <div class="shaded-border md-margin-top">
+               <div class="container md-margin-top">
+                       <div class="row">
+                               <div class="col-sm-4">
+                                       <h2 id="sponsors" class="nomargin margin-bottom">Project Sponsors</h2>
+                                       <p>
+                                               Shown are our bronze, silver and gold project sponsors. <br>
+                                               Big thanks to these companies for supporting the project. <br>
+                                               Note: Listed services are not tested, vetted nor supported by the official BookStack project in any manner. <br>
+                                               <br>
+                                               <a target="_blank" href="https://github.com/sponsors/ssddanbrown">View all sponsors &raquo;</a>
+                                       </p>
+                               </div>
+                               <div class="col-sm-8 text-center">
+                                       <p><br></p>
+                                       <h5>Our Silver Sponsors</h5>
+
+                                       <div class="sponsor-list">
+
+                                               <a href="https://www.diagrams.net/" target="_blank">
+                                                       <img width="400" src="/images/sponsors/diagramsnet.png" alt="Diagrams.net logo">
+                                               </a>
+                                               <a href="https://cloudabove.com/hosting" target="_blank">
+                                                       <img height="100" src="/images/sponsors/cloudabove.svg" alt="Cloudabove logo">
+                                               </a>
+
+                                       </div>
+
+                                       <p><br></p>
+
+                                       <h5>Our Bronze Sponsor</h5>
+
+                                       <div class="sponsor-list">
+                                               
+                                               <a href="https://www.stellarhosted.com/bookstack/" target="_blank">
+                                                       <img width="280" src="/images/sponsors/stellarhosted.png" alt="Stellar Hosted Logo">
+                                               </a>
+
+                                       </div>
+
+                               </div>
+                       </div>
+
+               </div>
+       </div>
+
+
+
     <div class="shaded shaded-border md-margin-top padded-vertical large">
        <div class="container">
                <div class="row">
                        <div class="col-sm-6 text-center">
                                        <div class="demo-box text-left " >
                                                <label>Demo site url</label> <br><a href="https://demo.bookstackapp.com" target="_blank">https://demo.bookstackapp.com</a> <br>
-                                               <label>Admin email</label> <br><input type="text" onclick="this.select();" value="admin@example.com" readonly="true"><br>
-                                               <label>Admin password</label> <br><input type="text" onclick="this.select();" value="password" readonly="true"> <br><br>
+                                               <label for="demo-email">Admin email</label> <br><input id="demo-email" type="text" onclick="this.select();" value="admin@example.com" readonly="true"><br>
+                                               <label for="demo-password">Admin password</label> <br><input id="demo-password" type="text" onclick="this.select();" value="password" readonly="true"> <br><br>
                                                <a href="https://demo.bookstackapp.com/login?email=admin@example.com&password=password" class="button" target="_blank">Open demo site</a>
                                        </div>
                        </div>
                                <div class="row">
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Page View</h4>
-                                               <p>How core content is viewed in BookStack</p>
                                                <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>How core content is viewed in BookStack</figcaption>
                                                    <a href="images/screenshots/page-view.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_page-view.png" alt="Page View" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Page Editor</h4>
-                                               <p>The WYSIWYG interface for editing pages</p>
-                                               <figure  itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                               <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>The WYSIWYG interface for editing pages</figcaption>
                                                    <a href="images/screenshots/page-edit.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_page-edit.png" alt="Page Editing" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Image Manager</h4>
-                                               <p>How images are uploaded and managed</p>
-                                               <figure  itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                               <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>How images are uploaded and managed</figcaption>
                                                    <a href="images/screenshots/image-manager.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_image-manager.png" alt="Image Manager" loading="lazy">
                                                    </a>
                                <div class="row">
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>All Books Overview</h4>
-                                               <p>An overview of the top-level categorisation</p>
-                                               <figure  itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                               <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>An overview of the top-level categorisation</figcaption>
                                                    <a href="images/screenshots/books-view.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_books-view.png" alt="View of all books" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Book Overview</h4>
-                                               <p>A view of the main content container: A book</p>
-                                               <figure  itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                               <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>A view of the main content container: A book</figcaption>
                                                    <a href="images/screenshots/book-overview.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_book-overview.png" alt="Book Overview" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Book Sorting</h4>
-                                               <p>How created content can be sorted within a book</p>
-                                               <figure  itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                               <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>How created content can be sorted within a book</figcaption>
                                                    <a href="images/screenshots/book-sorting.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_book-sorting.png" alt="Book Content Sorting View" loading="lazy">
                                                    </a>
                                <div class="row">
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Global Search</h4>
-                                               <p>The main interface for searching created content</p>
                                                <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>The main interface for searching created content</figcaption>
                                                    <a href="images/screenshots/search.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_search.png" alt="Searching all content" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>App Settings</h4>
-                                               <p>A view the of application system settings</p>
                                                <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>A view the of application system settings</figcaption>
                                                    <a href="images/screenshots/settings-view.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_settings-view.png" alt="Settings View" loading="lazy">
                                                    </a>
                                        </div>
                                        <div class="col-sm-4 col-sm-offset-0 col-xs-8 col-xs-offset-2">
                                                <h4>Profile Page</h4>
-                                               <p>A user profile page, showing their activity and content</p>
                                                <figure itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
+                                                       <figcaption>A user profile page, showing their activity and content</figcaption>
                                                    <a href="images/screenshots/profile-page.png" data-size="1666x910">
                                                        <img src="images/screenshots/thumb_profile-page.png" alt="Profile Editing Screen" loading="lazy">
                                                    </a>
        </div>
 
 
-    <div class="shaded shaded-border md-margin-top padded-vertical large">
+    <div class="shaded shaded-border md-margin-top padded-vertical large" id="blog-list">
         <div class="container">
             <h2>Latest From The Blog</h2>
 
-                       {{ range first 4 ( where .Site.Pages "Section" "blog") }}
-                               <div class="blogpost-card">
-                                       <a href="{{.Permalink}}">
-                                               {{ if .Params.image }}
-                                                       <div class="image" style="background-image: url({{.Params.image}});"></div>
-                                               {{ end }}
-                                               <div class="text">{{ .Title }}</div>
-                                       </a>
-                               </div>
-                       {{ end }}
+                       <div class="blogpost-cards">
+                               {{ range first 4 ( where .Site.Pages "Section" "blog") }}
+                                       <div class="blogpost-card">
+                                               <a href="{{.Permalink}}">
+                                                       {{ if .Params.image }}
+                                                           {{ $image := resources.Get .Params.image }}
+                                                               {{ $sized := $image.Resize "604x q90" }}
+                                                               <div class="image">
+                                                                       <img src="{{$sized.RelPermalink}}" loading="lazy" width="302" height="160" alt="{{ .Title }}">
+                                                               </div>
+                                                       {{ end }}
+                                                       <div class="text">{{ .Title }}</div>
+                                               </a>
+                                       </div>
+                               {{ end }}
+                       </div>
 
                        <div class="clearfix"></div>
 
index eaf1290e0fb6c21fcf4f977581817fbb8b04796e..4a68eb0db32b5ebf0e96c4d0ab87bd8b5b31f905 100644 (file)
@@ -4,39 +4,68 @@
         <div class="container">
             <div class="row">
                <div class="col-lg-4">
-                       <p class="muted text-small">
+                       <p class="text-small">
+                        <iframe src="https://github.com/sponsors/ssddanbrown/button" title="Sponsor ssddanbrown" height="35" width="116" style="border: 0;" loading="lazy"></iframe>
+                        <br><br>
                            BookStack - Created By <a href="https://danb.me" title="danb.me" target="_blank" rel="noopener">Dan Brown</a> and developed with the <a href="https://github.com/BookStackApp/BookStack/graphs/contributors" target="_blank" rel="noopener">community</a>.
-                        <br>
-                        <!-- Thanks to <a href="https://www.browserstack.com/" target="_blank">BrowserStack</a> for providing easy cross-browser testing. <br> -->
                         Page generated with <a href="https://gohugo.io">hugo</a>, Site source can be found <a href="https://github.com/BookStackApp/website" rel="noopener" target="_blank">here on GitHub</a>.
-                       </p>
+                        <br><br>
+                        This website uses a self-hosted instance of <a href="https://plausible.io/" target="_blank" rel="noreferrer">Plausible</a> for analytics.
+                        Our site stats can <a href="https://analytics.bookstackapp.com/bookstackapp.com" target="_blank" rel="noreferrer">be viewed here</a>. 
+                        We also use <a href="https://www.meilisearch.com/" target="_blank" rel="noreferrer">MeiliSearch</a> for providing better search.
+                        <br><br>
+                        Latest Blog Post: 
+                        {{ range first 1 ( where .Site.Pages "Section" "blog") }}
+                                <a href="{{.Permalink}}">{{ .Title }}</a>
+                        {{ end }}
+                    </p>
                </div>
-               <div class="col-lg-8 col-md-9 menu">
-                    <a href="{{.Site.BaseURL}}docs"><span class="icon">{{partial "icon/book.svg"}}</span>Documentation</a>
-                    <a href="{{.Site.BaseURL}}#features"><span class="icon">{{partial "icon/star.svg"}}</span>Features</a>
-                    <a href="{{.Site.BaseURL}}#demo"><span class="icon">{{partial "icon/touch_app.svg"}}</span>Demo</a>
-                    <a href="https://github.com/BookStackApp/BookStack" target="_blank"><span class="icon">{{partial "icon/github.svg"}}</span>Github</a>
-                    <a href="https://discord.gg/ztkBqR2" target="_blank"><span class="icon">{{partial "icon/discord.svg"}}</span>Discord</a>
-                    <a href="{{.Site.BaseURL}}blog" target="_blank"><span class="icon">{{partial "icon/rss_feed.svg"}}</span>Blog</a>
+                <div class="col-lg-1"></div>
+               <div class="col-xs-6 col-sm-4 col-lg-2">
+                    <h5>On the site</h5>
+                    <ul class="nav-list">
+                        <li><a href="{{.Site.BaseURL}}docs">Documentation</a></li>
+                        <li><a href="{{.Site.BaseURL}}#features">Our Features</a></li>
+                        <li><a href="{{.Site.BaseURL}}#demo">View the Demo</a></li>
+                        <li><a href="{{.Site.BaseURL}}blog" target="_blank">Read our Blog</a></li>
+                    </ul>
+               </div>
+                <div class="col-xs-6 col-sm-4 col-lg-2">
+                    <h5>Follow Us</h5>
+                    <ul class="nav-list">
+                        <li><a href="https://github.com/BookStackApp/BookStack" target="_blank">Github</a></li>
+                        <li><a href="https://discord.gg/ztkBqR2" target="_blank">Discord</a></li>
+                        <li><a href="https://twitter.com/bookstack_app" target="_blank">Twitter</a></li>
+                        <li><a href="https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g" target="_blank">YouTube</a></li>
+                    </ul>
+               </div>
+                <div class="col-6 col-sm-4 col-lg-3">
+                    <h5>Information</h5>
+                    <ul class="nav-list">
+                        <li><a href="/about/confluence-alternative/">A Confluence Alternative</a></li>
+                        <li><a href="/about/open-source-documentation-software/">Open Source Documentation Software</a></li>
+                    </ul>
                </div>
             </div>
-            <p class="text-small">
-                This website uses Google Analytics for reporting on site metrics and Algolia for providing better search. Third party cookies are not used and IP addresses Google Analytics receives are anonymized to protect your Privacy.
-            </p>
+
         </div>
     </footer>
 
-    <script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
-    <script type="text/javascript">
-    if (document.querySelector('.doc-search-input') !== null) {
-        docsearch({
-                apiKey: '0401e1a1383bf8baa64b07a6eeecce0c',
-                indexName: 'bookstackapp',
-                inputSelector: '.doc-search-input',
-                debug: false // Set debug to true if you want to inspect the dropdown
-            });
-    }
+
+    <script src="{{.Site.BaseURL}}libs/docs-searchbar.min.js"></script>
+    <script>
+        const searchInputSelector = '.doc-search-input,.blog-search-input';
+        if (document.querySelector(searchInputSelector) !== null) {
+            docsSearchBar({
+                hostUrl: 'https://search.bookstackapp.com',
+                apiKey: '9ea2b6fd2a41f5243f741746d5281b7ff85451d51f4d50a270426a6dd430156d',
+                indexUid: 'docs',
+                inputSelector: searchInputSelector,
+                debug: false, // Set debug to true if you want to inspect the dropdown
+            })
+        }
     </script>
+
     <script src="{{.Site.BaseURL}}libs/codemirror/codemirror.min.js"></script>
     <script src="{{.Site.BaseURL}}libs/codemirror/modes.js"></script>
     <script src="{{.Site.BaseURL}}js/script.js"></script>
index 98817effd23cd75fd4f99f70d722a84406685658..3c766b21805a63c5557fb8f7f480a12d596d4140 100644 (file)
@@ -7,18 +7,18 @@
 
     {{ partial "twitter_card.html" . }}
 
-       <meta property="og:title" content="{{ if ne .RelPermalink "/" }} {{ .Title }} &middot; {{ end }} {{ .Site.Title }}" />
+       <meta property="og:title" content="{{ if ne .RelPermalink "/" }}{{ .Title }} &middot; {{ end }} {{ .Site.Title }}" />
        <meta property="og:site_name" content="{{ .Site.Title }}" />
        <meta property="og:url" content="{{ .Permalink }}" />
 
 
     {{ $baseURL := .Site.BaseURL }}
-    {{ if .IsPage }}
+    {{ if and (.IsPage) (ne .Type "about")  }}
 
     {{ with .Params.image }}
         <!-- Twitter summary card with large image must be at least 280x150px -->
         <meta name="twitter:card" content="summary_large_image"/>
-        <meta name="og:image" content="{{ $baseURL }}{{ . }}"/>
+        <meta  name="image" property="og:image" content="{{ . | absURL }}"/>
     {{ end }}
 
        <meta property="og:type" content="article" />
       {{ if ne .RelPermalink "/" }} {{ .Title }} &middot; {{ end }} {{ .Site.Title }}
     </title>
 
-    <meta name="description" content="{{ .Site.Params.description }}" />
+    {{ $description := .Site.Params.Description }}
+    {{ if and (.Params.Description) (.IsPage) }}
+      {{ $description = .Params.Description }}
+    {{ else if .IsPage}}
+        {{ $description = .Summary }}
+    {{ end }}
+    <meta name="description" content="{{ $description }}" />
 
     <meta name="HandheldFriendly" content="True" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -52,8 +58,7 @@
     <link rel="icon" type="image/png" href="/images/favicon-96x96.png" sizes="96x96" />
     <link rel="icon" type="image/png" href="/images/favicon-32x32.png" sizes="32x32" />
 
-    <link rel="stylesheet" type="text/css" href="{{.Site.BaseURL}}css/styles.css?v=1.3" />
-
+    <link rel="stylesheet" type="text/css" href="{{.Site.BaseURL}}css/styles.css?v={{ now.Format "2006-01-02T15:04:05" }}" />
 
     {{ if .Site.Params.RSSLink}}
         <link href="{{.Site.Params.RSSLink }}" rel="alternate" type="application/rss+xml" title="{{ .Site.Title }}" />
 
     <link rel="canonical" href="{{ .Permalink }}" />
 
-    {{with  .Site.Params.googleAnalyticsUserID }}
-    <script>
-
-        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-        })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
-
-        var GA_LOCAL_STORAGE_KEY = 'ga:clientId';
-
-        if (window.localStorage) {
-            ga('create', '{{.}}', {
-                'storage': 'none',
-                'clientId': localStorage.getItem(GA_LOCAL_STORAGE_KEY)
-            });
-            ga(function (tracker) {
-                localStorage.setItem(GA_LOCAL_STORAGE_KEY, tracker.get('clientId'));
-            });
-        }
-        else {
-            ga('create', '{{.}}', {'storage': 'none'});
-        }
-
-        ga('set', 'anonymizeIp', true);
-        ga('send', 'pageview');
-      
-    </script>
-    {{end}}
+    <script async defer data-domain="bookstackapp.com" src="https://analytics.bookstackapp.com/js/plausible.js"></script>
 
-    <!-- at the end of the HEAD -->
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
+    <link rel="stylesheet" type="text/css" href="{{.Site.BaseURL}}libs/docs-searchbar.min.css" />
 
     {{ if .Site.Params.customHeaderPartial }}
         {{ partial .Site.Params.customHeaderPartial . }}
 </head>
 <body class="nav-closed">
 
-      <header id="header" class="header">
-        <div class="container">
-            <div class="row fix-mobile">
-                <div class="col-md-3 col-sm-6 col-xs-8">
-                    <div class="logo">
-                        <a href="{{.Site.BaseURL}}">
-                            {{partial "icon/logo.svg"}}
-                            <h1>BookStack</h1>
-                        </a>
-
-                    </div>
+  <header id="header" class="header clearfix">
+    <div class="container">
+      <div class="logo">
+          <a href="{{.Site.BaseURL}}">
+              {{partial "icon/logo.svg"}}
+              <div>BookStack</div>
+          </a>
+          <button tabindex="1" id="menu-button" class="button muted" type="button">{{partial "icon/menu.svg"}}</button>
+      </div>
+      <div class="header-search-section">
+        <input type="text" placeholder="Search site" class="doc-search-input">
+      </div>
+      <div class="main-nav">
+          <div class="nav-dropdown-wrap">
+            <a href="#"  onclick="event.preventDefault()" class="nav-dropdown-trigger">Platform</a>
+            <div class="nav-dropdown-menu">
+              <a href="/#features" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/features.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Features</div>
+                  <p>An overview of the core features of BookStack</p>
+                </div>
+              </a>
+              <a href="/#demo" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/demo.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Demo</div>
+                  <p>Give BookStack a test drive on our demo instance</p>
+                </div>
+              </a>
+              <a href="/docs" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/book.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Documentation</div>
+                  <p>Guidance for managing and using BookStack</p>
+                </div>
+              </a>
+              <a href="/blog" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/rss_feed.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Our Blog</div>
+                  <p>Get the latest project news from our blog</p>
+                </div>
+              </a>
+            </div>
+          </div>
+          <div class="nav-dropdown-wrap">
+            <a href="/support"  onclick="event.preventDefault()" class="nav-dropdown-trigger">Support</a>
+            <div class="nav-dropdown-menu">
+              <a href="/docs" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/book.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Documentation</div>
+                  <p>Get support from our user and admin documentation</p>
+                </div>
+              </a>
+              <a href="/support" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/support.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Support Plans</div>
+                  <p>See our range of support plans for BookStack</p>
+                </div>
+              </a>
+              <a href="https://www.youtube.com/c/BookStackApp" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/youtube.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Video Guides</div>
+                  <p>Watch our video guides on YouTube</p>
+                </div>
+              </a>
+              <a href="https://github.com/BookStackApp/BookStack/issues" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/github.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">GitHub Issues</div>
+                  <p>Find or report issues on the GitHub project</p>
+                </div>
+              </a>
+            </div>
+          </div>
+          <div class="nav-dropdown-wrap">
+            <a href="#" onclick="event.preventDefault()" class="nav-dropdown-trigger">Community</a>
+            <div class="nav-dropdown-menu">
+              <a href="https://github.com/BookStackApp/BookStack" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/github.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">GitHub</div>
+                  <p>Star the project on GitHub and follow us</p>
+                </div>
+              </a>
+              <a href="https://discord.gg/ztkBqR2" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/discord.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Discord</div>
+                  <p>Chat with the development team and other users</p>
+                </div>
+              </a>
+              <a href="https://twitter.com/bookstack_app" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/twitter.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Twitter</div>
+                  <p>See updates and shout us out on Twitter</p>
+                </div>
+              </a>
+              <a href="https://www.youtube.com/c/BookStackApp" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/youtube.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">YouTube</div>
+                  <p>Watch and comment on our YouTube videos</p>
+                </div>
+              </a>
+              <a href="https://www.reddit.com/r/bookstack" target="_blank" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/reddit.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Reddit</div>
+                  <p>Join our community on the BookStack subreddit</p>
                 </div>
-                <div class="col-md-9 col-sm-6 menu col-xs-4">
-                    <button tabindex="1" id="menu-button" class="button muted" type="button">{{partial "icon/menu.svg"}}</button>
-                    <div class="inner">
-                        <a href="/docs"><span class="icon">{{partial "icon/book.svg"}}</span>Documentation</a>
-                        <a href="/#features"><span class="icon">{{partial "icon/star.svg"}}</span>Features</a>
-                        <a href="/#demo"><span class="icon">{{partial "icon/touch_app.svg"}}</span>Demo</a>
-                        <a href="https://github.com/BookStackApp/BookStack" target="_blank"><span class="icon">{{partial "icon/github.svg"}}</span>Github</a>
-                        <a href="https://discord.gg/ztkBqR2" target="_blank"><span class="icon">{{partial "icon/discord.svg"}}</span>Discord</a>
-                        <a href="/blog"><span class="icon">{{partial "icon/rss_feed.svg"}}</span>Blog</a>
-                    </div>
+              </a>
+              <a href="/blog" class="nav-dropdown-item">
+                <div class="nav-dropdown-item-icon" aria-hidden="true">{{partial "icon/rss_feed.svg"}}</div>
+                <div>
+                  <div class="nav-dropdown-item-title">Our Blog</div>
+                  <p>Keep up with the latest developments via our blog</p>
                 </div>
+              </a>
             </div>
-        </div>
-    </header>
+          </div>
+      </div>
+    </div>
+  </header>
 
   <div id="content">
index d167160dfef4d5c278cfa814d62990a5fd07a0ae..3a302ba8ea574b8a18c6c3ebcc7fcba92763978d 100644 (file)
@@ -1,4 +1,3 @@
 <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
-    <path d="M0 0h24v24H0V0z" fill="none"/>
     <path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/>
 </svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/dark-mode.svg b/themes/bookstack/layouts/partials/icon/dark-mode.svg
new file mode 100644 (file)
index 0000000..b463e78
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24"><path d="M10 2c-1.82 0-3.53.5-5 1.35C7.99 5.08 10 8.3 10 12s-2.01 6.92-5 8.65C6.47 21.5 8.18 22 10 22c5.52 0 10-4.48 10-10S15.52 2 10 2z"/></svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/drawing.svg b/themes/bookstack/layouts/partials/icon/drawing.svg
new file mode 100644 (file)
index 0000000..6d05465
--- /dev/null
@@ -0,0 +1,3 @@
+<svg viewBox="0 0 24 24" height="24"  xmlns="http://www.w3.org/2000/svg">
+    <path d="M23 7V1h-6v2H7V1H1v6h2v10H1v6h6v-2h10v2h6v-6h-2V7h2zM3 3h2v2H3V3zm2 18H3v-2h2v2zm12-2H7v-2H5V7h2V5h10v2h2v10h-2v2zm4 2h-2v-2h2v2zM19 5V3h2v2h-2zm-5.27 9h-3.49l-.73 2H7.89l3.4-9h1.4l3.41 9h-1.63l-.74-2zm-3.04-1.26h2.61L12 8.91l-1.31 3.83z"/>
+</svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/email.svg b/themes/bookstack/layouts/partials/icon/email.svg
new file mode 100644 (file)
index 0000000..afbd592
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
\ No newline at end of file
index 962a98f42582f5949d5c589b9a3c81a4cb5c39d2..0cb11f026a509612a6aa1339d14018b6f3851d5a 100644 (file)
@@ -1,13 +1,13 @@
-<svg id="svg4200" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="61.699mm" width="65.023mm" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 230.39711 218.6199">
- <g id="layer1" stroke-linejoin="round" fill-rule="evenodd" transform="translate(-245.27 -58.434)" stroke="#0288d1" stroke-width="6" fill="#fff">
+<svg xmlns="http://www.w3.org/2000/svg" height="61.699mm" width="65.023mm" version="1.1" viewBox="0 0 230.39711 218.6199" aria-label="BookStack Logo">
+ <g stroke-linejoin="round" fill-rule="evenodd" transform="translate(-245.27 -58.434)" stroke="#0288d1" stroke-width="6" fill="#fff">
   <g stroke-linecap="round">
-   <path id="path5686" d="m343.79 238.6 128.88-74.409-92.058-53.15-128.88 74.409z"/>
-   <path id="path5688" d="m251.73 185.45v21.26l92.058 53.15 128.88-74.409v-21.26"/>
-   <path id="path5694" d="m343.79 274.03-92.058-53.15s-7.5-16.918 0-28.346l92.058 53.15 128.88-74.409v28.346l-128.88 74.409"/>
-   <path id="path5686-5" d="m343.79 188.99 128.88-74.41-92.06-53.146-128.88 74.406z"/>
-   <path id="path5692-7" d="m343.79 188.99 128.88-74.409 0.00001 28.346-128.88 74.409-92.058-53.15s-6.0714-17.632 0-28.346z"/>
-   <path id="path5694-5" d="m343.79 245.69-92.058-53.15s-7.5-16.918 0-28.346l92.058 53.15 128.88-74.409-0.00001 28.346-128.88 74.409"/>
+   <path d="m343.79 238.6 128.88-74.409-92.058-53.15-128.88 74.409z"/>
+   <path d="m251.73 185.45v21.26l92.058 53.15 128.88-74.409v-21.26"/>
+   <path d="m343.79 274.03-92.058-53.15s-7.5-16.918 0-28.346l92.058 53.15 128.88-74.409v28.346l-128.88 74.409"/>
+   <path d="m343.79 188.99 128.88-74.41-92.06-53.146-128.88 74.406z"/>
+   <path d="m343.79 188.99 128.88-74.409 0.00001 28.346-128.88 74.409-92.058-53.15s-6.0714-17.632 0-28.346z"/>
+   <path d="m343.79 245.69-92.058-53.15s-7.5-16.918 0-28.346l92.058 53.15 128.88-74.409-0.00001 28.346-128.88 74.409"/>
   </g>
-  <path id="path5831" d="m402.09 73.836-55.234 31.89 21.48 1.7716 3.0686 12.402 55.235-31.89z"/>
+  <path d="m402.09 73.836-55.234 31.89 21.48 1.7716 3.0686 12.402 55.235-31.89z"/>
  </g>
 </svg>
diff --git a/themes/bookstack/layouts/partials/icon/mfa.svg b/themes/bookstack/layouts/partials/icon/mfa.svg
new file mode 100644 (file)
index 0000000..f89d0b1
--- /dev/null
@@ -0,0 +1,3 @@
+<svg viewBox="0 0 24 24" height="24" xmlns="http://www.w3.org/2000/svg">
+    <path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
+</svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/reddit.svg b/themes/bookstack/layouts/partials/icon/reddit.svg
new file mode 100644 (file)
index 0000000..c708f71
--- /dev/null
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-reddit" viewBox="0 0 16 16">
+  <path d="M6.167 8a.831.831 0 0 0-.83.83c0 .459.372.84.83.831a.831.831 0 0 0 0-1.661zm1.843 3.647c.315 0 1.403-.038 1.976-.611a.232.232 0 0 0 0-.306.213.213 0 0 0-.306 0c-.353.363-1.126.487-1.67.487-.545 0-1.308-.124-1.671-.487a.213.213 0 0 0-.306 0 .213.213 0 0 0 0 .306c.564.563 1.652.61 1.977.61zm.992-2.807c0 .458.373.83.831.83.458 0 .83-.381.83-.83a.831.831 0 0 0-1.66 0z"/>
+  <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.828-1.165c-.315 0-.602.124-.812.325-.801-.573-1.9-.945-3.121-.993l.534-2.501 1.738.372a.83.83 0 1 0 .83-.869.83.83 0 0 0-.744.468l-1.938-.41a.203.203 0 0 0-.153.028.186.186 0 0 0-.086.134l-.592 2.788c-1.24.038-2.358.41-3.17.992-.21-.2-.496-.324-.81-.324a1.163 1.163 0 0 0-.478 2.224c-.02.115-.029.23-.029.353 0 1.795 2.091 3.256 4.669 3.256 2.577 0 4.668-1.451 4.668-3.256 0-.114-.01-.238-.029-.353.401-.181.688-.592.688-1.069 0-.65-.525-1.165-1.165-1.165z"/>
+</svg>
diff --git a/themes/bookstack/layouts/partials/icon/support.svg b/themes/bookstack/layouts/partials/icon/support.svg
new file mode 100644 (file)
index 0000000..73a545e
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><rect height="11" width="4" x="1" y="11"/><path d="M16,3.25C16.65,2.49,17.66,2,18.7,2C20.55,2,22,3.45,22,5.3c0,2.27-2.91,4.9-6,7.7c-3.09-2.81-6-5.44-6-7.7 C10,3.45,11.45,2,13.3,2C14.34,2,15.35,2.49,16,3.25z"/><path d="M20,17h-7l-2.09-0.73l0.33-0.94L13,16h2.82c0.65,0,1.18-0.53,1.18-1.18v0c0-0.49-0.31-0.93-0.77-1.11L8.97,11H7v9.02 L14,22l8.01-3v0C22,17.9,21.11,17,20,17z"/></g></g></svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/twitter.svg b/themes/bookstack/layouts/partials/icon/twitter.svg
new file mode 100644 (file)
index 0000000..829c6fe
--- /dev/null
@@ -0,0 +1 @@
+<svg width="24" viewBox="0 0 248 24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M188.134-20.903c.098 1.422.098 2.844.098 4.279 0 43.725-33.287 94.154-94.154 94.154v-.026a93.68 93.68 0 0 1-50.724-14.835c2.614.314 5.242.472 7.876.478a66.448 66.448 0 0 0 41.098-14.193 33.133 33.133 0 0 1-30.915-22.98 32.985 32.985 0 0 0 14.94-.57C60.915 22.285 49.808 8.721 49.808-7.03v-.42a32.881 32.881 0 0 0 15.019 4.142c-14.54-9.718-19.023-29.061-10.242-44.185a93.92 93.92 0 0 0 68.2 34.572 33.112 33.112 0 0 1 9.573-31.616c13.328-12.529 34.29-11.887 46.818 1.435a66.402 66.402 0 0 0 21.014-8.034 33.215 33.215 0 0 1-14.546 18.302 65.81 65.81 0 0 0 19.002-5.21 67.226 67.226 0 0 1-16.512 17.142z"/></svg>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/icon/youtube.svg b/themes/bookstack/layouts/partials/icon/youtube.svg
new file mode 100644 (file)
index 0000000..c491dbf
--- /dev/null
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-youtube" viewBox="0 0 16 16">
+  <path d="M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408L6.4 5.209z"/>
+</svg>
diff --git a/themes/bookstack/layouts/partials/mailchimp.html b/themes/bookstack/layouts/partials/mailchimp.html
deleted file mode 100644 (file)
index e1d3f9c..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-<!-- Begin MailChimp Signup Form -->
-
-<div id="mc_embed_signup">
-    <form action="https://bookstackapp.us14.list-manage.com/subscribe/post?u=18917f477406e2be3f062086a&amp;id=7de4fb0c79" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
-        <h4>Subscribe to Updates</h4>
-        <p>This is a weekly newsletter, summarising content from the blog.</p>
-        <div class="mc-field-group">
-            <input placeholder="Email address" type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
-            <button class="button">Subscribe</button>
-        </div>
-        <div id="mce-responses" class="clear">
-            <div class="response" id="mce-error-response" style="display:none"></div>
-            <div class="response" id="mce-success-response" style="display:none"></div>
-        </div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
-        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_18917f477406e2be3f062086a_7de4fb0c79" tabindex="-1" value=""></div>
-        <p class="text-small">This service uses MailChimp to manage sending emails so any information provided will go into their systems. Feel free to review their <a href="https://mailchimp.com/legal/privacy" target="_blank">Privacy Policy</a> and <a href="https://mailchimp.com/legal/terms" target="_blank">Terms</a>. You'll be able to opt-out via a link in the email whenever you want. If you don't trust MailChimp or any BookStack maintainers with the data you provide or emit then please don't sign up.</p>
-        <p class="text-small">A security-specific mailing list <a href="http://eepurl.com/glIh8z" target="_blank">can be found here</a>.</p>
-    </form>
-</div>
\ No newline at end of file
index 8b6ffbffc36ca717fa311b26d5396ff1b1ef5458..cf97d180ae4d8fac34aa9bfe92c1f077eba1fd2b 100644 (file)
@@ -1,35 +1,52 @@
-<h4>Setup</h4>
-<ul>
-       <li><a href="/docs/admin/installation">Installation</a></li>
-       <li><a href="/docs/admin/security">Security</a></li>
-       <li><a href="/docs/admin/multi-instance">Multiple Instances</a></li>
-       <li><a href="/docs/admin/subdirectory-setup">Subdirectory Setup</a></li>
-</ul>
+<div>
+       <h4>Setup</h4>
+       <ul>
+               <li><a href="/docs/admin/installation">Installation</a></li>
+               <li><a href="/docs/admin/security">Security</a></li>
+               <li><a href="/docs/admin/multi-instance">Multiple Instances</a></li>
+               <li><a href="/docs/admin/subdirectory-setup">Subdirectory Setup</a></li>
+       </ul>
+</div>
 
-<h4>Maintenance</h4>
-<ul>
-       <li><a href="/docs/admin/updates">Updates</a></li>
-       <li><a href="/docs/admin/commands">Commands</a></li>
-       <li><a href="/docs/admin/backup-restore">Backup &amp; Restore</a></li>
-       <li><a href="/docs/admin/debugging">Debugging</a></li>
-</ul>
+<div>
+       <h4>Maintenance</h4>
+       <ul>
+               <li><a href="/docs/admin/updates">Updates</a></li>
+               <li><a href="/docs/admin/commands">Commands</a></li>
+               <li><a href="/docs/admin/backup-restore">Backup &amp; Restore</a></li>
+               <li><a href="/docs/admin/debugging">Debugging</a></li>
+       </ul>
+</div>
 
-<h4>Authentication</h4>
-<ul>
-       <li><a href="/docs/admin/saml2-auth">SAML 2.0</a></li>
-       <li><a href="/docs/admin/ldap-auth">LDAP</a></li>
-       <li><a href="/docs/admin/social-auth">Third Party & Social</a></li>
-</ul>
+<div>
+       <h4>Authentication</h4>
+       <ul>
+               <li><a href="/docs/admin/oidc-auth">OpenID Connect</a></li>
+               <li><a href="/docs/admin/saml2-auth">SAML 2.0</a></li>
+               <li><a href="/docs/admin/ldap-auth">LDAP</a></li>
+               <li><a href="/docs/admin/social-auth">Third Party & Social</a></li>
+       </ul>
+</div>
 
-<h4>Configuration</h4>
-<ul>
-       <li><a href="/docs/admin/email-config">Email Configuration</a></li>
-       <li><a href="/docs/admin/visual-customisation">Visual Customisation</a></li>
-       <li><a href="/docs/admin/language-config">Language &amp; Locale</a></li>
-       <li><a href="/docs/admin/upload-config">File Uploads</a></li>
-       <li><a href="/docs/admin/cache-session-config">Caching &amp; Sessions</a></li>
-       <li><a href="/docs/admin/pdf-rendering">PDF Rendering</a></li>
-       <li><a href="/docs/admin/ut8mb4-support">UTF8mb4/Emoji Support</a></li>
-       <li><a href="/docs/admin/hacking-bookstack">Hacking BookStack</a></li>
-       <li><a href="/docs/admin/other-config">Other Configuration</a></li>
-</ul>
+<div>
+       <h4>Configuration</h4>
+       <div class="row">
+               <div class="col-md-6">
+                       <ul>
+                               <li><a href="/docs/admin/email-webhooks">Email & Webhooks</a></li>
+                               <li><a href="/docs/admin/visual-customisation">Visual Customisation</a></li>
+                               <li><a href="/docs/admin/language-config">Language &amp; Locale</a></li>
+                               <li><a href="/docs/admin/upload-config">File Uploads</a></li>
+                               <li><a href="/docs/admin/cache-session-config">Caching &amp; Sessions</a></li>
+                       </ul>
+               </div>
+               <div class="col-md-6">
+                       <ul>
+                               <li><a href="/docs/admin/pdf-rendering">PDF Rendering</a></li>
+                               <li><a href="/docs/admin/ut8mb4-support">UTF8mb4/Emoji Support</a></li>
+                               <li><a href="/docs/admin/hacking-bookstack">Hacking BookStack</a></li>
+                               <li><a href="/docs/admin/other-config">Other Configuration</a></li>
+                       </ul>
+               </div>
+       </div>
+</div>
\ No newline at end of file
index 01e7a78adaec9b3b6667cdc37149c4a291175574..dc63ecc16cfa4913458c4da0649852864c143ce3 100644 (file)
@@ -1,16 +1,25 @@
+<div>
+       <h4>Getting Started</h4>
+       <ul>
+               <li><a href="/docs/user/content-overview">Content Overview</a></li>
+               <li><a href="/docs/user/organising-content">Organising Content</a></li>
+       </ul>
+</div>
 
-<h4>Getting Started</h4>
-<ul>
-       <li><a href="/docs/user/content-overview">Content Overview</a></li>
-       <li><a href="/docs/user/organising-content">Organising Content</a></li>
-</ul>
-<h4>Common Functions</h4>
-<ul>
-       <li><a href="/docs/user/searching">Searching Content</a></li>
-       <li><a href="/docs/user/wysiwyg-editor">Default (WYSIWYG) Editor</a></li>
-       <li><a href="/docs/user/markdown-editor">Markdown Editor</a></li>
-</ul>
-<h4>Advanced Features</h4>
-<ul>
-       <li><a href="/docs/user/reusing-page-content">Reusing Page Content</a></li>
-</ul>
+<div>
+       <h4>Common Functions</h4>
+       <ul>
+               <li><a href="/docs/user/searching">Searching Content</a></li>
+               <li><a href="/docs/user/wysiwyg-editor">Default (WYSIWYG) Editor</a></li>
+               <li><a href="/docs/user/markdown-editor">Markdown Editor</a></li>
+       </ul>
+</div>
+
+<div>
+       <h4>Advanced Features</h4>
+       <ul>
+               <li><a href="/docs/user/roles-and-permissions">Roles and Permissions</a></li>
+               <li><a href="/docs/user/reusing-page-content">Reusing Page Content</a></li>
+               <li><a href="/docs/user/content-permalinks">Page Permalinks</a></li>
+       </ul>
+</div>
\ No newline at end of file
diff --git a/themes/bookstack/layouts/partials/signup.html b/themes/bookstack/layouts/partials/signup.html
new file mode 100644 (file)
index 0000000..8134d28
--- /dev/null
@@ -0,0 +1,14 @@
+<!-- Begin MailChimp Signup Form -->
+
+<div id="mc_embed_signup">
+    <h4>Subscribe to Updates</h4>
+    <p>
+        There are two lists you can sign-up to for updates, A general news & updates list, sent on a weekly basis, and a security alerts list
+        that's sent when new security updates are available.
+    </p>
+    <p>
+        <a href="https://updates.bookstackapp.com/signup/bookstack-news-and-updates" target="_blank">News and Updates</a>
+        <span class="muted padded-horizontal">|</span>
+        <a href="https://updates.bookstackapp.com/signup/bookstack-security-updates" target="_blank">Security Alerts</a>
+    </p>
+</div>
\ No newline at end of file
index a6927839da45c636440f5a3c354b3b65b6d2e580..2dec7bc0aa6c281d9d24d7a609693b80db33a6ac 100644 (file)
@@ -4,13 +4,13 @@
     {{ with .Params.image }}
         <!-- Twitter summary card with large image must be at least 280x150px -->
         <meta name="twitter:card" content="summary_large_image"/>
-        <meta name="twitter:image" content="{{ $baseUrl }}{{ . }}"/>
+        <meta name="twitter:image" content="{{ . | absURL }}"/>
     {{ else }}
         <meta name="twitter:card" content="summary"/>
     {{ end }}
 {{ else }}
     <meta name="twitter:card" content="summary"/>
-    <meta name="twitter:image" content="{{ $baseUrl }}{{ .Site.Params.cover }}"/>
+    <meta name="twitter:image" content="{{ .Site.Params.cover | absURL }}"/>
 {{ end }}
 
 <!-- Twitter Card data -->
index de11a0b5f84b2dc368d48c3e9fcedf967cc0273e..3950e8dbfba805ed1bed6f564dc99ba3354ac596 100644 (file)
@@ -1,15 +1,11 @@
 {{ partial "header.html" . }}
 
 
-{{if .Site.Params.blogCover}}
-       <div class="blog-cover" style="background-image: url({{.Site.Params.blogCover}})">
-{{else}}
-       <div class="no-cover">
-{{end}}
-       <div class="container">
+<div class="shaded primary">
+       <div class="container hero padded-vertical">
                <div class="row">
                        <div class="col-sm-6 col-sm-offset-3">
-                               <h1>BookStack Blog</h1>
+                               <h1 class="text-left">{{.Title}}</h1>
                        </div>
                </div>
        </div>
 <div class="container">
        <div class="row">
 
-               <div class="col-sm-6 col-sm-offset-3">
+
+               <div class="col-sm-6 col-sm-offset-3 padded-top">
+
+                       <div class="margin-top large text-left">
+                               <a href="{{.Site.BaseURL}}blog/index.xml" target="_blank"><span class="icon" aria-hidden="true">{{partial "icon/rss_feed.svg"}}</span>RSS Feed</a>
+                               <span class="inline block margins-horizontal muted">|</span>
+                               <a href="https://updates.bookstackapp.com/signup/bookstack-news-and-updates" target="_blank"><span class="icon" aria-hidden="true">{{partial "icon/email.svg"}}</span>Email Updates</a>
+                       </div>
 
                        {{ $paginator := .Paginator }}
 
index 17a1d993f67a2759f143576652e7318fa868e522..b23a0f6c95be8658eb4641fd8949748608d427c1 100644 (file)
@@ -1,55 +1,63 @@
 {{ partial "header.html" . }}
 
-<div class="header">
+<div class="primary shaded padded-vertical">
        <div class="container">
-               <div class="row">
-                       <div class="col-sm-6">
-                               {{if eq .Page.Type "admin-doc"}}
-                               <h2 class="thin-margin">Admin Documentation</h2>
-                               {{else if eq .Page.Type "user-doc"}}
-                               <h2 class="thin-margin">User Documentation</h2>
-                               {{else}}
-                               <h2 class="thin-margin">Documentation</h2>
-                               {{end}}
-
-                               
-                       </div>
-                       <div class="col-sm-6">
-                               <div class="float right float-none-sm">
-                                       <input type="text" placeholder="Search docs" class="doc-search-input">
-                               </div>
-                       </div>
-               </div>
+               {{ if eq .Type "admin-doc" }}
+               <div class="docs-section-title">Admin Documentation</div>
+               {{ else if eq .Type "user-doc" }}
+               <div class="docs-section-title">User Documentation</div>
+               {{else}}
+               <div class="docs-section-title">Documentation</div>
+               {{end}}
        </div>
 </div>
 
 
-<div class="container">
-       <div class="row docs-index">
+<div class="container docs-index">
+       {{if ne .Page.Type "user-doc"}}
+       <div class="padded-vertical margin-bottom">
+               {{if ne .Page.Type "admin-doc"}}
+               <h3>Admin Documentation</h3>
+               {{end}}
+               <div class="docs-index-row">
+                       {{partial "menu_admin_docs"}}
+               </div>
+       </div>
+       <hr class="nomargin margin-bottom">
+       {{end}}
 
+       {{if ne .Page.Type "admin-doc"}}
+       <div class="padded-vertical margin-bottom">
                {{if ne .Page.Type "user-doc"}}
-               <div class="col-sm-6">
-                       <div class="shaded padded">
-                               {{if ne .Page.Type "admin-doc"}}
-                               <h3>Admin Documentation</h3>
-                               {{end}}
-                               {{partial "menu_admin_docs"}}
-                       </div>
-               </div>
+               <h3>User Documentation</h3>
                {{end}}
-               {{if ne .Page.Type "admin-doc"}}
-               <div class="col-sm-6">
-                       <div class="shaded padded">
-                               {{if ne .Page.Type "user-doc"}}
-                               <h3>User Documentation</h3>
-                               {{end}}
-                               {{partial "menu_user_docs"}}    
-                       </div>
+               <div class="docs-index-row">
+                       {{partial "menu_user_docs"}}
                </div>
-               {{end}}
        </div>
+       {{end}}
 </div>
 
-<p><br></p>
+<div class="shaded shaded-border padded-vertical large">
+       <div class="container">
+               <div class="row">
+                       <div class="col-md-6">
+                               <h3>Our Guides on YouTube</h3>
+                               <p>
+                                       If you prefer videos to text, or need a bit of visual guidance,
+                                       our YouTube channel hosts a series of videos that cover installation,
+                                       configuration, maintenance and usage.
+                               </p>
+                               <p>
+                                       <a href="https://www.youtube.com/channel/UCH66RFWfw6CSm2T1EM4ik1g" target="_blank"
+                                               rel="noopener">View our channel here &raquo;</a>
+                               </p>
+                       </div>
+                       <div class="col-md-6">
+                               <iframe loading="lazy" width="560" height="315" src="https://www.youtube-nocookie.com/embed/tSaDVduc3uI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+                       </div>
+               </div>
+       </div>
+</div>
 
 {{ partial "footer.html" . }}
\ No newline at end of file
index 755d6e073b632596b7bc45731850ed0b58235d92..b81acb13fbea22ecceefb9454d1d603cc0a9454b 100644 (file)
@@ -17,7 +17,7 @@
     background-color: lighten($negative, 20%);
   }
   &.primary {
-    background-color: lighten($primary, 40%);
+    background-color: rgba($primary, 10%);
   }
   &.secondary {
     background-color: lighten($secondary, 30%);
index ac23d4747225bea2be37068196158ee31a76302b..dfee5fdedb2ec3b064089b715430bf7cbdfa559c 100644 (file)
        border: 0;
 }
 
+.post-content {
+       max-width: 700px;
+       margin: 0 auto;
+}
+
 .post-content img:not(.no-border) {
        border-radius: 3px;
        border: 1px solid #DDD;
   border-radius: 3px;
 }
 
+.post-content iframe {
+  max-width: 100%;
+}
+
 .pagination {
        text-align: center;
        font-size: 0.8em;
@@ -65,10 +74,11 @@ article.post {
 
 #mc_embed_signup {
        background: #F8F8F8;
-       padding: $-m;
+       padding: $-l;
        border: 1px solid #DDD;
        margin-bottom: $-xl;
        margin-top: $-l;
+       border-radius: 4px;
        h4 {
                margin-top: 0;
        }
index 72fa33cfedfc1f3a76244cb929f53d844d8c16c2..fc088790dcbbf9fe732a9da0cfd192f5963c5d08 100644 (file)
   &:active {
     background-color: darken($backgroundColor, 8%);
   }
+  &:focus {
+    outline: 1px solid $primary;
+    outline-offset: 3px;
+  }
 }
 
 // Button Specific Variables
@@ -31,51 +35,13 @@ $button-border-radius: 2px;
   cursor: pointer;
   transition: all ease-in-out 120ms;
   box-shadow: 0 0.5px 1.5px 0 rgba(0, 0, 0, 0.21);
-  @include generate-button-colors(#EEE, $primary);
+  @include generate-button-colors(#FFF, $primary);
 }
 
 .button, input[type="button"], input[type="submit"]  {
   @extend .button-base;
-  &.pos {
-    @include generate-button-colors(#EEE, $positive);
-  }
-  &.neg {
-    @include generate-button-colors(#EEE, $negative);
-  }
-  &.secondary {
-    @include generate-button-colors(#EEE, $secondary);
-  }
   &.muted {
-    @include generate-button-colors(#EEE, #888);
-  }
-}
-
-.text-button {
-  @extend .link;
-  background-color: transparent;
-  padding: 0;
-  margin: 0;
-  border: none;
-  &:focus, &:active {
-    outline: 0;
-  }
-  &.neg {
-    color: $negative;
-  }
-}
-
-.button-group {
-  @include clearfix;
-  .button, button[type="button"] {
-    margin: $-xs 0 $-xs 0;
-    float: left;
-    border-radius: 0;
-    &:first-child {
-      border-radius: $button-border-radius 0 0 $button-border-radius;
-    }
-    &:last-child {
-      border-radius: 0 $button-border-radius $button-border-radius 0;
-    }
+    @include generate-button-colors(#FFF, #888);
   }
 }
 
diff --git a/themes/bookstack/sass/_code.scss b/themes/bookstack/sass/_code.scss
new file mode 100644 (file)
index 0000000..2a44e3c
--- /dev/null
@@ -0,0 +1,82 @@
+/* Background */ .chroma { background-color: #F8F8F8 }
+/* Other */ .chroma .x {  }
+/* Error */ .chroma .err {  }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: 100%; overflow: auto; display: block; }
+/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
+/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Keyword */ .chroma .k { color: #0000ff }
+/* KeywordConstant */ .chroma .kc { color: #0000ff }
+/* KeywordDeclaration */ .chroma .kd { color: #0000ff }
+/* KeywordNamespace */ .chroma .kn { color: #0000ff }
+/* KeywordPseudo */ .chroma .kp { color: #0000ff }
+/* KeywordReserved */ .chroma .kr { color: #0000ff }
+/* KeywordType */ .chroma .kt { color: #2b91af }
+/* Name */ .chroma .n {  }
+/* NameAttribute */ .chroma .na {  }
+/* NameBuiltin */ .chroma .nb {  }
+/* NameBuiltinPseudo */ .chroma .bp {  }
+/* NameClass */ .chroma .nc { color: #2b91af }
+/* NameConstant */ .chroma .no {  }
+/* NameDecorator */ .chroma .nd {  }
+/* NameEntity */ .chroma .ni {  }
+/* NameException */ .chroma .ne {  }
+/* NameFunction */ .chroma .nf {  }
+/* NameFunctionMagic */ .chroma .fm {  }
+/* NameLabel */ .chroma .nl {  }
+/* NameNamespace */ .chroma .nn {  }
+/* NameOther */ .chroma .nx {  }
+/* NameProperty */ .chroma .py {  }
+/* NameTag */ .chroma .nt {  }
+/* NameVariable */ .chroma .nv {  }
+/* NameVariableClass */ .chroma .vc {  }
+/* NameVariableGlobal */ .chroma .vg {  }
+/* NameVariableInstance */ .chroma .vi {  }
+/* NameVariableMagic */ .chroma .vm {  }
+/* Literal */ .chroma .l {  }
+/* LiteralDate */ .chroma .ld {  }
+/* LiteralString */ .chroma .s { color: #a31515 }
+/* LiteralStringAffix */ .chroma .sa { color: #a31515 }
+/* LiteralStringBacktick */ .chroma .sb { color: #a31515 }
+/* LiteralStringChar */ .chroma .sc { color: #a31515 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #a31515 }
+/* LiteralStringDoc */ .chroma .sd { color: #a31515 }
+/* LiteralStringDouble */ .chroma .s2 { color: #a31515 }
+/* LiteralStringEscape */ .chroma .se { color: #a31515 }
+/* LiteralStringHeredoc */ .chroma .sh { color: #a31515 }
+/* LiteralStringInterpol */ .chroma .si { color: #a31515 }
+/* LiteralStringOther */ .chroma .sx { color: #a31515 }
+/* LiteralStringRegex */ .chroma .sr { color: #a31515 }
+/* LiteralStringSingle */ .chroma .s1 { color: #a31515 }
+/* LiteralStringSymbol */ .chroma .ss { color: #a31515 }
+/* LiteralNumber */ .chroma .m {  }
+/* LiteralNumberBin */ .chroma .mb {  }
+/* LiteralNumberFloat */ .chroma .mf {  }
+/* LiteralNumberHex */ .chroma .mh {  }
+/* LiteralNumberInteger */ .chroma .mi {  }
+/* LiteralNumberIntegerLong */ .chroma .il {  }
+/* LiteralNumberOct */ .chroma .mo {  }
+/* Operator */ .chroma .o {  }
+/* OperatorWord */ .chroma .ow { color: #0000ff }
+/* Punctuation */ .chroma .p {  }
+/* Comment */ .chroma .c { color: #008000 }
+/* CommentHashbang */ .chroma .ch { color: #008000 }
+/* CommentMultiline */ .chroma .cm { color: #008000 }
+/* CommentSingle */ .chroma .c1 { color: #008000 }
+/* CommentSpecial */ .chroma .cs { color: #008000 }
+/* CommentPreproc */ .chroma .cp { color: #0000ff }
+/* CommentPreprocFile */ .chroma .cpf { color: #0000ff }
+/* Generic */ .chroma .g {  }
+/* GenericDeleted */ .chroma .gd {  }
+/* GenericEmph */ .chroma .ge { font-style: italic }
+/* GenericError */ .chroma .gr {  }
+/* GenericHeading */ .chroma .gh { font-weight: bold }
+/* GenericInserted */ .chroma .gi {  }
+/* GenericOutput */ .chroma .go {  }
+/* GenericPrompt */ .chroma .gp { font-weight: bold }
+/* GenericStrong */ .chroma .gs { font-weight: bold }
+/* GenericSubheading */ .chroma .gu { font-weight: bold }
+/* GenericTraceback */ .chroma .gt {  }
+/* GenericUnderline */ .chroma .gl {  }
+/* TextWhitespace */ .chroma .w {  }
\ No newline at end of file
index 0ba27703547a8f68cb27c08b7e0ede3013c69e5f..4008c95761b839ee26cd28ce33af15f047366e6b 100644 (file)
@@ -368,7 +368,7 @@ span.CodeMirror-selectedtext { background: none; }
 
 .cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute { color: #678c30; }
 .cm-s-base16-light span.cm-keyword { color: #ac4142; }
-.cm-s-base16-light span.cm-string { color: #e09c3c; }
+.cm-s-base16-light span.cm-string { color: #9b661b; }
 
 .cm-s-base16-light span.cm-variable { color: #90a959; }
 .cm-s-base16-light span.cm-variable-2 { color: #6a9fb5; }
index 213ae63a3ee1c5ad624440a18232e2709365a944..2cf39d0f5b59b915342d85bc5d482b467899cd53 100644 (file)
@@ -1,27 +1,3 @@
-
-/** Flexbox styling rules **/
-body.flexbox {
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-  height: 100%;
-  min-height: 100%;
-  max-height: 100%;
-  overflow: hidden;
-  #content {
-    flex: 1;
-    display: flex;
-  }
-}
-
-.flex-fill {
-  display: flex;
-  align-items: stretch;
-  .flex, &.flex {
-    flex: 1;
-  }
-}
-
 /** Rules for all columns */
 div[class^="col-"] img {
   max-width: 100%;
@@ -41,20 +17,6 @@ div[class^="col-"] img {
   }
 }
 
-.center-box {
-  margin: $-xl auto 0 auto;
-  padding: $-m $-xxl $-xl*2 $-xxl;
-  max-width: 346px;
-  display: inline-block;
-  text-align: left;
-  vertical-align: top;
-  &.login {
-    background-color: #EEE;
-    box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
-    border: 1px solid #DDD;
-  }
-}
-
 .row {
   margin-left: -$-m;
   margin-right: -$-m;
@@ -131,84 +93,6 @@ div[class^="col-"] img {
 .col-xs-1 {
   width: 8.33333333%;
 }
-.col-xs-pull-12 {
-  right: 100%;
-}
-.col-xs-pull-11 {
-  right: 91.66666667%;
-}
-.col-xs-pull-10 {
-  right: 83.33333333%;
-}
-.col-xs-pull-9 {
-  right: 75%;
-}
-.col-xs-pull-8 {
-  right: 66.66666667%;
-}
-.col-xs-pull-7 {
-  right: 58.33333333%;
-}
-.col-xs-pull-6 {
-  right: 50%;
-}
-.col-xs-pull-5 {
-  right: 41.66666667%;
-}
-.col-xs-pull-4 {
-  right: 33.33333333%;
-}
-.col-xs-pull-3 {
-  right: 25%;
-}
-.col-xs-pull-2 {
-  right: 16.66666667%;
-}
-.col-xs-pull-1 {
-  right: 8.33333333%;
-}
-.col-xs-pull-0 {
-  right: auto;
-}
-.col-xs-push-12 {
-  left: 100%;
-}
-.col-xs-push-11 {
-  left: 91.66666667%;
-}
-.col-xs-push-10 {
-  left: 83.33333333%;
-}
-.col-xs-push-9 {
-  left: 75%;
-}
-.col-xs-push-8 {
-  left: 66.66666667%;
-}
-.col-xs-push-7 {
-  left: 58.33333333%;
-}
-.col-xs-push-6 {
-  left: 50%;
-}
-.col-xs-push-5 {
-  left: 41.66666667%;
-}
-.col-xs-push-4 {
-  left: 33.33333333%;
-}
-.col-xs-push-3 {
-  left: 25%;
-}
-.col-xs-push-2 {
-  left: 16.66666667%;
-}
-.col-xs-push-1 {
-  left: 8.33333333%;
-}
-.col-xs-push-0 {
-  left: auto;
-}
 .col-xs-offset-12 {
   margin-left: 100%;
 }
@@ -288,84 +172,6 @@ div[class^="col-"] img {
   .col-sm-1 {
     width: 8.33333333%;
   }
-  .col-sm-pull-12 {
-    right: 100%;
-  }
-  .col-sm-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-sm-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-sm-pull-9 {
-    right: 75%;
-  }
-  .col-sm-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-sm-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-sm-pull-6 {
-    right: 50%;
-  }
-  .col-sm-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-sm-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-sm-pull-3 {
-    right: 25%;
-  }
-  .col-sm-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-sm-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-sm-pull-0 {
-    right: auto;
-  }
-  .col-sm-push-12 {
-    left: 100%;
-  }
-  .col-sm-push-11 {
-    left: 91.66666667%;
-  }
-  .col-sm-push-10 {
-    left: 83.33333333%;
-  }
-  .col-sm-push-9 {
-    left: 75%;
-  }
-  .col-sm-push-8 {
-    left: 66.66666667%;
-  }
-  .col-sm-push-7 {
-    left: 58.33333333%;
-  }
-  .col-sm-push-6 {
-    left: 50%;
-  }
-  .col-sm-push-5 {
-    left: 41.66666667%;
-  }
-  .col-sm-push-4 {
-    left: 33.33333333%;
-  }
-  .col-sm-push-3 {
-    left: 25%;
-  }
-  .col-sm-push-2 {
-    left: 16.66666667%;
-  }
-  .col-sm-push-1 {
-    left: 8.33333333%;
-  }
-  .col-sm-push-0 {
-    left: auto;
-  }
   .col-sm-offset-12 {
     margin-left: 100%;
   }
@@ -446,84 +252,6 @@ div[class^="col-"] img {
   .col-md-1 {
     width: 8.33333333%;
   }
-  .col-md-pull-12 {
-    right: 100%;
-  }
-  .col-md-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-md-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-md-pull-9 {
-    right: 75%;
-  }
-  .col-md-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-md-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-md-pull-6 {
-    right: 50%;
-  }
-  .col-md-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-md-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-md-pull-3 {
-    right: 25%;
-  }
-  .col-md-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-md-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-md-pull-0 {
-    right: auto;
-  }
-  .col-md-push-12 {
-    left: 100%;
-  }
-  .col-md-push-11 {
-    left: 91.66666667%;
-  }
-  .col-md-push-10 {
-    left: 83.33333333%;
-  }
-  .col-md-push-9 {
-    left: 75%;
-  }
-  .col-md-push-8 {
-    left: 66.66666667%;
-  }
-  .col-md-push-7 {
-    left: 58.33333333%;
-  }
-  .col-md-push-6 {
-    left: 50%;
-  }
-  .col-md-push-5 {
-    left: 41.66666667%;
-  }
-  .col-md-push-4 {
-    left: 33.33333333%;
-  }
-  .col-md-push-3 {
-    left: 25%;
-  }
-  .col-md-push-2 {
-    left: 16.66666667%;
-  }
-  .col-md-push-1 {
-    left: 8.33333333%;
-  }
-  .col-md-push-0 {
-    left: auto;
-  }
   .col-md-offset-12 {
     margin-left: 100%;
   }
@@ -604,84 +332,6 @@ div[class^="col-"] img {
   .col-lg-1 {
     width: 8.33333333%;
   }
-  .col-lg-pull-12 {
-    right: 100%;
-  }
-  .col-lg-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-lg-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-lg-pull-9 {
-    right: 75%;
-  }
-  .col-lg-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-lg-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-lg-pull-6 {
-    right: 50%;
-  }
-  .col-lg-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-lg-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-lg-pull-3 {
-    right: 25%;
-  }
-  .col-lg-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-lg-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-lg-pull-0 {
-    right: auto;
-  }
-  .col-lg-push-12 {
-    left: 100%;
-  }
-  .col-lg-push-11 {
-    left: 91.66666667%;
-  }
-  .col-lg-push-10 {
-    left: 83.33333333%;
-  }
-  .col-lg-push-9 {
-    left: 75%;
-  }
-  .col-lg-push-8 {
-    left: 66.66666667%;
-  }
-  .col-lg-push-7 {
-    left: 58.33333333%;
-  }
-  .col-lg-push-6 {
-    left: 50%;
-  }
-  .col-lg-push-5 {
-    left: 41.66666667%;
-  }
-  .col-lg-push-4 {
-    left: 33.33333333%;
-  }
-  .col-lg-push-3 {
-    left: 25%;
-  }
-  .col-lg-push-2 {
-    left: 16.66666667%;
-  }
-  .col-lg-push-1 {
-    left: 8.33333333%;
-  }
-  .col-lg-push-0 {
-    left: auto;
-  }
   .col-lg-offset-12 {
     margin-left: 100%;
   }
@@ -724,8 +374,6 @@ div[class^="col-"] img {
 }
 .clearfix:before,
 .clearfix:after,
-.container:before,
-.container:after,
 .container-fluid:before,
 .container-fluid:after,
 .row:before,
@@ -734,7 +382,6 @@ div[class^="col-"] img {
   display: table;
 }
 .clearfix:after,
-.container:after,
 .container-fluid:after,
 .row:after {
   clear: both;
index dfe033a34347b9580da537466b29a686e01f8b24..ff18eb808019d7a0ca88e4494d4d964fe6182ce6 100644 (file)
@@ -8,9 +8,10 @@
   top: 0;
   background-color: $primary-dark;
   color: #fff;
-  h2 {
+  position: relative;
+  h1 {
     color: #FFF;
-    font-size: 2.666em;
+    font-size: 2.666rem;
     font-weight: 300;
     @include smaller-than($s) {
       font-size: 2em;
@@ -19,7 +20,7 @@
   .spaced {
     margin-top: $-xxl;
     margin-bottom: $-l*2;
-    h2, p {
+    h1, p {
       max-width: 420px;
     }
     @include smaller-than($screen-lg) {
       margin-bottom: $-m*2;
     }
   }
-  p {
-    font-size: 1.2em;
-    font-weight: 300;
-  }
   .icon svg {
     fill: #FFF
   }
-  .menu {
-    padding-top: 8px;
-  }
+}
+
+#header.header {
+  z-index: 5;
+}
+
+#header .container {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+#header .container > * {
+  flex: 1;
+}
+
+.header .docs-searchbar-js {
+  text-align: center;
+  margin-top: 4px;
 }
 
 .logo {
   text-align: left;
   margin: $-m 0;
-  h1, img {
+  div, img {
     vertical-align: top;
     display: inline-block;
     margin: 0;
   }
-  h1 {
+  div {
     color: #FFF;
     font-size: 1.8em;
     font-weight: 400;
     height: 43px;
     width: auto;
   }
+
   @include smaller-than($s) {
-    h1 {
+    margin: $-s 0;
+    div {
       font-size: 1.8em;
     }
     svg {
       margin-right: $-s;
     }
   }
+
   a {
-    display: inline-block;
+    display: inline-flex;
+    align-items: center;
   }
   a:hover {
     text-decoration: none;
   }
 }
+
+
+/** Header Menu **/
+@media screen and (min-width: $l) {
+  .main-nav {
+    display: flex;
+    justify-content: flex-end;
+    gap: 1rem;
+  }
+  .nav-dropdown-wrap {
+    position: relative;
+  }
+  .nav-dropdown-trigger {
+    color: #FFF;
+    display: block;
+    padding: 1rem;
+    font-size: 1.1rem;
+    font-weight: 500;
+  }
+  .nav-dropdown-trigger:hover {
+    color: #FFF;
+  }
+  
+  .nav-dropdown-trigger:focus + .nav-dropdown-menu,
+  .nav-dropdown-trigger:hover + .nav-dropdown-menu,
+  .nav-dropdown-menu:focus-within,
+  .nav-dropdown-menu:hover
+  {
+    display: block;
+  }
+  .nav-dropdown-menu {
+    display: none;
+    background-color: #FFF;
+    border-radius: 4px;
+    position: absolute;
+    z-index: 51;
+    box-shadow: $bs-med, 
+    0 0 120px 0 rgba(0, 0, 0, 0.1);
+    padding: .5rem;
+    width: 240px;
+    right: 0;
+    top: 96%;
+  }
+  .nav-dropdown-menu:after {
+    content: '';
+    display: block;
+    background-color: #FFF;
+    position: absolute;
+    top: -7px;
+    z-index: 2;
+    right: 2.5rem;
+    border-radius: 3px;
+    width: 16px;
+    height: 16px;
+    transform: rotate(45deg);
+  }
+  .nav-dropdown-item {
+    color: #222;
+    padding: .5rem;
+    display: flex;
+    gap: 1rem;
+    border-radius: 4px;
+    z-index: 3;
+    position: relative;
+  }
+  .nav-dropdown-item:hover {
+    background-color: rgba($primary, 0.1);
+    text-decoration: none;
+  }
+  .nav-dropdown-item-icon {
+    padding-top: .2rem;
+  }
+  .nav-dropdown-item-icon svg {
+    width: 32px;
+    height: 32px;
+    padding: 6px;
+    border-radius: 50%;
+    background-color: rgba($primary, 0.1);
+    fill: $primary;
+  }
+  .nav-dropdown-item-title {
+    font-weight: 700;
+  }
+  .nav-dropdown-item p {
+    margin: 0;
+    opacity: .8;
+    font-size: .8rem;
+  }
+  header #menu-button {
+    display: none;
+  }
+}
+
+@media screen and (max-width: $l) {
+  header #menu-button {
+    display: inline-block;
+    background-color: transparent;
+    border: 0;
+    box-shadow: none;
+    padding: $-m;
+    line-height: 0;
+  }
+  header #menu-button svg {
+    margin-right: 0;
+  }
+  .header .logo {
+    display: flex;
+    justify-content: space-between;
+  }
+  .header-search-section {
+    display: none;
+  }
+  #header > .container {
+    display: block;
+  }
+  #header .main-nav {
+    display: none;
+    margin-top: -1rem;
+  }
+  #header .main-nav.showing {
+    display: block;
+  }
+  .nav-dropdown-trigger {
+    color: #FFF;
+    pointer-events: none;
+    font-weight: bold;
+    margin-bottom: .2rem;
+    margin-top: .5rem;
+    display: block;
+  }
+  .nav-dropdown-menu {
+    display: flex;
+    overflow-x: scroll;
+    gap: 1rem;
+    padding-bottom: .5rem;
+  }
+  .nav-dropdown-item {
+    color: #FFF;
+    border: 1px solid #FFF;
+    border-radius: 4px;
+    display: block;
+    padding: 1rem;
+    display: flex;
+    min-width: 200px;
+  }
+  .nav-dropdown-item:hover {
+    text-decoration: none;
+    color: #FFF;
+    background-color: rgba(255 255 255 / 10%);
+  }
+  .nav-dropdown-item-icon svg {
+    display: none;
+  }
+  .nav-dropdown-item-title {
+    font-weight: bold;
+  }
+  .nav-dropdown-item p {
+    font-size: .8em;
+    margin-bottom: 0;
+  }
+}
+
+
+
+/** Hero Content **/
+.header .hero p {
+  font-size: 1.2rem;
+}
+
+/** Footer lists **/
+ul.nav-list {
+  list-style: none;
+  padding-left: 0;
+  margin-left: 0;
+  li {
+    display: block;
+    padding: .5rem 0;
+  }
+}
\ No newline at end of file
index 5618231b17b666ec8c3c67cb1abcd75a46b75a52..158798d3e358d46b601d992026758086d23faedb 100644 (file)
@@ -9,7 +9,7 @@ body {
   font-family: $text;
   font-size: $fs-m;
   line-height: 1.6;
-  color: #616161;
+  color: #555;
   overflow-x: hidden;
   max-width: 100%;
 }
index b7bc369d0dcafab3130b2719f3c6a9fc304521e1..021b21287c0bfb258f5efbb019e40fd072e61b9e 100644 (file)
@@ -1,6 +1,7 @@
 /* roboto-300 - latin */
 @font-face {
   font-family: 'Roboto';
+  font-display: swap;
   font-style: normal;
   font-weight: 300;
   src: local('Roboto Light'), local('Roboto-Light'),
@@ -10,6 +11,7 @@
 /* roboto-regular - latin */
 @font-face {
   font-family: 'Roboto';
+  font-display: swap;
   font-style: normal;
   font-weight: 400;
   src: local('Roboto'), local('Roboto-Regular'),
@@ -19,6 +21,7 @@
 /* roboto-500 - latin */
 @font-face {
   font-family: 'Roboto';
+  font-display: swap;
   font-style: normal;
   font-weight: 500;
   src: local('Roboto Medium'), local('Roboto-Medium'),
@@ -28,6 +31,7 @@
 /* roboto-700 - latin */
 @font-face {
   font-family: 'Roboto';
+  font-display: swap;
   font-style: normal;
   font-weight: 700;
   src: local('Roboto Bold'), local('Roboto-Bold'),
@@ -70,7 +74,7 @@ h1, h2, h3, h4, h5 {
   font-weight: 400;
   position: relative;
   display: block;
-  color: #555;
+  color: #444;
   .subheader, small {
     font-size: 0.5em;
     line-height: 1em;
@@ -109,6 +113,21 @@ a, .link {
   }
 }
 
+@include smaller-than($m) {
+  h1 {
+    font-size: 2.666em;
+  }
+  h2 {
+    font-size: 2.2222em;
+  }
+  h3 {
+    font-size: 1.6666em;
+  }
+  h4 {
+    font-size: 1.3333em;
+  }
+}
+
 /*
  * Other HTML Text Elements
  */
@@ -144,7 +163,6 @@ em, i, .italic {
 
 small, p.small, span.small, .text-small {
   font-size: 0.8em;
-  color: lighten($text-dark, 20%);
 }
 
 sup, .superscript {
@@ -160,7 +178,6 @@ pre {
   border-radius: 2px;
   // box-shadow: inset 1px 2px 2px rgba(10, 10, 10, 0.06);
   border: 1px solid #DDD;
-  border-top: 3px solid $primary;
   background-color: #F8F8F8;
   padding: 0;
   overflow-x: auto;
@@ -200,7 +217,8 @@ code {
   @extend .code-base;
   display: inline;
   padding: 1px 3px;
-  white-space:pre;
+  white-space: pre-wrap;
+  word-break: break-word;
   line-height: 1.2em;
   margin-bottom: 1.2em;
   color: #c54545;
@@ -218,6 +236,7 @@ pre code {
   max-width: 100%;
   display: block;
   margin-bottom: 0;
+  padding: .5rem 1rem;
 }
 /*
  * Text colors
@@ -295,6 +314,14 @@ ul {
   }
 }
 
+@include smaller-than($l) {
+  ul li a, ol li a {
+    display: inline-block;
+    padding-top: .5rem;
+    padding-bottom: .5rem;
+  }
+}
+
 ol {
   list-style-type: decimal;
   padding-left: $-m*1.2;
@@ -311,15 +338,15 @@ ol {
 }
 
 .text-center {
-  text-align: center;
+  text-align: center !important;
 }
 
 .text-left {
-  text-align: left;
+  text-align: left !important;
 }
 
 .text-right {
-  text-align: right;
+  text-align: right !important;
 }
 
 /**
index 095a0e0dc24582d29eeba63e8036a7be9a6e7d14..264ddad371e7b6bbca80079c522aa5598a4505cd 100644 (file)
@@ -7,7 +7,7 @@ $max-width: 1400px;
 // Screen breakpoints
 $xl: 1100px;
 $ipad-width: 1028px; // Is actually 1024 but we go over to ensure functionality.
-$l: 1000px;
+$l: 992px;
 $m: 800px;
 $s: 600px;
 $xs: 400px;
@@ -29,12 +29,12 @@ $-xxs: 3px;
 // Fonts
 $heading:  'Roboto', Helvetica, Arial, sans-serif;
 $text: 'Roboto', Helvetica, Arial, sans-serif;
-$fs-m: 15px;
+$fs-m: 16px;
 $fs-s: 14px;
 
 // Colours
-$primary: #0288D1;
-$primary-dark: #0288D1;
+$primary: #0078b9;
+$primary-dark: #0078b9;
 $secondary: #e27b41;
 $positive: #52A256;
 $negative: #E84F4F;
index 047e94068131fb56bed2b729794ff66aec839aef..2c401b1e7c98d1ede2d031de3ba4ca9062834d94 100644 (file)
@@ -2,6 +2,7 @@
 @import "variables";
 @import "mixins";
 @import "html";
+@import "code";
 @import "codemirror";
 @import "text";
 @import "grid";
 @import "photoswipe";
 
 .md-margin-top {
-  margin-top: $-xxl*1.6;
+  margin-top: $-xxl*1.4;
   @include smaller-than($screen-lg) {
     margin-top: $-m;
   }
 }
 
 .md-margin-bottom {
-  margin-bottom: $-xxl*1.6;
+  margin-bottom: $-xxl*1.4;
   @include smaller-than($screen-lg) {
     margin-bottom: $-m;
   }
   border-radius: 2px;
 }
 
-header .menu, footer .menu {
+.sponsor-list {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-wrap: wrap;
+  a {
+    display: block;
+    margin: $-s $-xl;
+    min-width: 240px;
+  }
+}
+
+footer .menu {
   text-align: right;
   a {
     display: inline-block;
@@ -104,10 +117,6 @@ header .menu, footer .menu {
   }
 }
 
-header .menu a {
-  color: #FFF;
-}
-
 .hero a {
   color: #FFF;
   font-weight: 400;
@@ -115,65 +124,7 @@ header .menu a {
   text-decoration: underline;
 }
 
-header #menu-button {
-  display: none;
-  background-color: transparent;
-  border: 0;
-  box-shadow: none;
-  padding-top: $-m;
-}
-
-@include smaller-than($xl) {
-  body header {
-    padding-top: $-xxl*1.7;
-  }
-  header .row.fix-mobile {
-    top: 0;
-    position: fixed;
-    background-color: $primary;
-    width: 100%;
-    z-index: 99;
-    border-bottom: 1px solid rgba(255, 255, 255, 0.3);
-  }
-  header #menu-button {
-    display: inline-block;
-  }
-  header .menu .inner {
-    display: none;
-    right: $-m;
-    position: fixed;
-    border-radius: 3px;
-    width: 240px;
-    background-color: #FFF;
-    z-index: 55;
-    padding: 0;
-    box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.25);
-    &.showing {
-      display: block;
-    }
-    a {
-      color: #444;
-      display: block;
-      text-align: left;
-      border-bottom: 1px solid #EEE;
-      padding: $-m $-m*2;
-    }
-    a .icon svg {
-      fill: #444;
-    }
-    &:after {
-      width: 16px;
-      height: 16px;
-      content: '';
-      display: block;
-      background-color: #FFF;
-      transform: rotate(45deg);
-      position: absolute;
-      top: -6px;
-      border-radius: 2px;
-      right: 22px;
-    }
-  }
+@include smaller-than($xl) {  
   footer .menu {
     display: none;
   }
@@ -183,7 +134,7 @@ footer {
   padding-top: $-l;
   padding-bottom: $-l;
   p {
-    padding: $-m;
+    padding: $-m 0;
     margin: 0;
   }
 }
@@ -240,8 +191,15 @@ input[type=text] {
       box-shadow: 0 5px 10px 1px rgba(0, 0, 0, 0.1);
     }
   }
+  figcaption {
+    margin-bottom: .5rem;
+  }
+  h4 {
+    margin-bottom: 0;
+  }
 }
 
+
 .docs-content {
   padding-bottom: $-xxl;
   .edit-link {
@@ -276,15 +234,21 @@ input[type=text] {
   img, video {
     border: 1px solid $primary;
     border-radius: 3px;
+    max-width: 100%;
   }
   video {
     cursor: pointer;
     margin: $-l auto;
     display: block;
-    max-width: 100%;
   }
 }
 
+.docs-section-title {
+  font-size: 2rem;
+  font-weight: 400;
+  color: #444;
+}
+
 .sidebar, .docs-index {
   margin-top: $-xl;
   h4 {
@@ -300,19 +264,47 @@ input[type=text] {
   }
 }
 
+.sidebar .col-md-6 {
+  width: 100%;
+  ul {
+    margin-bottom: 0;
+  }
+}
+
+.docs-index-row {
+  display: flex;
+  flex-wrap: wrap;
+  column-gap: 6rem;
+  row-gap: .5rem;
+  justify-content: left;
+}
+
 .sidebar {
   border-right: 2px dotted #E5E5E5;
 }
 
-@include larger-than($xl) {
+.sidebar-inner {
+  display: none;
+}
+.sidebar-inner.mobile {
+  display: block;
+}
+
+
+@include larger-than($l) {
   .sidebar {
     position: sticky;
     top: $-m;
   }
+  .sidebar-inner {
+    display: block;
+  }
+  .sidebar-inner.mobile {
+    display: none;
+  }
 }
 
 
-
 h2.thin-margin {
   margin-top: 0;
 }
@@ -322,33 +314,55 @@ h2.thin-margin {
 }
 
 
+.blogpost-cards {
+  display: flex;
+  gap: 32px;
+  flex-wrap: wrap;
+  margin-top: $-xl;
+}
+
 .blogpost-card {
   display: block;
-  float: left;
-  margin-right: $-m;
   margin-bottom: $-m;
-  width: 280px;
+  flex: 1;
+  min-width: 280px;
+  max-width: 320px;
   background-color: #FFF;
-  border: 1px solid #DDD;
+  border-radius: 4px;
+  overflow: hidden;
+  box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.1);
   a {
     display: block;
   }
   .image {
     height: 160px;
-    background-size: cover;
+  }
+  .image img {
+    object-fit: cover;
+    height: 100%;
+    width: 100%;
   }
   .text {
     padding: $-m;
-    min-height: 80px;
+    font-size: 1.1rem;
+    font-weight: 500;
+  }
+  &:hover {
+    box-shadow: 0 0 3px 1px rgba(0, 0, 0, 0.15);
   }
 }
 
+.shaded .hero h1 {
+  font-size: 4rem;
+  font-weight: 500;
+}
+
 
 input[type="text"].doc-search-input {
   background-color: transparent;
   border: 2px solid #FFF;
   color: #FFF;
-  background-color: $primary;
+  background-color: transparent;
   border-radius: 30px;
   width: 300px;
   margin-bottom: $-m;
@@ -356,9 +370,6 @@ input[type="text"].doc-search-input {
   &::-webkit-input-placeholder { /* Chrome */
     color: rgba(255, 255, 255, 0.7);
   }
-  &:-ms-input-placeholder { /* IE 10+ */
-    color: rgba(255, 255, 255, 0.7);
-  }
   &::-moz-placeholder { /* Firefox 19+ */
     color: rgba(255, 255, 255, 0.7);
     opacity: 1;
@@ -371,3 +382,97 @@ input[type="text"].doc-search-input {
     outline: none;
   }
 }
+
+iframe[src^="https://www.youtube-nocookie.com"] {
+  max-width: 100%;
+  aspect-ratio: 16/9;
+  height: auto;
+  overflow: hidden;
+  border-radius: 3px;
+  box-shadow: 0 4px 12px 0 rgba(0, 0, 0, 0.15);
+  background-color: #222;
+}
+
+.meilisearch-autocomplete .docs-searchbar-footer-logo,
+.meilisearch-autocomplete .docs-searchbar-footer {
+  vertical-align: top !important;
+}
+
+.meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-column {
+  color: #53555a !important;
+}
+
+.price-table {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 2rem;
+  margin-bottom: $-xl;
+  justify-content: center;
+  margin-left: -20vw;
+  margin-right: -20vw;
+  // align-items: flex-start;
+}
+@include smaller-than(1200px) {
+  .price-table {
+    margin-left: 0;
+    margin-right: 0;
+  }
+}
+
+.price-table-offer {
+  box-shadow: 0 1px 4px 0 rgba(94, 94, 94, 0.26);
+  border-radius: 3px;
+  min-width: 280px;
+  flex-basis: 360px;
+  display: flex;
+  flex-direction: column;
+}
+.price-table-item, .price-table-header, .price-table-action {
+  padding: .5rem 1rem;
+}
+.price-table-item {
+  border-bottom: 1px solid #EEE;
+  position: relative;
+  padding-left: 3rem;
+}
+.price-table-item:before {
+  content: '';
+  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23329f71"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>');
+  width: 16px;
+  height: 16px;
+  display: block;
+  position: absolute;
+  left: 1rem;
+  top: .8rem;
+}
+.price-table-item.price-table-item-negative:before {
+  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23888888"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm5 11H7v-2h10v2z"/></svg>');
+}
+.price-table-item-highlight {
+  font-weight: 700;
+}
+.price-table-item-highlight small {
+  font-weight: 400;
+}
+.price-table-header {
+  background-color: rgba($primary, 0.1);
+}
+.price-table-header h3 {
+  font-size: 1.2rem;
+  font-weight: 700;
+  margin-bottom: .2em;
+}
+.price-table-header .price {
+  font-weight: 700;
+  color: #666;
+  margin-bottom: .5rem;
+}
+.price-table-action {
+  font-weight: 700;
+  background-color: rgba($primary, 0.1);
+  text-align: center;
+  margin-top: auto;
+}
+.price-table-action a {
+  display: block;
+}
\ No newline at end of file
diff --git a/themes/bookstack/static/images/bookstack-hero-screenshot.jpg b/themes/bookstack/static/images/bookstack-hero-screenshot.jpg
deleted file mode 100644 (file)
index 1fe6a3f..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:35de68ad0a7b54ca54baaa46fb91f6561b223586cec05a2f80ad8c773b599945
-size 198224
diff --git a/themes/bookstack/static/images/bookstack-hero-screenshot.webp b/themes/bookstack/static/images/bookstack-hero-screenshot.webp
new file mode 100644 (file)
index 0000000..0de3d18
Binary files /dev/null and b/themes/bookstack/static/images/bookstack-hero-screenshot.webp differ
diff --git a/themes/bookstack/static/images/docs/book-sort.png b/themes/bookstack/static/images/docs/book-sort.png
deleted file mode 100644 (file)
index 2b11683..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:f9effd611c7ec84ada25457cf95b36904ebcb8b1a4e5b92740e80a6d15921047
-size 21961
diff --git a/themes/bookstack/static/images/docs/page-move-menu.png b/themes/bookstack/static/images/docs/page-move-menu.png
deleted file mode 100644 (file)
index 668a24a..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:80e3c6847a1806825846124b679c759d082dde779641735c437f64ca2d657f49
-size 14408
index 19364df02164e40c29b69fde5a80d3a6fe006e82..da9a8d78fd2e2954b5b80be36255493ce76f21a6 100644 (file)
@@ -1,29 +1,23 @@
 
 // Mobile menu
 
-var menuButton = document.getElementById('menu-button');
-var menuDropDown = document.querySelector('header div.inner');
+const menuButton = document.getElementById('menu-button');
+const menuDropDown = document.querySelector('#header .main-nav');
 
-menuButton.onclick = function(event) {
-    var menuClass = menuDropDown.className;
-    var visible = menuClass.indexOf('showing') !== -1;
-    if (visible) {
-        menuDropDown.className = menuClass.replace('showing', '');
-    } else {
-        menuDropDown.className += ' showing';
-    }
-    event.stopPropagation();
-};
+menuButton.addEventListener('click', function(event) {
+  menuDropDown.classList.toggle('showing');
+  event.stopPropagation();
+});
 
-document.body.onclick = function(event) {
-    menuDropDown.className = menuDropDown.className.replace('showing', '');
-    event.stopPropagation();
-};
+document.body.addEventListener('click', function(event) {
+  menuDropDown.classList.remove('showing');
+  event.stopPropagation();  
+});
 
 
 // Handle video click to play
-var videos = document.querySelectorAll('video');
-for (var i = 0; i < videos.length; i++) {
+const videos = document.querySelectorAll('video');
+for (let i = 0; i < videos.length; i++) {
     videos[i].addEventListener('click', videoClick)
 }
 
@@ -35,7 +29,7 @@ function videoClick() {
 
 // Codemirror Setup
 
-var modeMap = {
+const modeMap = {
   'language-html': 'htmlmixed',
   'language-bash': 'shell',
   'language-js': 'javascript',
@@ -43,17 +37,18 @@ var modeMap = {
   'language-nginx': 'nginx',
   'language-apache': 'apache',
   'language-php': 'php',
+  'language-sql': 'text/x-mysql',
 };
 
-var codeBlocks = document.querySelectorAll('pre');
-for (var i = 0; i < codeBlocks.length; i++) {
-  var block = codeBlocks[i];
-  var codeElem = block.querySelector('code');
+const codeBlocks = document.querySelectorAll('pre');
+for (let i = 0; i < codeBlocks.length; i++) {
+  const block = codeBlocks[i];
+  const codeElem = block.querySelector('code');
   if (codeElem === null) continue;
 
-  var langClass = codeElem.className;
-  var mode = (typeof modeMap[langClass] !== 'undefined') ? modeMap[langClass] : 'htmlmixed';
-  var content = codeElem.textContent.trim();
+  const langClass = codeElem.className;
+  const mode = (typeof modeMap[langClass] !== 'undefined') ? modeMap[langClass] : 'htmlmixed';
+  const content = codeElem.textContent.trim();
   CodeMirror(function(cmElem) {
     block.parentNode.replaceChild(cmElem, block);
   }, {
index 39efca3c2460e0e8ab1d271da09126db84ebac5b..497dba4b10cbb5f79d878270c45bdf12776d318d 100644 (file)
@@ -2533,3 +2533,321 @@ if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
   CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
 
 });
+
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("sql", function(config, parserConfig) {
+  var client         = parserConfig.client || {},
+      atoms          = parserConfig.atoms || {"false": true, "true": true, "null": true},
+      builtin        = parserConfig.builtin || set(defaultBuiltin),
+      keywords       = parserConfig.keywords || set(sqlKeywords),
+      operatorChars  = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/,
+      support        = parserConfig.support || {},
+      hooks          = parserConfig.hooks || {},
+      dateSQL        = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true},
+      backslashStringEscapes = parserConfig.backslashStringEscapes !== false,
+      brackets       = parserConfig.brackets || /^[\{}\(\)\[\]]/,
+      punctuation    = parserConfig.punctuation || /^[;.,:]/
+
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+
+    // call hooks from the mime type
+    if (hooks[ch]) {
+      var result = hooks[ch](stream, state);
+      if (result !== false) return result;
+    }
+
+    if (support.hexNumber &&
+      ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
+      || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
+      // hex
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
+      return "number";
+    } else if (support.binaryNumber &&
+      (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
+      || (ch == "0" && stream.match(/^b[01]+/)))) {
+      // bitstring
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
+      return "number";
+    } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
+      // numbers
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
+      stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/);
+      support.decimallessFloat && stream.match(/^\.(?!\.)/);
+      return "number";
+    } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
+      // placeholders
+      return "variable-3";
+    } else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
+      // strings
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
+      state.tokenize = tokenLiteral(ch);
+      return state.tokenize(stream, state);
+    } else if ((((support.nCharCast && (ch == "n" || ch == "N"))
+        || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
+        && (stream.peek() == "'" || stream.peek() == '"'))) {
+      // charset casting: _utf8'str', N'str', n'str'
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
+      return "keyword";
+    } else if (support.escapeConstant && (ch == "e" || ch == "E")
+        && (stream.peek() == "'" || (stream.peek() == '"' && support.doubleQuote))) {
+      // escape constant: E'str', e'str'
+      // ref: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS-ESCAPE
+      state.tokenize = function(stream, state) {
+        return (state.tokenize = tokenLiteral(stream.next(), true))(stream, state);
+      }
+      return "keyword";
+    } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
+      // 1-line comment
+      stream.skipToEnd();
+      return "comment";
+    } else if ((support.commentHash && ch == "#")
+        || (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
+      // 1-line comments
+      // ref: https://kb.askmonty.org/en/comment-syntax/
+      stream.skipToEnd();
+      return "comment";
+    } else if (ch == "/" && stream.eat("*")) {
+      // multi-line comments
+      // ref: https://kb.askmonty.org/en/comment-syntax/
+      state.tokenize = tokenComment(1);
+      return state.tokenize(stream, state);
+    } else if (ch == ".") {
+      // .1 for 0.1
+      if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i))
+        return "number";
+      if (stream.match(/^\.+/))
+        return null
+      // .table_name (ODBC)
+      // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
+      if (support.ODBCdotTable && stream.match(/^[\w\d_$#]+/))
+        return "variable-2";
+    } else if (operatorChars.test(ch)) {
+      // operators
+      stream.eatWhile(operatorChars);
+      return "operator";
+    } else if (brackets.test(ch)) {
+      // brackets
+      return "bracket";
+    } else if (punctuation.test(ch)) {
+      // punctuation
+      stream.eatWhile(punctuation);
+      return "punctuation";
+    } else if (ch == '{' &&
+        (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
+      // dates (weird ODBC syntax)
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
+      return "number";
+    } else {
+      stream.eatWhile(/^[_\w\d]/);
+      var word = stream.current().toLowerCase();
+      // dates (standard SQL syntax)
+      // ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
+      if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
+        return "number";
+      if (atoms.hasOwnProperty(word)) return "atom";
+      if (builtin.hasOwnProperty(word)) return "builtin";
+      if (keywords.hasOwnProperty(word)) return "keyword";
+      if (client.hasOwnProperty(word)) return "string-2";
+      return null;
+    }
+  }
+
+  // 'string', with char specified in quote escaped by '\'
+  function tokenLiteral(quote, backslashEscapes) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) {
+          state.tokenize = tokenBase;
+          break;
+        }
+        escaped = (backslashStringEscapes || backslashEscapes) && !escaped && ch == "\\";
+      }
+      return "string";
+    };
+  }
+  function tokenComment(depth) {
+    return function(stream, state) {
+      var m = stream.match(/^.*?(\/\*|\*\/)/)
+      if (!m) stream.skipToEnd()
+      else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1)
+      else if (depth > 1) state.tokenize = tokenComment(depth - 1)
+      else state.tokenize = tokenBase
+      return "comment"
+    }
+  }
+
+  function pushContext(stream, state, type) {
+    state.context = {
+      prev: state.context,
+      indent: stream.indentation(),
+      col: stream.column(),
+      type: type
+    };
+  }
+
+  function popContext(state) {
+    state.indent = state.context.indent;
+    state.context = state.context.prev;
+  }
+
+  return {
+    startState: function() {
+      return {tokenize: tokenBase, context: null};
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (state.context && state.context.align == null)
+          state.context.align = false;
+      }
+      if (state.tokenize == tokenBase && stream.eatSpace()) return null;
+
+      var style = state.tokenize(stream, state);
+      if (style == "comment") return style;
+
+      if (state.context && state.context.align == null)
+        state.context.align = true;
+
+      var tok = stream.current();
+      if (tok == "(")
+        pushContext(stream, state, ")");
+      else if (tok == "[")
+        pushContext(stream, state, "]");
+      else if (state.context && state.context.type == tok)
+        popContext(state);
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      var cx = state.context;
+      if (!cx) return CodeMirror.Pass;
+      var closing = textAfter.charAt(0) == cx.type;
+      if (cx.align) return cx.col + (closing ? 0 : 1);
+      else return cx.indent + (closing ? 0 : config.indentUnit);
+    },
+
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--",
+    closeBrackets: "()[]{}''\"\"``"
+  };
+});
+
+  // `identifier`
+  function hookIdentifier(stream) {
+    // MySQL/MariaDB identifiers
+    // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
+    var ch;
+    while ((ch = stream.next()) != null) {
+      if (ch == "`" && !stream.eat("`")) return "variable-2";
+    }
+    stream.backUp(stream.current().length - 1);
+    return stream.eatWhile(/\w/) ? "variable-2" : null;
+  }
+
+  // "identifier"
+  function hookIdentifierDoublequote(stream) {
+    // Standard SQL /SQLite identifiers
+    // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
+    // ref: http://sqlite.org/lang_keywords.html
+    var ch;
+    while ((ch = stream.next()) != null) {
+      if (ch == "\"" && !stream.eat("\"")) return "variable-2";
+    }
+    stream.backUp(stream.current().length - 1);
+    return stream.eatWhile(/\w/) ? "variable-2" : null;
+  }
+
+  // variable token
+  function hookVar(stream) {
+    // variables
+    // @@prefix.varName @varName
+    // varName can be quoted with ` or ' or "
+    // ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
+    if (stream.eat("@")) {
+      stream.match(/^session\./);
+      stream.match(/^local\./);
+      stream.match(/^global\./);
+    }
+
+    if (stream.eat("'")) {
+      stream.match(/^.*'/);
+      return "variable-2";
+    } else if (stream.eat('"')) {
+      stream.match(/^.*"/);
+      return "variable-2";
+    } else if (stream.eat("`")) {
+      stream.match(/^.*`/);
+      return "variable-2";
+    } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) {
+      return "variable-2";
+    }
+    return null;
+  };
+
+  // short client keyword token
+  function hookClient(stream) {
+    // \N means NULL
+    // ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
+    if (stream.eat("N")) {
+        return "atom";
+    }
+    // \g, etc
+    // ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
+    return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
+  }
+
+  // these keywords are used by all SQL dialects (however, a mode can still overwrite it)
+  var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit ";
+
+  // turn a space-separated list into an array
+  function set(str) {
+    var obj = {}, words = str.split(" ");
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+    return obj;
+  }
+
+  var defaultBuiltin = "bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"
+
+  // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
+  CodeMirror.defineMIME("text/x-sql", {
+    name: "sql",
+    keywords: set(sqlKeywords + "begin"),
+    builtin: set(defaultBuiltin),
+    atoms: set("false true null unknown"),
+    dateSQL: set("date time timestamp"),
+    support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
+  });
+
+
+  CodeMirror.defineMIME("text/x-mysql", {
+    name: "sql",
+    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
+    keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
+    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
+    atoms: set("false true null unknown"),
+    operatorChars: /^[*+\-%<>!=&|^]/,
+    dateSQL: set("date time timestamp"),
+    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
+    hooks: {
+      "@":   hookVar,
+      "`":   hookIdentifier,
+      "\\":  hookClient
+    }
+  });
+
+});
\ No newline at end of file
diff --git a/themes/bookstack/static/libs/docs-searchbar.min.css b/themes/bookstack/static/libs/docs-searchbar.min.css
new file mode 100644 (file)
index 0000000..ae33a95
--- /dev/null
@@ -0,0 +1,2 @@
+.searchbox{display:inline-block;position:relative;width:200px;height:32px;white-space:nowrap;box-sizing:border-box;visibility:visible}.searchbox .meilisearch-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox input{color:#555;display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox input::-webkit-search-cancel-button,.searchbox input::-webkit-search-decoration,.searchbox input::-webkit-search-results-button,.searchbox input::-webkit-search-results-decoration{display:none}.searchbox input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox input:active,.searchbox input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox input::-moz-placeholder{color:#aaa}.searchbox input:-ms-input-placeholder{color:#aaa}.searchbox input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes sbx-reset-in{0%{transform:translate3d(-20%,0,0);opacity:0}to{transform:none;opacity:1}}.meilisearch-autocomplete.meilisearch-autocomplete-right .dsb-dropdown-menu{right:0;left:inherit}.meilisearch-autocomplete.meilisearch-autocomplete-right .dsb-dropdown-menu:before{right:48px}.meilisearch-autocomplete.meilisearch-autocomplete-left .dsb-dropdown-menu{left:0;right:inherit}.meilisearch-autocomplete.meilisearch-autocomplete-left .dsb-dropdown-menu:before{left:48px}.meilisearch-autocomplete .dsb-dropdown-menu{position:relative;top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.meilisearch-autocomplete .dsb-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;transform:rotate(-45deg);border-radius:2px}.meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestions{position:relative;z-index:1000;margin-top:8px}.meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestions a:hover{text-decoration:none}.meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion{cursor:pointer}.meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion.dsb-cursor .docs-searchbar-suggestion.suggestion-layout-simple{background-color:rgba(69,142,225,.05)}.meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion.dsb-cursor .docs-searchbar-suggestion:not(.suggestion-layout-simple) .docs-searchbar-suggestion--content{background-color:rgba(69,142,225,.05)}.meilisearch-autocomplete .dsb-dropdown-menu [class^=dsb-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.meilisearch-autocomplete .dsb-dropdown-menu *{box-sizing:border-box}.meilisearch-autocomplete .docs-searchbar-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.meilisearch-autocomplete .docs-searchbar-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:0 .05em}.meilisearch-autocomplete .docs-searchbar-suggestion--category-header .docs-searchbar-suggestion--category-header-lvl0 .docs-searchbar-suggestion--highlight,.meilisearch-autocomplete .docs-searchbar-suggestion--category-header .docs-searchbar-suggestion--category-header-lvl1 .docs-searchbar-suggestion--highlight,.meilisearch-autocomplete .docs-searchbar-suggestion--text .docs-searchbar-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.meilisearch-autocomplete .docs-searchbar-suggestion--content{display:block;width:70%;position:relative;padding:5.3333333333px 0 5.3333333333px 10.6666666667px;cursor:pointer}.meilisearch-autocomplete .docs-searchbar-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.meilisearch-autocomplete .docs-searchbar-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.meilisearch-autocomplete .docs-searchbar-suggestion--wrapper{width:100%;display:flex;align-items:flex-start;padding:8px 0 0}.meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-column{width:30%;text-align:right;position:relative;padding:5.3333333333px 10.6666666667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-inline{display:none}.meilisearch-autocomplete .docs-searchbar-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.meilisearch-autocomplete .docs-searchbar-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.meilisearch-autocomplete .docs-searchbar-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.meilisearch-autocomplete .docs-searchbar-suggestion--no-results:before{display:none}.meilisearch-autocomplete .docs-searchbar-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.meilisearch-autocomplete .docs-searchbar-suggestion code .docs-searchbar-suggestion--highlight{background:none}.meilisearch-autocomplete .docs-searchbar-suggestion.docs-searchbar-suggestion__main .docs-searchbar-suggestion--category-header,.meilisearch-autocomplete .docs-searchbar-suggestion.docs-searchbar-suggestion__secondary{display:block}@media (min-width:768px){.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column{display:block}}@media (max-width:768px){.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column{display:inline-block;width:auto;text-align:left;padding:0;font-size:.9em;font-weight:700;opacity:.5;color:#02060c}.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column:before{display:none}.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column:after{content:"|"}.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--content{display:inline-block;width:auto;text-align:left;padding:0}.meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--content:before{display:none}}.meilisearch-autocomplete .suggestion-layout-simple.docs-searchbar-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--content{width:100%;padding:0}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--content:before{display:none}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl0,.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,<svg width="10" height="10" viewBox="0 0 20 38" xmlns="http://www.w3.org/2000/svg"><path d="M1.49 4.31l14 16.126.002-2.624-14 16.074-1.314 1.51 3.017 2.626 1.313-1.508 14-16.075 1.142-1.313-1.14-1.313-14-16.125L3.2.18.18 2.8l1.31 1.51z" fill-rule="evenodd" fill="%231D3657" /></svg>');content:"";width:10px;height:10px;display:inline-block}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--wrapper{width:100%;margin:0;padding:0}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--duplicate-content,.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--subcategory-inline{display:none}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.3333333333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--text .docs-searchbar-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.meilisearch-autocomplete .docs-searchbar-footer{width:100%;text-align:right;height:20px;z-index:2000;margin-top:10.6666666667px;color:#63676d;margin-left:auto}.meilisearch-autocomplete .docs-searchbar-footer-logo{margin-bottom:4px}div[data-ds-theme=dark] .searchbox{display:inline-block;position:relative;width:350px;height:32px;white-space:nowrap;box-sizing:border-box;visibility:visible}div[data-ds-theme=dark] .searchbox .meilisearch-autocomplete{display:block;width:100%;height:100%}div[data-ds-theme=dark] .searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}div[data-ds-theme=dark] .searchbox input{color:#eaeaea;display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #686d71;background:#444d52;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:90%;-webkit-appearance:none;-moz-appearance:none;appearance:none}div[data-ds-theme=dark] .searchbox input::-webkit-search-cancel-button,div[data-ds-theme=dark] .searchbox input::-webkit-search-decoration,div[data-ds-theme=dark] .searchbox input::-webkit-search-results-button,div[data-ds-theme=dark] .searchbox input::-webkit-search-results-decoration{display:none}div[data-ds-theme=dark] .searchbox input:hover{box-shadow:inset 0 0 0 1px #505356;box-shadow:inset 0 0 0 1px #747a7e}div[data-ds-theme=dark] .searchbox input:active,div[data-ds-theme=dark] .searchbox input:focus{outline:0;box-shadow:inset 0 0 0 1px #919598;background:#444d52}div[data-ds-theme=dark] .searchbox input::-moz-placeholder{color:#bbb}div[data-ds-theme=dark] .searchbox input:-ms-input-placeholder{color:#bbb}div[data-ds-theme=dark] .searchbox input::placeholder{color:#bbb}div[data-ds-theme=dark] .searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}div[data-ds-theme=dark] .searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}div[data-ds-theme=dark] .searchbox__submit:active,div[data-ds-theme=dark] .searchbox__submit:hover{cursor:pointer}div[data-ds-theme=dark] .searchbox__submit:focus{outline:0}div[data-ds-theme=dark] .searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}div[data-ds-theme=dark] .searchbox__reset{display:block;position:absolute;top:5px;right:5px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}div[data-ds-theme=dark] .searchbox__reset.hide{display:none}div[data-ds-theme=dark] .searchbox__reset:focus{outline:0}div[data-ds-theme=dark] .searchbox__reset svg{display:block;margin:4px;width:14px;height:14px}div[data-ds-theme=dark] .searchbox__input:valid~div[data-ds-theme=dark] .searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@keyframes sbx-reset-in{0%{transform:translate3d(-20%,0,0);opacity:0}to{transform:none;opacity:1}}div[data-ds-theme=dark] .meilisearch-autocomplete.meilisearch-autocomplete-right .dsb-dropdown-menu{right:0;left:inherit}div[data-ds-theme=dark] .meilisearch-autocomplete.meilisearch-autocomplete-right .dsb-dropdown-menu:before{right:48px}div[data-ds-theme=dark] .meilisearch-autocomplete.meilisearch-autocomplete-left .dsb-dropdown-menu{left:0;right:inherit}div[data-ds-theme=dark] .meilisearch-autocomplete.meilisearch-autocomplete-left .dsb-dropdown-menu:before{left:48px}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu{position:relative;top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#2c363e;z-index:1000;top:-7px;border-top:1px solid #5b6369;border-right:1px solid #5b6369;transform:rotate(-45deg);border-radius:2px}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestions{position:relative;z-index:1000;margin-top:8px}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestions a:hover{text-decoration:none}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion{cursor:pointer}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion.dsb-cursor .docs-searchbar-suggestion.suggestion-layout-simple{background-color:rgba(69,142,225,.5)}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu .dsb-suggestion.dsb-cursor .docs-searchbar-suggestion:not(.suggestion-layout-simple) .docs-searchbar-suggestion--content{background-color:rgba(69,142,225,.5)}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu [class^=dsb-dataset-]{position:relative;border:1px solid #5b6369;background:#2c363e;border-radius:4px;overflow:auto;padding:0 8px 8px}div[data-ds-theme=dark] .meilisearch-autocomplete .dsb-dropdown-menu *{box-sizing:border-box}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion{display:block;position:relative;padding:0 8px;background:#2c363e;color:#eaeaea;overflow:hidden}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:0 .05em;color:#9dc3ef}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--category-header .docs-searchbar-suggestion--category-header-lvl0 .docs-searchbar-suggestion--highlight,div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--category-header .docs-searchbar-suggestion--category-header-lvl1 .docs-searchbar-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--text .docs-searchbar-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--content{display:block;width:70%;position:relative;padding:5.3333333333px 0 5.3333333333px 10.6666666667px;cursor:pointer}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#7db0ea;color:#d5d5d5}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--wrapper{width:100%;display:flex;align-items:flex-start;padding:8px 0 0}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-column{width:30%;text-align:right;position:relative;padding:5.3333333333px 10.6666666667px;color:#bbb;font-size:.9em;word-wrap:break-word}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--subcategory-inline{display:none}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--title{margin-bottom:4px;color:#eaeaea;font-size:.9em;font-weight:700}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#eaeaea}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion--no-results:before{display:none}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion code .docs-searchbar-suggestion--highlight{background:none}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion.docs-searchbar-suggestion__main .docs-searchbar-suggestion--category-header{display:block}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion.docs-searchbar-suggestion__secondary{display:block}@media (min-width:768px){div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column{display:block}}@media (max-width:768px){div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column{display:inline-block;width:auto;text-align:left;padding:0;font-size:.9em;font-weight:700;opacity:.5;color:#02060c;color:#bbb;opacity:unset}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column:before{display:none}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--subcategory-column:after{content:"|"}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--content{display:inline-block;width:auto;text-align:left;padding:0}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-suggestion .docs-searchbar-suggestion--content:before{display:none}}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple.docs-searchbar-suggestion{padding:8px;margin:0;border-bottom:1px solid #737d84}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--content{width:100%;padding:0}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--content:before{display:none}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl0,div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl1{opacity:.6;font-size:.85em;opacity:unset;color:#d5d5d5}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,<svg width="10" height="10" viewBox="0 0 20 38" xmlns="http://www.w3.org/2000/svg"><path d="M1.49 4.31l14 16.126.002-2.624-14 16.074-1.314 1.51 3.017 2.626 1.313-1.508 14-16.075 1.142-1.313-1.14-1.313-14-16.125L3.2.18.18 2.8l1.31 1.51z" fill-rule="evenodd" fill="%231D3657" /></svg>');content:"";width:10px;height:10px;display:inline-block;filter:invert(1)}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--wrapper{width:100%;margin:0;padding:0}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--duplicate-content,div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--subcategory-inline{display:none}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400;color:#eaeaea}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block;color:#eaeaea}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.3333333333px 8px;background:#6b7278;font-size:.85em;opacity:.8}div[data-ds-theme=dark] .meilisearch-autocomplete .suggestion-layout-simple .docs-searchbar-suggestion--text .docs-searchbar-suggestion--highlight{color:#c4c4c4;font-weight:700;box-shadow:none;color:#fff}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-footer{width:100%;text-align:right;height:20px;z-index:2000;margin-top:10.6666666667px;color:#eaeaea;margin-left:auto}div[data-ds-theme=dark] .meilisearch-autocomplete .docs-searchbar-footer-logo{margin-bottom:4px;filter:invert(1)}
+/*# sourceMappingURL=docs-searchbar.min.css.map */
\ No newline at end of file
diff --git a/themes/bookstack/static/libs/docs-searchbar.min.js b/themes/bookstack/static/libs/docs-searchbar.min.js
new file mode 100644 (file)
index 0000000..3ba71ed
--- /dev/null
@@ -0,0 +1,2 @@
+/*! docs-searchbar UNRELEASED | © Meili | github.com/meilisearch/docs-searchbar.js */
+!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.docsSearchBar=e():t.docsSearchBar=e()}(self,function(){return(()=>{var t={639:(t,e,n)=>{"use strict";t.exports=n(281)},114:(t,e,n)=>{"use strict";var i=n(670),r={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none",opacity:"1"},input:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},inputWithNoHint:{position:"relative",verticalAlign:"top"},dropdown:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"},suggestions:{display:"block"},suggestion:{whiteSpace:"nowrap",cursor:"pointer"},suggestionChild:{whiteSpace:"normal"},ltr:{left:"0",right:"auto"},rtl:{left:"auto",right:"0"},defaultClasses:{root:"algolia-autocomplete",prefix:"aa",noPrefix:!1,dropdownMenu:"dropdown-menu",input:"input",hint:"hint",suggestions:"suggestions",suggestion:"suggestion",cursor:"cursor",dataset:"dataset",empty:"empty"},appendTo:{wrapper:{position:"absolute",zIndex:"100",display:"none"},input:{},inputWithNoHint:{},dropdown:{display:"block"}}};i.isMsie()&&i.mixin(r.input,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),i.isMsie()&&i.isMsie()<=7&&i.mixin(r.input,{marginTop:"-1px"}),t.exports=r},312:(t,e,n)=>{"use strict";var i="aaDataset",r="aaValue",s="aaDatum",o=n(670),a=n(855),u=n(619),c=n(114),l=n(823);function h(t){var e,n,i,r;(t=t||{}).templates=t.templates||{},t.source||o.error("missing source"),t.name&&(e=t.name,!/^[_a-zA-Z0-9-]+$/.test(e))&&o.error("invalid dataset name: "+t.name),this.query=null,this._isEmpty=!0,this.highlight=!!t.highlight,this.name=void 0===t.name||null===t.name?o.getUniqueId():t.name,this.source=t.source,this.displayFn=(r=(r=t.display||t.displayKey)||"value",o.isFunction(r)?r:function(t){return t[r]}),this.debounce=t.debounce,this.cache=!1!==t.cache,this.templates=(n=t.templates,i=this.displayFn,{empty:n.empty&&o.templatify(n.empty),header:n.header&&o.templatify(n.header),footer:n.footer&&o.templatify(n.footer),suggestion:n.suggestion||function(t){return"<p>"+i(t)+"</p>"}}),this.css=o.mixin({},c,t.appendTo?c.appendTo:{}),this.cssClasses=t.cssClasses=o.mixin({},c.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix||o.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix);var s=o.className(this.cssClasses.prefix,this.cssClasses.dataset);this.$el=t.$menu&&t.$menu.find(s+"-"+this.name).length>0?a.element(t.$menu.find(s+"-"+this.name)[0]):a.element(u.dataset.replace("%CLASS%",this.name).replace("%PREFIX%",this.cssClasses.prefix).replace("%DATASET%",this.cssClasses.dataset)),this.$menu=t.$menu,this.clearCachedSuggestions()}h.extractDatasetName=function(t){return a.element(t).data(i)},h.extractValue=function(t){return a.element(t).data(r)},h.extractDatum=function(t){var e=a.element(t).data(s);return"string"==typeof e&&(e=JSON.parse(e)),e},o.mixin(h.prototype,l,{_render:function(t,e){if(this.$el){var n,c=this,l=[].slice.call(arguments,2);if(this.$el.empty(),n=e&&e.length,this._isEmpty=!n,!n&&this.templates.empty)this.$el.html(h.apply(this,l)).prepend(c.templates.header?d.apply(this,l):null).append(c.templates.footer?f.apply(this,l):null);else if(n)this.$el.html(p.apply(this,l)).prepend(c.templates.header?d.apply(this,l):null).append(c.templates.footer?f.apply(this,l):null);else if(e&&!Array.isArray(e))throw new TypeError("suggestions must be an array");this.$menu&&this.$menu.addClass(this.cssClasses.prefix+(n?"with":"without")+"-"+this.name).removeClass(this.cssClasses.prefix+(n?"without":"with")+"-"+this.name),this.trigger("rendered",t)}function h(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!0}].concat(e),c.templates.empty.apply(this,e)}function p(){var t,n,l=[].slice.call(arguments,0),h=this,p=u.suggestions.replace("%PREFIX%",this.cssClasses.prefix).replace("%SUGGESTIONS%",this.cssClasses.suggestions);return t=a.element(p).css(this.css.suggestions),n=o.map(e,d),t.append.apply(t,n),t;function d(t){var e,n=u.suggestion.replace("%PREFIX%",h.cssClasses.prefix).replace("%SUGGESTION%",h.cssClasses.suggestion);return(e=a.element(n).attr({role:"option",id:["option",Math.floor(1e8*Math.random())].join("-")}).append(c.templates.suggestion.apply(this,[t].concat(l)))).data(i,c.name),e.data(r,c.displayFn(t)||void 0),e.data(s,JSON.stringify(t)),e.children().each(function(){a.element(this).css(h.css.suggestionChild)}),e}}function d(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!n}].concat(e),c.templates.header.apply(this,e)}function f(){var e=[].slice.call(arguments,0);return e=[{query:t,isEmpty:!n}].concat(e),c.templates.footer.apply(this,e)}},getRoot:function(){return this.$el},update:function(t){function e(e){if(!this.canceled&&t===this.query){var n=[].slice.call(arguments,1);this.cacheSuggestions(t,e,n),this._render.apply(this,[t,e].concat(n))}}if(this.query=t,this.canceled=!1,this.shouldFetchFromCache(t))e.apply(this,[this.cachedSuggestions].concat(this.cachedRenderExtraArgs));else{var n=this,i=function(){n.canceled||n.source(t,e.bind(n))};this.debounce?(clearTimeout(this.debounceTimeout),this.debounceTimeout=setTimeout(function(){n.debounceTimeout=null,i()},this.debounce)):i()}},cacheSuggestions:function(t,e,n){this.cachedQuery=t,this.cachedSuggestions=e,this.cachedRenderExtraArgs=n},shouldFetchFromCache:function(t){return this.cache&&this.cachedQuery===t&&this.cachedSuggestions&&this.cachedSuggestions.length},clearCachedSuggestions:function(){delete this.cachedQuery,delete this.cachedSuggestions,delete this.cachedRenderExtraArgs},cancel:function(){this.canceled=!0},clear:function(){this.$el&&(this.cancel(),this.$el.empty(),this.trigger("rendered",""))},isEmpty:function(){return this._isEmpty},destroy:function(){this.clearCachedSuggestions(),this.$el=null}}),t.exports=h},445:(t,e,n)=>{"use strict";var i=n(670),r=n(855),s=n(823),o=n(312),a=n(114);function u(t){var e,n,s,o=this;(t=t||{}).menu||i.error("menu is required"),i.isArray(t.datasets)||i.isObject(t.datasets)||i.error("1 or more datasets required"),t.datasets||i.error("datasets is required"),this.isOpen=!1,this.isEmpty=!0,this.minLength=t.minLength||0,this.templates={},this.appendTo=t.appendTo||!1,this.css=i.mixin({},a,t.appendTo?a.appendTo:{}),this.cssClasses=t.cssClasses=i.mixin({},a.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix||i.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix),e=i.bind(this._onSuggestionClick,this),n=i.bind(this._onSuggestionMouseEnter,this),s=i.bind(this._onSuggestionMouseLeave,this);var c=i.className(this.cssClasses.prefix,this.cssClasses.suggestion);this.$menu=r.element(t.menu).on("mouseenter.aa",c,n).on("mouseleave.aa",c,s).on("click.aa",c,e),this.$container=t.appendTo?t.wrapper:this.$menu,t.templates&&t.templates.header&&(this.templates.header=i.templatify(t.templates.header),this.$menu.prepend(this.templates.header())),t.templates&&t.templates.empty&&(this.templates.empty=i.templatify(t.templates.empty),this.$empty=r.element('<div class="'+i.className(this.cssClasses.prefix,this.cssClasses.empty,!0)+'"></div>'),this.$menu.append(this.$empty),this.$empty.hide()),this.datasets=i.map(t.datasets,function(e){return function(t,e,n){return new u.Dataset(i.mixin({$menu:t,cssClasses:n},e))}(o.$menu,e,t.cssClasses)}),i.each(this.datasets,function(t){var e=t.getRoot();e&&0===e.parent().length&&o.$menu.append(e),t.onSync("rendered",o._onRendered,o)}),t.templates&&t.templates.footer&&(this.templates.footer=i.templatify(t.templates.footer),this.$menu.append(this.templates.footer()));var l=this;r.element(window).resize(function(){l._redraw()})}i.mixin(u.prototype,s,{_onSuggestionClick:function(t){this.trigger("suggestionClicked",r.element(t.currentTarget))},_onSuggestionMouseEnter:function(t){var e=r.element(t.currentTarget);if(!e.hasClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0))){this._removeCursor();var n=this;setTimeout(function(){n._setCursor(e,!1)},0)}},_onSuggestionMouseLeave:function(t){t.relatedTarget&&r.element(t.relatedTarget).closest("."+i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).length>0||(this._removeCursor(),this.trigger("cursorRemoved"))},_onRendered:function(t,e){if(this.isEmpty=i.every(this.datasets,function(t){return t.isEmpty()}),this.isEmpty)if(e.length>=this.minLength&&this.trigger("empty"),this.$empty)if(e.length<this.minLength)this._hide();else{var n=this.templates.empty({query:this.datasets[0]&&this.datasets[0].query});this.$empty.html(n),this.$empty.show(),this._show()}else i.any(this.datasets,function(t){return t.templates&&t.templates.empty})?e.length<this.minLength?this._hide():this._show():this._hide();else this.isOpen&&(this.$empty&&(this.$empty.empty(),this.$empty.hide()),e.length>=this.minLength?this._show():this._hide());this.trigger("datasetRendered")},_hide:function(){this.$container.hide()},_show:function(){this.$container.css("display","block"),this._redraw(),this.trigger("shown")},_redraw:function(){this.isOpen&&this.appendTo&&this.trigger("redrawn")},_getSuggestions:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.suggestion))},_getCursor:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.cursor)).first()},_setCursor:function(t,e){t.first().addClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).attr("aria-selected","true"),this.trigger("cursorMoved",e)},_removeCursor:function(){this._getCursor().removeClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).removeAttr("aria-selected")},_moveCursor:function(t){var e,n,i,r;this.isOpen&&(n=this._getCursor(),e=this._getSuggestions(),this._removeCursor(),-1!=(i=((i=e.index(n)+t)+1)%(e.length+1)-1)?(i<-1&&(i=e.length-1),this._setCursor(r=e.eq(i),!0),this._ensureVisible(r)):this.trigger("cursorRemoved"))},_ensureVisible:function(t){var e,n,i,r;n=(e=t.position().top)+t.height()+parseInt(t.css("margin-top"),10)+parseInt(t.css("margin-bottom"),10),i=this.$menu.scrollTop(),r=this.$menu.height()+parseInt(this.$menu.css("padding-top"),10)+parseInt(this.$menu.css("padding-bottom"),10),e<0?this.$menu.scrollTop(i+e):r<n&&this.$menu.scrollTop(i+(n-r))},close:function(){this.isOpen&&(this.isOpen=!1,this._removeCursor(),this._hide(),this.trigger("closed"))},open:function(){this.isOpen||(this.isOpen=!0,this.isEmpty||this._show(),this.trigger("opened"))},setLanguageDirection:function(t){this.$menu.css("ltr"===t?this.css.ltr:this.css.rtl)},moveCursorUp:function(){this._moveCursor(-1)},moveCursorDown:function(){this._moveCursor(1)},getDatumForSuggestion:function(t){var e=null;return t.length&&(e={raw:o.extractDatum(t),value:o.extractValue(t),datasetName:o.extractDatasetName(t)}),e},getCurrentCursor:function(){return this._getCursor().first()},getDatumForCursor:function(){return this.getDatumForSuggestion(this._getCursor().first())},getDatumForTopSuggestion:function(){return this.getDatumForSuggestion(this._getSuggestions().first())},cursorTopSuggestion:function(){this._setCursor(this._getSuggestions().first(),!1)},update:function(t){i.each(this.datasets,function(e){e.update(t)})},empty:function(){i.each(this.datasets,function(t){t.clear()}),this.isEmpty=!0},isVisible:function(){return this.isOpen&&!this.isEmpty},destroy:function(){this.$menu.off(".aa"),this.$menu=null,i.each(this.datasets,function(t){t.destroy()})}}),u.Dataset=o,t.exports=u},368:(t,e,n)=>{"use strict";var i=n(670),r=n(855);function s(t){t&&t.el||i.error("EventBus initialized without el"),this.$el=r.element(t.el)}i.mixin(s.prototype,{trigger:function(t,e,n,r){var s=i.Event("autocomplete:"+t);return this.$el.trigger(s,[e,n,r]),s}}),t.exports=s},823:(t,e,n)=>{"use strict";var i=n(624),r=/\s+/;function s(t,e,n,i){var s;if(!n)return this;for(e=e.split(r),n=i?function(t,e){return t.bind?t.bind(e):function(){t.apply(e,[].slice.call(arguments,0))}}(n,i):n,this._callbacks=this._callbacks||{};s=e.shift();)this._callbacks[s]=this._callbacks[s]||{sync:[],async:[]},this._callbacks[s][t].push(n);return this}function o(t,e,n){return function(){for(var i,r=0,s=t.length;!i&&r<s;r+=1)i=!1===t[r].apply(e,n);return!i}}t.exports={onSync:function(t,e,n){return s.call(this,"sync",t,e,n)},onAsync:function(t,e,n){return s.call(this,"async",t,e,n)},off:function(t){var e;if(!this._callbacks)return this;for(t=t.split(r);e=t.shift();)delete this._callbacks[e];return this},trigger:function(t){var e,n,s,a,u;if(!this._callbacks)return this;for(t=t.split(r),s=[].slice.call(arguments,1);(e=t.shift())&&(n=this._callbacks[e]);)a=o(n.sync,this,[e].concat(s)),u=o(n.async,this,[e].concat(s)),a()&&i(u);return this}}},619:t=>{"use strict";t.exports={wrapper:'<span class="%ROOT%"></span>',dropdown:'<span class="%PREFIX%%DROPDOWN_MENU%"></span>',dataset:'<div class="%PREFIX%%DATASET%-%CLASS%"></div>',suggestions:'<span class="%PREFIX%%SUGGESTIONS%"></span>',suggestion:'<div class="%PREFIX%%SUGGESTION%"></div>'}},286:(t,e,n)=>{"use strict";var i;i={9:"tab",27:"esc",37:"left",39:"right",13:"enter",38:"up",40:"down"};var r=n(670),s=n(855),o=n(823);function a(t){var e,n,o,a,u,c=this;(t=t||{}).input||r.error("input is missing"),e=r.bind(this._onBlur,this),n=r.bind(this._onFocus,this),o=r.bind(this._onKeydown,this),a=r.bind(this._onInput,this),this.$hint=s.element(t.hint),this.$input=s.element(t.input).on("blur.aa",e).on("focus.aa",n).on("keydown.aa",o),0===this.$hint.length&&(this.setHint=this.getHint=this.clearHint=this.clearHintIfInvalid=r.noop),r.isMsie()?this.$input.on("keydown.aa keypress.aa cut.aa paste.aa",function(t){i[t.which||t.keyCode]||r.defer(r.bind(c._onInput,c,t))}):this.$input.on("input.aa",a),this.query=this.$input.val(),this.$overflowHelper=(u=this.$input,s.element('<pre aria-hidden="true"></pre>').css({position:"absolute",visibility:"hidden",whiteSpace:"pre",fontFamily:u.css("font-family"),fontSize:u.css("font-size"),fontStyle:u.css("font-style"),fontVariant:u.css("font-variant"),fontWeight:u.css("font-weight"),wordSpacing:u.css("word-spacing"),letterSpacing:u.css("letter-spacing"),textIndent:u.css("text-indent"),textRendering:u.css("text-rendering"),textTransform:u.css("text-transform")}).insertAfter(u))}function u(t){return t.altKey||t.ctrlKey||t.metaKey||t.shiftKey}a.normalizeQuery=function(t){return(t||"").replace(/^\s*/g,"").replace(/\s{2,}/g," ")},r.mixin(a.prototype,o,{_onBlur:function(){this.resetInputValue(),this.$input.removeAttr("aria-activedescendant"),this.trigger("blurred")},_onFocus:function(){this.trigger("focused")},_onKeydown:function(t){var e=i[t.which||t.keyCode];this._managePreventDefault(e,t),e&&this._shouldTrigger(e,t)&&this.trigger(e+"Keyed",t)},_onInput:function(){this._checkInputValue()},_managePreventDefault:function(t,e){var n,i,r;switch(t){case"tab":i=this.getHint(),r=this.getInputValue(),n=i&&i!==r&&!u(e);break;case"up":case"down":n=!u(e);break;default:n=!1}n&&e.preventDefault()},_shouldTrigger:function(t,e){var n;switch(t){case"tab":n=!u(e);break;default:n=!0}return n},_checkInputValue:function(){var t,e,n,i,r;i=t=this.getInputValue(),r=this.query,n=!(!(e=a.normalizeQuery(i)===a.normalizeQuery(r))||!this.query)&&this.query.length!==t.length,this.query=t,e?n&&this.trigger("whitespaceChanged",this.query):this.trigger("queryChanged",this.query)},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getQuery:function(){return this.query},setQuery:function(t){this.query=t},getInputValue:function(){return this.$input.val()},setInputValue:function(t,e){void 0===t&&(t=this.query),this.$input.val(t),e?this.clearHint():this._checkInputValue()},expand:function(){this.$input.attr("aria-expanded","true")},collapse:function(){this.$input.attr("aria-expanded","false")},setActiveDescendant:function(t){this.$input.attr("aria-activedescendant",t)},removeActiveDescendant:function(){this.$input.removeAttr("aria-activedescendant")},resetInputValue:function(){this.setInputValue(this.query,!0)},getHint:function(){return this.$hint.val()},setHint:function(t){this.$hint.val(t)},clearHint:function(){this.setHint("")},clearHintIfInvalid:function(){var t,e,n;n=(t=this.getInputValue())!==(e=this.getHint())&&0===e.indexOf(t),""!==t&&n&&!this.hasOverflow()||this.clearHint()},getLanguageDirection:function(){return(this.$input.css("direction")||"ltr").toLowerCase()},hasOverflow:function(){var t=this.$input.width()-2;return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>=t},isCursorAtEnd:function(){var t,e,n;return t=this.$input.val().length,e=this.$input[0].selectionStart,r.isNumber(e)?e===t:!document.selection||((n=document.selection.createRange()).moveStart("character",-t),t===n.text.length)},destroy:function(){this.$hint.off(".aa"),this.$input.off(".aa"),this.$hint=this.$input=this.$overflowHelper=null}}),t.exports=a},520:(t,e,n)=>{"use strict";var i="aaAttrs",r=n(670),s=n(855),o=n(368),a=n(286),u=n(445),c=n(619),l=n(114);function h(t){var e,n;if((t=t||{}).input||r.error("missing input"),this.isActivated=!1,this.debug=!!t.debug,this.autoselect=!!t.autoselect,this.autoselectOnBlur=!!t.autoselectOnBlur,this.openOnFocus=!!t.openOnFocus,this.minLength=r.isNumber(t.minLength)?t.minLength:1,this.autoWidth=void 0===t.autoWidth||!!t.autoWidth,this.clearOnSelected=!!t.clearOnSelected,this.tabAutocomplete=void 0===t.tabAutocomplete||!!t.tabAutocomplete,t.hint=!!t.hint,t.hint&&t.appendTo)throw new Error("[autocomplete.js] hint and appendTo options can't be used at the same time");this.css=t.css=r.mixin({},l,t.appendTo?l.appendTo:{}),this.cssClasses=t.cssClasses=r.mixin({},l.defaultClasses,t.cssClasses||{}),this.cssClasses.prefix=t.cssClasses.formattedPrefix=r.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix),this.listboxId=t.listboxId=[this.cssClasses.root,"listbox",r.getUniqueId()].join("-");var a=function(t){var e,n,o,a;e=s.element(t.input),n=s.element(c.wrapper.replace("%ROOT%",t.cssClasses.root)).css(t.css.wrapper),t.appendTo||"block"!==e.css("display")||"table"!==e.parent().css("display")||n.css("display","table-cell");var u,l=c.dropdown.replace("%PREFIX%",t.cssClasses.prefix).replace("%DROPDOWN_MENU%",t.cssClasses.dropdownMenu);o=s.element(l).css(t.css.dropdown).attr({role:"listbox",id:t.listboxId}),t.templates&&t.templates.dropdownMenu&&o.html(r.templatify(t.templates.dropdownMenu)()),(a=e.clone().css(t.css.hint).css((u=e,{backgroundAttachment:u.css("background-attachment"),backgroundClip:u.css("background-clip"),backgroundColor:u.css("background-color"),backgroundImage:u.css("background-image"),backgroundOrigin:u.css("background-origin"),backgroundPosition:u.css("background-position"),backgroundRepeat:u.css("background-repeat"),backgroundSize:u.css("background-size")}))).val("").addClass(r.className(t.cssClasses.prefix,t.cssClasses.hint,!0)).removeAttr("id name placeholder required").prop("readonly",!0).attr({"aria-hidden":"true",autocomplete:"off",spellcheck:"false",tabindex:-1}),a.removeData&&a.removeData(),e.data(i,{"aria-autocomplete":e.attr("aria-autocomplete"),"aria-expanded":e.attr("aria-expanded"),"aria-owns":e.attr("aria-owns"),autocomplete:e.attr("autocomplete"),dir:e.attr("dir"),role:e.attr("role"),spellcheck:e.attr("spellcheck"),style:e.attr("style"),type:e.attr("type")}),e.addClass(r.className(t.cssClasses.prefix,t.cssClasses.input,!0)).attr({autocomplete:"off",spellcheck:!1,role:"combobox","aria-autocomplete":t.datasets&&t.datasets[0]&&t.datasets[0].displayKey?"both":"list","aria-expanded":"false","aria-label":t.ariaLabel,"aria-owns":t.listboxId}).css(t.hint?t.css.input:t.css.inputWithNoHint);try{e.attr("dir")||e.attr("dir","auto")}catch(t){}return(n=t.appendTo?n.appendTo(s.element(t.appendTo).eq(0)).eq(0):e.wrap(n).parent()).prepend(t.hint?a:null).append(o),{wrapper:n,input:e,hint:a,menu:o}}(t);this.$node=a.wrapper;var u=this.$input=a.input;e=a.menu,n=a.hint,t.dropdownMenuContainer&&s.element(t.dropdownMenuContainer).css("position","relative").append(e.css("top","0")),u.on("blur.aa",function(t){var n=document.activeElement;r.isMsie()&&(e[0]===n||e[0].contains(n))&&(t.preventDefault(),t.stopImmediatePropagation(),r.defer(function(){u.focus()}))}),e.on("mousedown.aa",function(t){t.preventDefault()}),this.eventBus=t.eventBus||new o({el:u}),this.dropdown=new h.Dropdown({appendTo:t.appendTo,wrapper:this.$node,menu:e,datasets:t.datasets,templates:t.templates,cssClasses:t.cssClasses,minLength:this.minLength}).onSync("suggestionClicked",this._onSuggestionClicked,this).onSync("cursorMoved",this._onCursorMoved,this).onSync("cursorRemoved",this._onCursorRemoved,this).onSync("opened",this._onOpened,this).onSync("closed",this._onClosed,this).onSync("shown",this._onShown,this).onSync("empty",this._onEmpty,this).onSync("redrawn",this._onRedrawn,this).onAsync("datasetRendered",this._onDatasetRendered,this),this.input=new h.Input({input:u,hint:n}).onSync("focused",this._onFocused,this).onSync("blurred",this._onBlurred,this).onSync("enterKeyed",this._onEnterKeyed,this).onSync("tabKeyed",this._onTabKeyed,this).onSync("escKeyed",this._onEscKeyed,this).onSync("upKeyed",this._onUpKeyed,this).onSync("downKeyed",this._onDownKeyed,this).onSync("leftKeyed",this._onLeftKeyed,this).onSync("rightKeyed",this._onRightKeyed,this).onSync("queryChanged",this._onQueryChanged,this).onSync("whitespaceChanged",this._onWhitespaceChanged,this),this._bindKeyboardShortcuts(t),this._setLanguageDirection()}r.mixin(h.prototype,{_bindKeyboardShortcuts:function(t){if(t.keyboardShortcuts){var e=this.$input,n=[];r.each(t.keyboardShortcuts,function(t){"string"==typeof t&&(t=t.toUpperCase().charCodeAt(0)),n.push(t)}),s.element(document).keydown(function(t){var i=t.target||t.srcElement,r=i.tagName;if(!i.isContentEditable&&"INPUT"!==r&&"SELECT"!==r&&"TEXTAREA"!==r){var s=t.which||t.keyCode;-1!==n.indexOf(s)&&(e.focus(),t.stopPropagation(),t.preventDefault())}})}},_onSuggestionClicked:function(t,e){var n;(n=this.dropdown.getDatumForSuggestion(e))&&this._select(n,{selectionMethod:"click"})},_onCursorMoved:function(t,e){var n=this.dropdown.getDatumForCursor(),i=this.dropdown.getCurrentCursor().attr("id");this.input.setActiveDescendant(i),n&&(e&&this.input.setInputValue(n.value,!0),this.eventBus.trigger("cursorchanged",n.raw,n.datasetName))},_onCursorRemoved:function(){this.input.resetInputValue(),this._updateHint(),this.eventBus.trigger("cursorremoved")},_onDatasetRendered:function(){this._updateHint(),this.eventBus.trigger("updated")},_onOpened:function(){this._updateHint(),this.input.expand(),this.eventBus.trigger("opened")},_onEmpty:function(){this.eventBus.trigger("empty")},_onRedrawn:function(){this.$node.css("top","0px"),this.$node.css("left","0px");var t=this.$input[0].getBoundingClientRect();this.autoWidth&&this.$node.css("width",t.width+"px");var e=this.$node[0].getBoundingClientRect(),n=t.bottom-e.top;this.$node.css("top",n+"px");var i=t.left-e.left;this.$node.css("left",i+"px"),this.eventBus.trigger("redrawn")},_onShown:function(){this.eventBus.trigger("shown"),this.autoselect&&this.dropdown.cursorTopSuggestion()},_onClosed:function(){this.input.clearHint(),this.input.removeActiveDescendant(),this.input.collapse(),this.eventBus.trigger("closed")},_onFocused:function(){if(this.isActivated=!0,this.openOnFocus){var t=this.input.getQuery();t.length>=this.minLength?this.dropdown.update(t):this.dropdown.empty(),this.dropdown.open()}},_onBlurred:function(){var t,e;t=this.dropdown.getDatumForCursor(),e=this.dropdown.getDatumForTopSuggestion();var n={selectionMethod:"blur"};this.debug||(this.autoselectOnBlur&&t?this._select(t,n):this.autoselectOnBlur&&e?this._select(e,n):(this.isActivated=!1,this.dropdown.empty(),this.dropdown.close()))},_onEnterKeyed:function(t,e){var n,i;n=this.dropdown.getDatumForCursor(),i=this.dropdown.getDatumForTopSuggestion();var r={selectionMethod:"enterKey"};n?(this._select(n,r),e.preventDefault()):this.autoselect&&i&&(this._select(i,r),e.preventDefault())},_onTabKeyed:function(t,e){var n;this.tabAutocomplete?(n=this.dropdown.getDatumForCursor())?(this._select(n,{selectionMethod:"tabKey"}),e.preventDefault()):this._autocomplete(!0):this.dropdown.close()},_onEscKeyed:function(){this.dropdown.close(),this.input.resetInputValue()},_onUpKeyed:function(){var t=this.input.getQuery();this.dropdown.isEmpty&&t.length>=this.minLength?this.dropdown.update(t):this.dropdown.moveCursorUp(),this.dropdown.open()},_onDownKeyed:function(){var t=this.input.getQuery();this.dropdown.isEmpty&&t.length>=this.minLength?this.dropdown.update(t):this.dropdown.moveCursorDown(),this.dropdown.open()},_onLeftKeyed:function(){"rtl"===this.dir&&this._autocomplete()},_onRightKeyed:function(){"ltr"===this.dir&&this._autocomplete()},_onQueryChanged:function(t,e){this.input.clearHintIfInvalid(),e.length>=this.minLength?this.dropdown.update(e):this.dropdown.empty(),this.dropdown.open(),this._setLanguageDirection()},_onWhitespaceChanged:function(){this._updateHint(),this.dropdown.open()},_setLanguageDirection:function(){var t=this.input.getLanguageDirection();this.dir!==t&&(this.dir=t,this.$node.css("direction",t),this.dropdown.setLanguageDirection(t))},_updateHint:function(){var t,e,n,i,s;(t=this.dropdown.getDatumForTopSuggestion())&&this.dropdown.isVisible()&&!this.input.hasOverflow()?(e=this.input.getInputValue(),n=a.normalizeQuery(e),i=r.escapeRegExChars(n),(s=new RegExp("^(?:"+i+")(.+$)","i").exec(t.value))?this.input.setHint(e+s[1]):this.input.clearHint()):this.input.clearHint()},_autocomplete:function(t){var e,n,i,r;e=this.input.getHint(),n=this.input.getQuery(),i=t||this.input.isCursorAtEnd(),e&&n!==e&&i&&((r=this.dropdown.getDatumForTopSuggestion())&&this.input.setInputValue(r.value),this.eventBus.trigger("autocompleted",r.raw,r.datasetName))},_select:function(t,e){void 0!==t.value&&this.input.setQuery(t.value),this.clearOnSelected?this.setVal(""):this.input.setInputValue(t.value,!0),this._setLanguageDirection(),!1===this.eventBus.trigger("selected",t.raw,t.datasetName,e).isDefaultPrevented()&&(this.dropdown.close(),r.defer(r.bind(this.dropdown.empty,this.dropdown)))},open:function(){if(!this.isActivated){var t=this.input.getInputValue();t.length>=this.minLength?this.dropdown.update(t):this.dropdown.empty()}this.dropdown.open()},close:function(){this.dropdown.close()},setVal:function(t){t=r.toStr(t),this.isActivated?this.input.setInputValue(t):(this.input.setQuery(t),this.input.setInputValue(t,!0)),this._setLanguageDirection()},getVal:function(){return this.input.getQuery()},destroy:function(){var t,e,n;this.input.destroy(),this.dropdown.destroy(),t=this.$node,e=this.cssClasses,n=t.find(r.className(e.prefix,e.input)),r.each(n.data(i),function(t,e){void 0===t?n.removeAttr(e):n.attr(e,t)}),n.detach().removeClass(r.className(e.prefix,e.input,!0)).insertAfter(t),n.removeData&&n.removeData(i),t.remove(),this.$node=null},getWrapper:function(){return this.dropdown.$container[0]}}),h.Dropdown=u,h.Input=a,h.sources=n(331),t.exports=h},855:t=>{"use strict";t.exports={element:null}},926:t=>{"use strict";t.exports=function(t){var e=t.match(/Algolia for JavaScript \((\d+\.)(\d+\.)(\d+)\)/)||t.match(/Algolia for vanilla JavaScript (\d+\.)(\d+\.)(\d+)/);if(e)return[e[1],e[2],e[3]]}},670:(t,e,n)=>{"use strict";var i,r=n(855);function s(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}t.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(t){if(void 0===t&&(t=navigator.userAgent),/(msie|trident)/i.test(t)){var e=t.match(/(msie |rv:)(\d+(.\d+)?)/i);if(e)return e[2]}return!1},escapeRegExChars:function(t){return t.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(t){return"number"==typeof t},toStr:function(t){return null==t?"":t+""},cloneDeep:function(t){var e=this.mixin({},t),n=this;return this.each(e,function(t,i){t&&(n.isArray(t)?e[i]=[].concat(t):n.isObject(t)&&(e[i]=n.cloneDeep(t)))}),e},error:function(t){throw new Error(t)},every:function(t,e){var n=!0;return t?(this.each(t,function(i,r){n&&(n=e.call(null,i,r,t)&&n)}),!!n):n},any:function(t,e){var n=!1;return t?(this.each(t,function(i,r){if(e.call(null,i,r,t))return n=!0,!1}),n):n},getUniqueId:(i=0,function(){return i++}),templatify:function(t){if(this.isFunction(t))return t;var e=r.element(t);return"SCRIPT"===e.prop("tagName")?function(){return e.text()}:function(){return String(t)}},defer:function(t){setTimeout(t,0)},noop:function(){},formatPrefix:function(t,e){return e?"":t+"-"},className:function(t,e,n){return(n?"":".")+t+e},escapeHighlightedString:function(t,e,n){e=e||"<em>";var i=document.createElement("div");i.appendChild(document.createTextNode(e)),n=n||"</em>";var r=document.createElement("div");r.appendChild(document.createTextNode(n));var o=document.createElement("div");return o.appendChild(document.createTextNode(t)),o.innerHTML.replace(RegExp(s(i.innerHTML),"g"),e).replace(RegExp(s(r.innerHTML),"g"),n)}}},683:(t,e,n)=>{"use strict";var i,r,s=n(670),o=n(489),a=n(926),u=(i=[],r=window.Promise.resolve(),function(t,e){return function(n,o){var a,u;(a=t.as,u=i.push({indexName:t.indexName,query:n,params:e})-1,window.Promise.resolve().then(function(){return i.length&&(r=a.search(i),i=[]),r}).then(function(t){if(t)return t.results[u]})).then(function(t){t&&o(t.hits,t)}).catch(function(t){s.error(t.message)})}});t.exports=function(t,e){var n=a(t.as._ua);if(n&&n[0]>=3&&n[1]>20){var i="autocomplete.js "+o;-1===t.as._ua.indexOf(i)&&(t.as._ua+="; "+i)}return u(t,e)}},331:(t,e,n)=>{"use strict";t.exports={hits:n(683),popularIn:n(226)}},226:(t,e,n)=>{"use strict";var i=n(670),r=n(489),s=n(926);t.exports=function(t,e,n,o){var a=s(t.as._ua);if(a&&a[0]>=3&&a[1]>20&&((e=e||{}).additionalUA="autocomplete.js "+r),!n.source)return i.error("Missing 'source' key");var u=i.isFunction(n.source)?n.source:function(t){return t[n.source]};if(!n.index)return i.error("Missing 'index' key");var c=n.index;return o=o||{},function(a,l){t.search(a,e,function(t,a){if(t)i.error(t.message);else{if(a.hits.length>0){var h=a.hits[0],p=i.mixin({hitsPerPage:0},n);delete p.source,delete p.index;var d=s(c.as._ua);return d&&d[0]>=3&&d[1]>20&&(e.additionalUA="autocomplete.js "+r),void c.search(u(h),p,function(t,e){if(t)i.error(t.message);else{var n=[];if(o.includeAll){var r=o.allTitle||"All departments";n.push(i.mixin({facet:{value:r,count:e.nbHits}},i.cloneDeep(h)))}i.each(e.facets,function(t,e){i.each(t,function(t,r){n.push(i.mixin({facet:{facet:e,value:r,count:t}},i.cloneDeep(h)))})});for(var s=1;s<a.hits.length;++s)n.push(a.hits[s]);l(n,a)}})}l([])}})}}},281:(t,e,n)=>{"use strict";var i=n(939);n(855).element=i;var r=n(670);r.isArray=i.isArray,r.isFunction=i.isFunction,r.isObject=i.isPlainObject,r.bind=i.proxy,r.each=function(t,e){i.each(t,function(t,n){return e(n,t)})},r.map=i.map,r.mixin=i.extend,r.Event=i.Event;var s="aaAutocomplete",o=n(520),a=n(368);function u(t,e,n,u){n=r.isArray(n)?n:[].slice.call(arguments,2);var c=i(t).each(function(t,r){var c=i(r),l=new a({el:c}),h=u||new o({input:c,eventBus:l,dropdownMenuContainer:e.dropdownMenuContainer,hint:void 0===e.hint||!!e.hint,minLength:e.minLength,autoselect:e.autoselect,autoselectOnBlur:e.autoselectOnBlur,tabAutocomplete:e.tabAutocomplete,openOnFocus:e.openOnFocus,templates:e.templates,debug:e.debug,clearOnSelected:e.clearOnSelected,cssClasses:e.cssClasses,datasets:n,keyboardShortcuts:e.keyboardShortcuts,appendTo:e.appendTo,autoWidth:e.autoWidth,ariaLabel:e.ariaLabel||r.getAttribute("aria-label")});c.data(s,h)});return c.autocomplete={},r.each(["open","close","getVal","setVal","destroy","getWrapper"],function(t){c.autocomplete[t]=function(){var e,n=arguments;return c.each(function(r,o){var a=i(o).data(s);e=a[t].apply(a,n)}),e}}),c}u.sources=o.sources,u.escapeHighlightedString=r.escapeHighlightedString;var c="autocomplete"in window,l=window.autocomplete;u.noConflict=function(){return c?window.autocomplete=l:delete window.autocomplete,u},t.exports=u},489:t=>{t.exports="0.38.0"},939:t=>{var e;e=window,t.exports=function(t){var e,n,i=function(){var e,n,i,r,s,o,a=[],u=a.concat,c=a.filter,l=a.slice,h=t.document,p={},d={},f={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},m=/^\s*<(\w+|!)[^>]*>/,g=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,y=/^(?:body|html)$/i,b=/([A-Z])/g,w=["val","css","html","text","data","width","height","offset"],x=h.createElement("table"),S=h.createElement("tr"),C={tr:h.createElement("tbody"),tbody:x,thead:x,tfoot:x,td:S,th:S,"*":h.createElement("div")},E=/complete|loaded|interactive/,_=/^[\w-]*$/,A={},O=A.toString,T={},k=h.createElement("div"),D={tabindex:"tabIndex",readonly:"readOnly",for:"htmlFor",class:"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},R=Array.isArray||function(t){return t instanceof Array};function q(t){return null==t?String(t):A[O.call(t)]||"object"}function I(t){return"function"==q(t)}function P(t){return null!=t&&t==t.window}function N(t){return null!=t&&t.nodeType==t.DOCUMENT_NODE}function $(t){return"object"==q(t)}function L(t){return $(t)&&!P(t)&&Object.getPrototypeOf(t)==Object.prototype}function M(t){var e=!!t&&"length"in t&&t.length,n=i.type(t);return"function"!=n&&!P(t)&&("array"==n||0===e||"number"==typeof e&&e>0&&e-1 in t)}function j(t){return t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function H(t){return t in d?d[t]:d[t]=new RegExp("(^|\\s)"+t+"(\\s|$)")}function F(t,e){return"number"!=typeof e||f[j(t)]?e:e+"px"}function B(t){return"children"in t?l.call(t.children):i.map(t.childNodes,function(t){if(1==t.nodeType)return t})}function U(t,e){var n,i=t?t.length:0;for(n=0;n<i;n++)this[n]=t[n];this.length=i,this.selector=e||""}function V(t,i,r){for(n in i)r&&(L(i[n])||R(i[n]))?(L(i[n])&&!L(t[n])&&(t[n]={}),R(i[n])&&!R(t[n])&&(t[n]=[]),V(t[n],i[n],r)):i[n]!==e&&(t[n]=i[n])}function K(t,e){return null==e?i(t):i(t).filter(e)}function z(t,e,n,i){return I(e)?e.call(t,n,i):e}function W(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function Q(t,n){var i=t.className||"",r=i&&i.baseVal!==e;if(n===e)return r?i.baseVal:i;r?i.baseVal=n:t.className=n}function G(t){try{return t?"true"==t||"false"!=t&&("null"==t?null:+t+""==t?+t:/^[\[\{]/.test(t)?i.parseJSON(t):t):t}catch(e){return t}}function Z(t,e){e(t);for(var n=0,i=t.childNodes.length;n<i;n++)Z(t.childNodes[n],e)}return T.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var n=t.matches||t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return n.call(t,e);var i,r=t.parentNode,s=!r;return s&&(r=k).appendChild(t),i=~T.qsa(r,e).indexOf(t),s&&k.removeChild(t),i},s=function(t){return t.replace(/-+(.)?/g,function(t,e){return e?e.toUpperCase():""})},o=function(t){return c.call(t,function(e,n){return t.indexOf(e)==n})},T.fragment=function(t,n,r){var s,o,a;return g.test(t)&&(s=i(h.createElement(RegExp.$1))),s||(t.replace&&(t=t.replace(v,"<$1></$2>")),n===e&&(n=m.test(t)&&RegExp.$1),n in C||(n="*"),(a=C[n]).innerHTML=""+t,s=i.each(l.call(a.childNodes),function(){a.removeChild(this)})),L(r)&&(o=i(s),i.each(r,function(t,e){w.indexOf(t)>-1?o[t](e):o.attr(t,e)})),s},T.Z=function(t,e){return new U(t,e)},T.isZ=function(t){return t instanceof T.Z},T.init=function(t,n){var r,s;if(!t)return T.Z();if("string"==typeof t)if("<"==(t=t.trim())[0]&&m.test(t))r=T.fragment(t,RegExp.$1,n),t=null;else{if(n!==e)return i(n).find(t);r=T.qsa(h,t)}else{if(I(t))return i(h).ready(t);if(T.isZ(t))return t;if(R(t))s=t,r=c.call(s,function(t){return null!=t});else if($(t))r=[t],t=null;else if(m.test(t))r=T.fragment(t.trim(),RegExp.$1,n),t=null;else{if(n!==e)return i(n).find(t);r=T.qsa(h,t)}}return T.Z(r,t)},(i=function(t,e){return T.init(t,e)}).extend=function(t){var e,n=l.call(arguments,1);return"boolean"==typeof t&&(e=t,t=n.shift()),n.forEach(function(n){V(t,n,e)}),t},T.qsa=function(t,e){var n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,o=_.test(s);return t.getElementById&&o&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType&&11!==t.nodeType?[]:l.call(o&&!i&&t.getElementsByClassName?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},i.contains=h.documentElement.contains?function(t,e){return t!==e&&t.contains(e)}:function(t,e){for(;e&&(e=e.parentNode);)if(e===t)return!0;return!1},i.type=q,i.isFunction=I,i.isWindow=P,i.isArray=R,i.isPlainObject=L,i.isEmptyObject=function(t){var e;for(e in t)return!1;return!0},i.isNumeric=function(t){var e=Number(t),n=typeof t;return null!=t&&"boolean"!=n&&("string"!=n||t.length)&&!isNaN(e)&&isFinite(e)||!1},i.inArray=function(t,e,n){return a.indexOf.call(e,t,n)},i.camelCase=s,i.trim=function(t){return null==t?"":String.prototype.trim.call(t)},i.uuid=0,i.support={},i.expr={},i.noop=function(){},i.map=function(t,e){var n,r,s,o,a=[];if(M(t))for(r=0;r<t.length;r++)null!=(n=e(t[r],r))&&a.push(n);else for(s in t)null!=(n=e(t[s],s))&&a.push(n);return(o=a).length>0?i.fn.concat.apply([],o):o},i.each=function(t,e){var n,i;if(M(t)){for(n=0;n<t.length;n++)if(!1===e.call(t[n],n,t[n]))return t}else for(i in t)if(!1===e.call(t[i],i,t[i]))return t;return t},i.grep=function(t,e){return c.call(t,e)},t.JSON&&(i.parseJSON=JSON.parse),i.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(t,e){A["[object "+e+"]"]=e.toLowerCase()}),i.fn={constructor:T.Z,length:0,forEach:a.forEach,reduce:a.reduce,push:a.push,sort:a.sort,splice:a.splice,indexOf:a.indexOf,concat:function(){var t,e,n=[];for(t=0;t<arguments.length;t++)e=arguments[t],n[t]=T.isZ(e)?e.toArray():e;return u.apply(T.isZ(this)?this.toArray():this,n)},map:function(t){return i(i.map(this,function(e,n){return t.call(e,n,e)}))},slice:function(){return i(l.apply(this,arguments))},ready:function(t){return E.test(h.readyState)&&h.body?t(i):h.addEventListener("DOMContentLoaded",function(){t(i)},!1),this},get:function(t){return t===e?l.call(this):this[t>=0?t:t+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return a.every.call(this,function(e,n){return!1!==t.call(e,n,e)}),this},filter:function(t){return I(t)?this.not(this.not(t)):i(c.call(this,function(e){return T.matches(e,t)}))},add:function(t,e){return i(o(this.concat(i(t,e))))},is:function(t){return this.length>0&&T.matches(this[0],t)},not:function(t){var n=[];if(I(t)&&t.call!==e)this.each(function(e){t.call(this,e)||n.push(this)});else{var r="string"==typeof t?this.filter(t):M(t)&&I(t.item)?l.call(t):i(t);this.forEach(function(t){r.indexOf(t)<0&&n.push(t)})}return i(n)},has:function(t){return this.filter(function(){return $(t)?i.contains(this,t):i(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var t=this[0];return t&&!$(t)?t:i(t)},last:function(){var t=this[this.length-1];return t&&!$(t)?t:i(t)},find:function(t){var e=this;return t?"object"==typeof t?i(t).filter(function(){var t=this;return a.some.call(e,function(e){return i.contains(e,t)})}):1==this.length?i(T.qsa(this[0],t)):this.map(function(){return T.qsa(this,t)}):i()},closest:function(t,e){var n=[],r="object"==typeof t&&i(t);return this.each(function(i,s){for(;s&&!(r?r.indexOf(s)>=0:T.matches(s,t));)s=s!==e&&!N(s)&&s.parentNode;s&&n.indexOf(s)<0&&n.push(s)}),i(n)},parents:function(t){for(var e=[],n=this;n.length>0;)n=i.map(n,function(t){if((t=t.parentNode)&&!N(t)&&e.indexOf(t)<0)return e.push(t),t});return K(e,t)},parent:function(t){return K(o(this.pluck("parentNode")),t)},children:function(t){return K(this.map(function(){return B(this)}),t)},contents:function(){return this.map(function(){return this.contentDocument||l.call(this.childNodes)})},siblings:function(t){return K(this.map(function(t,e){return c.call(B(e.parentNode),function(t){return t!==e})}),t)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(t){return i.map(this,function(e){return e[t]})},show:function(){return this.each(function(){var t,e,n;"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=(t=this.nodeName,p[t]||(e=h.createElement(t),h.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),p[t]=n),p[t]))})},replaceWith:function(t){return this.before(t).remove()},wrap:function(t){var e=I(t);if(this[0]&&!e)var n=i(t).get(0),r=n.parentNode||this.length>1;return this.each(function(s){i(this).wrapAll(e?t.call(this,s):r?n.cloneNode(!0):n)})},wrapAll:function(t){if(this[0]){var e;for(i(this[0]).before(t=i(t));(e=t.children()).length;)t=e.first();i(t).append(this)}return this},wrapInner:function(t){var e=I(t);return this.each(function(n){var r=i(this),s=r.contents(),o=e?t.call(this,n):t;s.length?s.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){i(this).replaceWith(i(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(t){return this.each(function(){var n=i(this);(t===e?"none"==n.css("display"):t)?n.show():n.hide()})},prev:function(t){return i(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return i(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return 0 in arguments?this.each(function(e){var n=this.innerHTML;i(this).empty().append(z(this,t,e,n))}):0 in this?this[0].innerHTML:null},text:function(t){return 0 in arguments?this.each(function(e){var n=z(this,t,e,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(t,i){var r;return"string"!=typeof t||1 in arguments?this.each(function(e){if(1===this.nodeType)if($(t))for(n in t)W(this,n,t[n]);else W(this,t,z(this,i,e,this.getAttribute(t)))}):0 in this&&1==this[0].nodeType&&null!=(r=this[0].getAttribute(t))?r:e},removeAttr:function(t){return this.each(function(){1===this.nodeType&&t.split(" ").forEach(function(t){W(this,t)},this)})},prop:function(t,e){return t=D[t]||t,1 in arguments?this.each(function(n){this[t]=z(this,e,n,this[t])}):this[0]&&this[0][t]},removeProp:function(t){return t=D[t]||t,this.each(function(){delete this[t]})},data:function(t,n){var i="data-"+t.replace(b,"-$1").toLowerCase(),r=1 in arguments?this.attr(i,n):this.attr(i);return null!==r?G(r):e},val:function(t){return 0 in arguments?(null==t&&(t=""),this.each(function(e){this.value=z(this,t,e,this.value)})):this[0]&&(this[0].multiple?i(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(e){if(e)return this.each(function(t){var n=i(this),r=z(this,e,t,n.offset()),s=n.offsetParent().offset(),o={top:r.top-s.top,left:r.left-s.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(h.documentElement!==this[0]&&!i.contains(h.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+t.pageXOffset,top:n.top+t.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(t,e){if(arguments.length<2){var r=this[0];if("string"==typeof t){if(!r)return;return r.style[s(t)]||getComputedStyle(r,"").getPropertyValue(t)}if(R(t)){if(!r)return;var o={},a=getComputedStyle(r,"");return i.each(t,function(t,e){o[e]=r.style[s(e)]||a.getPropertyValue(e)}),o}}var u="";if("string"==q(t))e||0===e?u=j(t)+":"+F(t,e):this.each(function(){this.style.removeProperty(j(t))});else for(n in t)t[n]||0===t[n]?u+=j(n)+":"+F(n,t[n])+";":this.each(function(){this.style.removeProperty(j(n))});return this.each(function(){this.style.cssText+=";"+u})},index:function(t){return t?this.indexOf(i(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return!!t&&a.some.call(this,function(t){return this.test(Q(t))},H(t))},addClass:function(t){return t?this.each(function(e){if("className"in this){r=[];var n=Q(this);z(this,t,e,n).split(/\s+/g).forEach(function(t){i(this).hasClass(t)||r.push(t)},this),r.length&&Q(this,n+(n?" ":"")+r.join(" "))}}):this},removeClass:function(t){return this.each(function(n){if("className"in this){if(t===e)return Q(this,"");r=Q(this),z(this,t,n,r).split(/\s+/g).forEach(function(t){r=r.replace(H(t)," ")}),Q(this,r.trim())}})},toggleClass:function(t,n){return t?this.each(function(r){var s=i(this);z(this,t,r,Q(this)).split(/\s+/g).forEach(function(t){(n===e?!s.hasClass(t):n)?s.addClass(t):s.removeClass(t)})}):this},scrollTop:function(t){if(this.length){var n="scrollTop"in this[0];return t===e?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=t}:function(){this.scrollTo(this.scrollX,t)})}},scrollLeft:function(t){if(this.length){var n="scrollLeft"in this[0];return t===e?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=t}:function(){this.scrollTo(t,this.scrollY)})}},position:function(){if(this.length){var t=this[0],e=this.offsetParent(),n=this.offset(),r=y.test(e[0].nodeName)?{top:0,left:0}:e.offset();return n.top-=parseFloat(i(t).css("margin-top"))||0,n.left-=parseFloat(i(t).css("margin-left"))||0,r.top+=parseFloat(i(e[0]).css("border-top-width"))||0,r.left+=parseFloat(i(e[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var t=this.offsetParent||h.body;t&&!y.test(t.nodeName)&&"static"==i(t).css("position");)t=t.offsetParent;return t})}},i.fn.detach=i.fn.remove,["width","height"].forEach(function(t){var n=t.replace(/./,function(t){return t[0].toUpperCase()});i.fn[t]=function(r){var s,o=this[0];return r===e?P(o)?o["inner"+n]:N(o)?o.documentElement["scroll"+n]:(s=this.offset())&&s[t]:this.each(function(e){(o=i(this)).css(t,z(this,r,e,o[t]()))})}}),["after","prepend","before","append"].forEach(function(n,r){var s=r%2;i.fn[n]=function(){var n,o,a=i.map(arguments,function(t){var r=[];return"array"==(n=q(t))?(t.forEach(function(t){return t.nodeType!==e?r.push(t):i.zepto.isZ(t)?r=r.concat(t.get()):void(r=r.concat(T.fragment(t)))}),r):"object"==n||null==t?t:T.fragment(t)}),u=this.length>1;return a.length<1?this:this.each(function(e,n){o=s?n:n.parentNode,n=0==r?n.nextSibling:1==r?n.firstChild:2==r?n:null;var c=i.contains(h.documentElement,o);a.forEach(function(e){if(u)e=e.cloneNode(!0);else if(!o)return i(e).remove();o.insertBefore(e,n),c&&Z(e,function(e){if(!(null==e.nodeName||"SCRIPT"!==e.nodeName.toUpperCase()||e.type&&"text/javascript"!==e.type||e.src)){var n=e.ownerDocument?e.ownerDocument.defaultView:t;n.eval.call(n,e.innerHTML)}})})})},i.fn[s?n+"To":"insert"+(r?"Before":"After")]=function(t){return i(t)[n](this),this}}),T.Z.prototype=U.prototype=i.fn,T.uniq=o,T.deserializeValue=G,i.zepto=T,i}();return function(e){var n,i=1,r=Array.prototype.slice,s=e.isFunction,o=function(t){return"string"==typeof t},a={},u={},c="onfocusin"in t,l={focus:"focusin",blur:"focusout"},h={mouseenter:"mouseover",mouseleave:"mouseout"};function p(t){return t._zid||(t._zid=i++)}function d(t,e,n,i){if((e=f(e)).ns)var r=(s=e.ns,new RegExp("(?:^| )"+s.replace(" "," .* ?")+"(?: |$)"));var s;return(a[p(t)]||[]).filter(function(t){return t&&(!e.e||t.e==e.e)&&(!e.ns||r.test(t.ns))&&(!n||p(t.fn)===p(n))&&(!i||t.sel==i)})}function f(t){var e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join(" ")}}function m(t,e){return t.del&&!c&&t.e in l||!!e}function g(t){return h[t]||c&&l[t]||t}function v(t,i,r,s,o,u,c){var l=p(t),d=a[l]||(a[l]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return e(document).ready(r);var a=f(i);a.fn=r,a.sel=o,a.e in h&&(r=function(t){var n=t.relatedTarget;if(!n||n!==this&&!e.contains(this,n))return a.fn.apply(this,arguments)}),a.del=u;var l=u||r;a.proxy=function(e){if(!(e=C(e)).isImmediatePropagationStopped()){try{var i=Object.getOwnPropertyDescriptor(e,"data");i&&!i.writable||(e.data=s)}catch(e){}var r=l.apply(t,e._args==n?[e]:[e].concat(e._args));return!1===r&&(e.preventDefault(),e.stopPropagation()),r}},a.i=d.length,d.push(a),"addEventListener"in t&&t.addEventListener(g(a.e),a.proxy,m(a,c))})}function y(t,e,n,i,r){var s=p(t);(e||"").split(/\s/).forEach(function(e){d(t,e,n,i).forEach(function(e){delete a[s][e.i],"removeEventListener"in t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",e.event={add:v,remove:y},e.proxy=function(t,n){var i=2 in arguments&&r.call(arguments,2);if(s(t)){var a=function(){return t.apply(n,i?i.concat(r.call(arguments)):arguments)};return a._zid=p(t),a}if(o(n))return i?(i.unshift(t[n],t),e.proxy.apply(null,i)):e.proxy(t[n],t);throw new TypeError("expected function")},e.fn.bind=function(t,e,n){return this.on(t,e,n)},e.fn.unbind=function(t,e){return this.off(t,e)},e.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var b=function(){return!0},w=function(){return!1},x=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,S={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};function C(t,i){if(i||!t.isDefaultPrevented){i||(i=t),e.each(S,function(e,n){var r=i[e];t[e]=function(){return this[n]=b,r&&r.apply(i,arguments)},t[n]=w});try{t.timeStamp||(t.timeStamp=Date.now())}catch(t){}(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?!1===i.returnValue:i.getPreventDefault&&i.getPreventDefault())&&(t.isDefaultPrevented=b)}return t}function E(t){var e,i={originalEvent:t};for(e in t)x.test(e)||t[e]===n||(i[e]=t[e]);return C(i,t)}e.fn.delegate=function(t,e,n){return this.on(e,t,n)},e.fn.undelegate=function(t,e,n){return this.off(e,t,n)},e.fn.live=function(t,n){return e(document.body).delegate(this.selector,t,n),this},e.fn.die=function(t,n){return e(document.body).undelegate(this.selector,t,n),this},e.fn.on=function(t,i,a,u,c){var l,h,p=this;return t&&!o(t)?(e.each(t,function(t,e){p.on(t,i,a,e,c)}),p):(o(i)||s(u)||!1===u||(u=a,a=i,i=n),u!==n&&!1!==a||(u=a,a=n),!1===u&&(u=w),p.each(function(n,s){c&&(l=function(t){return y(s,t.type,u),u.apply(this,arguments)}),i&&(h=function(t){var n,o=e(t.target).closest(i,s).get(0);if(o&&o!==s)return n=e.extend(E(t),{currentTarget:o,liveFired:s}),(l||u).apply(o,[n].concat(r.call(arguments,1)))}),v(s,t,u,a,i,h||l)}))},e.fn.off=function(t,i,r){var a=this;return t&&!o(t)?(e.each(t,function(t,e){a.off(t,i,e)}),a):(o(i)||s(r)||!1===r||(r=i,i=n),!1===r&&(r=w),a.each(function(){y(this,t,r,i)}))},e.fn.trigger=function(t,n){return(t=o(t)||e.isPlainObject(t)?e.Event(t):C(t))._args=n,this.each(function(){t.type in l&&"function"==typeof this[t.type]?this[t.type]():"dispatchEvent"in this?this.dispatchEvent(t):e(this).triggerHandler(t,n)})},e.fn.triggerHandler=function(t,n){var i,r;return this.each(function(s,a){(i=E(o(t)?e.Event(t):t))._args=n,i.target=a,e.each(d(a,t.type||t),function(t,e){if(r=e.proxy(i),i.isImmediatePropagationStopped())return!1})}),r},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(t){e.fn[t]=function(e){return 0 in arguments?this.bind(t,e):this.trigger(t)}}),e.Event=function(t,e){o(t)||(t=(e=t).type);var n=document.createEvent(u[t]||"Events"),i=!0;if(e)for(var r in e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return n.initEvent(t,i,!0),C(n)}}(i),n=[],i.fn.remove=function(){return this.each(function(){this.parentNode&&("IMG"===this.tagName&&(n.push(this),this.src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",e&&clearTimeout(e),e=setTimeout(function(){n=[]},6e4)),this.parentNode.removeChild(this))})},function(t){var e={},n=t.fn.data,i=t.camelCase,r=t.expando="Zepto"+ +new Date,s=[];function o(n,o,a){var u=n[r]||(n[r]=++t.uuid),c=e[u]||(e[u]=function(e){var n={};return t.each(e.attributes||s,function(e,r){0==r.name.indexOf("data-")&&(n[i(r.name.replace("data-",""))]=t.zepto.deserializeValue(r.value))}),n}(n));return void 0!==o&&(c[i(o)]=a),c}t.fn.data=function(s,a){return void 0===a?t.isPlainObject(s)?this.each(function(e,n){t.each(s,function(t,e){o(n,t,e)})}):0 in this?function(s,a){var u=s[r],c=u&&e[u];if(void 0===a)return c||o(s);if(c){if(a in c)return c[a];var l=i(a);if(l in c)return c[l]}return n.call(t(s),a)}(this[0],s):void 0:this.each(function(){o(this,s,a)})},t.data=function(e,n,i){return t(e).data(n,i)},t.hasData=function(n){var i=n[r],s=i&&e[i];return!!s&&!t.isEmptyObject(s)},t.fn.removeData=function(n){return"string"==typeof n&&(n=n.split(/\s+/)),this.each(function(){var s=this[r],o=s&&e[s];o&&t.each(n||o,function(t){delete o[n?i(this):t]})})},["remove","empty"].forEach(function(e){var n=t.fn[e];t.fn[e]=function(){var t=this.find("*");return"remove"===e&&(t=t.add(this)),t.removeData(),n.call(this)}})}(i),i}(e)},905:function(){!function(t){!function(e){var n="URLSearchParams"in t,i="Symbol"in t&&"iterator"in Symbol,r="FileReader"in t&&"Blob"in t&&function(){try{return new Blob,!0}catch(t){return!1}}(),s="FormData"in t,o="ArrayBuffer"in t;if(o)var a=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],u=ArrayBuffer.isView||function(t){return t&&a.indexOf(Object.prototype.toString.call(t))>-1};function c(t){if("string"!=typeof t&&(t=String(t)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(t))throw new TypeError("Invalid character in header field name");return t.toLowerCase()}function l(t){return"string"!=typeof t&&(t=String(t)),t}function h(t){var e={next:function(){var e=t.shift();return{done:void 0===e,value:e}}};return i&&(e[Symbol.iterator]=function(){return e}),e}function p(t){this.map={},t instanceof p?t.forEach(function(t,e){this.append(e,t)},this):Array.isArray(t)?t.forEach(function(t){this.append(t[0],t[1])},this):t&&Object.getOwnPropertyNames(t).forEach(function(e){this.append(e,t[e])},this)}function d(t){if(t.bodyUsed)return Promise.reject(new TypeError("Already read"));t.bodyUsed=!0}function f(t){return new Promise(function(e,n){t.onload=function(){e(t.result)},t.onerror=function(){n(t.error)}})}function m(t){var e=new FileReader,n=f(e);return e.readAsArrayBuffer(t),n}function g(t){if(t.slice)return t.slice(0);var e=new Uint8Array(t.byteLength);return e.set(new Uint8Array(t)),e.buffer}function v(){return this.bodyUsed=!1,this._initBody=function(t){var e;this._bodyInit=t,t?"string"==typeof t?this._bodyText=t:r&&Blob.prototype.isPrototypeOf(t)?this._bodyBlob=t:s&&FormData.prototype.isPrototypeOf(t)?this._bodyFormData=t:n&&URLSearchParams.prototype.isPrototypeOf(t)?this._bodyText=t.toString():o&&r&&(e=t)&&DataView.prototype.isPrototypeOf(e)?(this._bodyArrayBuffer=g(t.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):o&&(ArrayBuffer.prototype.isPrototypeOf(t)||u(t))?this._bodyArrayBuffer=g(t):this._bodyText=t=Object.prototype.toString.call(t):this._bodyText="",this.headers.get("content-type")||("string"==typeof t?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n&&URLSearchParams.prototype.isPrototypeOf(t)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},r&&(this.blob=function(){var t=d(this);if(t)return t;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?d(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(m)}),this.text=function(){var t,e,n,i=d(this);if(i)return i;if(this._bodyBlob)return t=this._bodyBlob,n=f(e=new FileReader),e.readAsText(t),n;if(this._bodyArrayBuffer)return Promise.resolve(function(t){for(var e=new Uint8Array(t),n=new Array(e.length),i=0;i<e.length;i++)n[i]=String.fromCharCode(e[i]);return n.join("")}(this._bodyArrayBuffer));if(this._bodyFormData)throw new Error("could not read FormData body as text");return Promise.resolve(this._bodyText)},s&&(this.formData=function(){return this.text().then(w)}),this.json=function(){return this.text().then(JSON.parse)},this}p.prototype.append=function(t,e){t=c(t),e=l(e);var n=this.map[t];this.map[t]=n?n+", "+e:e},p.prototype.delete=function(t){delete this.map[c(t)]},p.prototype.get=function(t){return t=c(t),this.has(t)?this.map[t]:null},p.prototype.has=function(t){return this.map.hasOwnProperty(c(t))},p.prototype.set=function(t,e){this.map[c(t)]=l(e)},p.prototype.forEach=function(t,e){for(var n in this.map)this.map.hasOwnProperty(n)&&t.call(e,this.map[n],n,this)},p.prototype.keys=function(){var t=[];return this.forEach(function(e,n){t.push(n)}),h(t)},p.prototype.values=function(){var t=[];return this.forEach(function(e){t.push(e)}),h(t)},p.prototype.entries=function(){var t=[];return this.forEach(function(e,n){t.push([n,e])}),h(t)},i&&(p.prototype[Symbol.iterator]=p.prototype.entries);var y=["DELETE","GET","HEAD","OPTIONS","POST","PUT"];function b(t,e){var n,i,r=(e=e||{}).body;if(t instanceof b){if(t.bodyUsed)throw new TypeError("Already read");this.url=t.url,this.credentials=t.credentials,e.headers||(this.headers=new p(t.headers)),this.method=t.method,this.mode=t.mode,this.signal=t.signal,r||null==t._bodyInit||(r=t._bodyInit,t.bodyUsed=!0)}else this.url=String(t);if(this.credentials=e.credentials||this.credentials||"same-origin",!e.headers&&this.headers||(this.headers=new p(e.headers)),this.method=(i=(n=e.method||this.method||"GET").toUpperCase(),y.indexOf(i)>-1?i:n),this.mode=e.mode||this.mode||null,this.signal=e.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&r)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(r)}function w(t){var e=new FormData;return t.trim().split("&").forEach(function(t){if(t){var n=t.split("="),i=n.shift().replace(/\+/g," "),r=n.join("=").replace(/\+/g," ");e.append(decodeURIComponent(i),decodeURIComponent(r))}}),e}function x(t,e){e||(e={}),this.type="default",this.status=void 0===e.status?200:e.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in e?e.statusText:"OK",this.headers=new p(e.headers),this.url=e.url||"",this._initBody(t)}b.prototype.clone=function(){return new b(this,{body:this._bodyInit})},v.call(b.prototype),v.call(x.prototype),x.prototype.clone=function(){return new x(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new p(this.headers),url:this.url})},x.error=function(){var t=new x(null,{status:0,statusText:""});return t.type="error",t};var S=[301,302,303,307,308];x.redirect=function(t,e){if(-1===S.indexOf(e))throw new RangeError("Invalid status code");return new x(null,{status:e,headers:{location:t}})},e.DOMException=t.DOMException;try{new e.DOMException}catch(t){e.DOMException=function(t,e){this.message=t,this.name=e;var n=Error(t);this.stack=n.stack},e.DOMException.prototype=Object.create(Error.prototype),e.DOMException.prototype.constructor=e.DOMException}function C(t,n){return new Promise(function(i,s){var o=new b(t,n);if(o.signal&&o.signal.aborted)return s(new e.DOMException("Aborted","AbortError"));var a=new XMLHttpRequest;function u(){a.abort()}a.onload=function(){var t,e,n={status:a.status,statusText:a.statusText,headers:(t=a.getAllResponseHeaders()||"",e=new p,t.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(t){var n=t.split(":"),i=n.shift().trim();if(i){var r=n.join(":").trim();e.append(i,r)}}),e)};n.url="responseURL"in a?a.responseURL:n.headers.get("X-Request-URL");var r="response"in a?a.response:a.responseText;i(new x(r,n))},a.onerror=function(){s(new TypeError("Network request failed"))},a.ontimeout=function(){s(new TypeError("Network request failed"))},a.onabort=function(){s(new e.DOMException("Aborted","AbortError"))},a.open(o.method,o.url,!0),"include"===o.credentials?a.withCredentials=!0:"omit"===o.credentials&&(a.withCredentials=!1),"responseType"in a&&r&&(a.responseType="blob"),o.headers.forEach(function(t,e){a.setRequestHeader(e,t)}),o.signal&&(o.signal.addEventListener("abort",u),a.onreadystatechange=function(){4===a.readyState&&o.signal.removeEventListener("abort",u)}),a.send(void 0===o._bodyInit?null:o._bodyInit)})}C.polyfill=!0,t.fetch||(t.fetch=C,t.Headers=p,t.Request=b,t.Response=x),e.Headers=p,e.Request=b,e.Response=x,e.fetch=C,Object.defineProperty(e,"__esModule",{value:!0})}({})}("undefined"!=typeof self?self:this)},624:(t,e,n)=>{"use strict";var i,r,s,o=[n(525),n(785),n(291),n(709),n(506),n(176)],a=-1,u=[],c=!1;function l(){i&&r&&(i=!1,r.length?u=r.concat(u):a=-1,u.length&&h())}function h(){if(!i){c=!1,i=!0;for(var t=u.length,e=setTimeout(l);t;){for(r=u,u=[];r&&++a<t;)r[a].run();a=-1,t=u.length}r=null,a=-1,i=!1,clearTimeout(e)}}for(var p=-1,d=o.length;++p<d;)if(o[p]&&o[p].test&&o[p].test()){s=o[p].install(h);break}function f(t,e){this.fun=t,this.array=e}f.prototype.run=function(){var t=this.fun,e=this.array;switch(e.length){case 0:return t();case 1:return t(e[0]);case 2:return t(e[0],e[1]);case 3:return t(e[0],e[1],e[2]);default:return t.apply(null,e)}},t.exports=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];u.push(new f(t,e)),c||i||(c=!0,s())}},709:(t,e,n)=>{"use strict";e.test=function(){return!n.g.setImmediate&&void 0!==n.g.MessageChannel},e.install=function(t){var e=new n.g.MessageChannel;return e.port1.onmessage=t,function(){e.port2.postMessage(0)}}},291:(t,e,n)=>{"use strict";var i=n.g.MutationObserver||n.g.WebKitMutationObserver;e.test=function(){return i},e.install=function(t){var e=0,r=new i(t),s=n.g.document.createTextNode("");return r.observe(s,{characterData:!0}),function(){s.data=e=++e%2}}},785:(t,e,n)=>{"use strict";e.test=function(){return"function"==typeof n.g.queueMicrotask},e.install=function(t){return function(){n.g.queueMicrotask(t)}}},506:(t,e,n)=>{"use strict";e.test=function(){return"document"in n.g&&"onreadystatechange"in n.g.document.createElement("script")},e.install=function(t){return function(){var e=n.g.document.createElement("script");return e.onreadystatechange=function(){t(),e.onreadystatechange=null,e.parentNode.removeChild(e),e=null},n.g.document.documentElement.appendChild(e),t}}},176:(t,e)=>{"use strict";e.test=function(){return!0},e.install=function(t){return function(){setTimeout(t,0)}}},229:function(t,e,n){!function(t){"use strict";var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])})(t,n)};function n(t,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}var i=function(){return(i=Object.assign||function(t){for(var e,n=1,i=arguments.length;n<i;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)};function r(t,e,n,i){return new(n||(n=Promise))(function(r,s){function o(t){try{u(i.next(t))}catch(t){s(t)}}function a(t){try{u(i.throw(t))}catch(t){s(t)}}function u(t){var e;t.done?r(t.value):(e=t.value,e instanceof n?e:new n(function(t){t(e)})).then(o,a)}u((i=i.apply(t,e||[])).next())})}function s(t,e){var n,i,r,s,o={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(s){return function(a){return function(s){if(n)throw new TypeError("Generator is already executing.");for(;o;)try{if(n=1,i&&(r=2&s[0]?i.return:s[0]?i.throw||((r=i.return)&&r.call(i),0):i.next)&&!(r=r.call(i,s[1])).done)return r;switch(i=0,r&&(s=[2&s[0],r.value]),s[0]){case 0:case 1:r=s;break;case 4:return o.label++,{value:s[1],done:!1};case 5:o.label++,i=s[1],s=[0];continue;case 7:s=o.ops.pop(),o.trys.pop();continue;default:if(!((r=(r=o.trys).length>0&&r[r.length-1])||6!==s[0]&&2!==s[0])){o=0;continue}if(3===s[0]&&(!r||s[1]>r[0]&&s[1]<r[3])){o.label=s[1];break}if(6===s[0]&&o.label<r[1]){o.label=r[1],r=s;break}if(r&&o.label<r[2]){o.label=r[2],o.ops.push(s);break}r[2]&&o.ops.pop(),o.trys.pop();continue}s=e.call(t,o)}catch(t){s=[6,t],i=0}finally{n=r=0}if(5&s[0])throw s[1];return{value:s[0]?s[1]:void 0,done:!0}}([s,a])}}}var o=function(t){function e(n,i,r,s){var o,a,u,c=t.call(this,n)||this;return c.name="MeiliSearchCommunicationError",c.type="MeiliSearchCommunicationError",i instanceof Response&&(c.message=i.statusText,c.statusCode=i.status),i instanceof Error&&(c.errno=i.errno,c.code=i.code),s?(c.stack=s,c.stack=null===(o=c.stack)||void 0===o?void 0:o.replace(/(TypeError|FetchError)/,c.name),c.stack=null===(a=c.stack)||void 0===a?void 0:a.replace("Failed to fetch","request to "+r+" failed, reason: connect ECONNREFUSED"),c.stack=null===(u=c.stack)||void 0===u?void 0:u.replace("Not Found","Not Found: "+r)):Error.captureStackTrace&&Error.captureStackTrace(c,e),c}return n(e,t),e}(Error),a=function(t){function e(e,n){var i=t.call(this,e.message)||this;return i.name="MeiliSearchApiError",i.code=e.code,i.type=e.type,i.link=e.link,i.message=e.message,i.httpStatus=n,Object.setPrototypeOf(i,a.prototype),Error.captureStackTrace&&Error.captureStackTrace(i,a),i}return n(e,t),e}(Error);function u(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:if(t.ok)return[3,5];e=void 0,n.label=1;case 1:return n.trys.push([1,3,,4]),[4,t.json()];case 2:return e=n.sent(),[3,4];case 3:throw n.sent(),new o(t.statusText,t,t.url);case 4:throw new a(e,t.status);case 5:return[2,t]}})})}function c(t,e,n){if("MeiliSearchApiError"!==t.type)throw new o(t.message,t,n,e);throw t}var l=function(t){function e(n){var i=t.call(this,n)||this;return i.name="MeiliSearchError",i.type="MeiliSearchError",Error.captureStackTrace&&Error.captureStackTrace(i,e),i}return n(e,t),e}(Error),h=function(t){function e(n){var i=t.call(this,n)||this;return i.name="MeiliSearchTimeOutError",i.type=i.constructor.name,Error.captureStackTrace&&Error.captureStackTrace(i,e),i}return n(e,t),e}(Error),p=function(){function t(t){this.headers=i(i(i({},t.headers||{}),{"Content-Type":"application/json"}),t.apiKey?{"X-Meili-API-Key":t.apiKey}:{}),this.url=new URL(t.host)}return t.addTrailingSlash=function(t){return t.endsWith("/")||(t+="/"),t},t.prototype.request=function(t){var e=t.method,n=t.url,o=t.params,a=t.body,l=t.config;return r(this,void 0,void 0,function(){var t,r,h,p,d;return s(this,function(s){switch(s.label){case 0:t=new URL(n,this.url),o&&(r=new URLSearchParams,Object.keys(o).filter(function(t){return null!==o[t]}).map(function(t){return r.set(t,o[t])}),t.search=r.toString()),s.label=1;case 1:return s.trys.push([1,4,,5]),[4,fetch(t.toString(),i(i({},l),{method:e,body:JSON.stringify(a),headers:this.headers})).then(function(t){return u(t)})];case 2:return[4,s.sent().text()];case 3:h=s.sent();try{return[2,JSON.parse(h)]}catch(t){return[2]}return[3,5];case 4:return p=s.sent(),d=p.stack,c(p,d,t.toString()),[3,5];case 5:return[2]}})})},t.prototype.get=function(t,e,n){return r(this,void 0,void 0,function(){return s(this,function(i){switch(i.label){case 0:return[4,this.request({method:"GET",url:t,params:e,config:n})];case 1:return[2,i.sent()]}})})},t.prototype.post=function(t,e,n,i){return r(this,void 0,void 0,function(){return s(this,function(r){switch(r.label){case 0:return[4,this.request({method:"POST",url:t,body:e,params:n,config:i})];case 1:return[2,r.sent()]}})})},t.prototype.put=function(t,e,n,i){return r(this,void 0,void 0,function(){return s(this,function(r){switch(r.label){case 0:return[4,this.request({method:"PUT",url:t,body:e,params:n,config:i})];case 1:return[2,r.sent()]}})})},t.prototype.delete=function(t,e,n,i){return r(this,void 0,void 0,function(){return s(this,function(r){switch(r.label){case 0:return[4,this.request({method:"DELETE",url:t,body:e,params:n,config:i})];case 1:return[2,r.sent()]}})})},t}();function d(t){return Object.entries(t).reduce(function(t,e){var n=e[0],i=e[1];return void 0!==i&&(t[n]=i),t},{})}function f(t){return r(this,void 0,void 0,function(){return s(this,function(e){switch(e.label){case 0:return[4,new Promise(function(e){return setTimeout(e,t)})];case 1:return[2,e.sent()]}})})}function m(t){return t.startsWith("https://")||t.startsWith("http://")?t:"http://"+t}var g=function(){function t(t,e,n){this.uid=e,this.primaryKey=n,this.httpRequest=new p(t)}return t.prototype.waitForPendingUpdate=function(t,e){var n=void 0===e?{}:e,i=n.timeOutMs,o=void 0===i?5e3:i,a=n.intervalMs,u=void 0===a?50:a;return r(this,void 0,void 0,function(){var e,n;return s(this,function(i){switch(i.label){case 0:e=Date.now(),i.label=1;case 1:return Date.now()-e<o?[4,this.getUpdateStatus(t)]:[3,4];case 2:return n=i.sent(),["enqueued","processing"].includes(n.status)?[4,f(u)]:[2,n];case 3:return i.sent(),[3,1];case 4:throw new h("timeout of "+o+"ms has exceeded on process "+t+" when waiting for pending update to resolve.")}})})},t.prototype.search=function(t,e,n){return r(this,void 0,void 0,function(){var r;return s(this,function(s){switch(s.label){case 0:return r="indexes/"+this.uid+"/search",[4,this.httpRequest.post(r,d(i(i({},e),{q:t})),void 0,n)];case 1:return[2,s.sent()]}})})},t.prototype.searchGet=function(t,e,n){return r(this,void 0,void 0,function(){var r,o,a;return s(this,function(s){switch(s.label){case 0:return r="indexes/"+this.uid+"/search",o=function(t){if("string"==typeof t)return t;if(Array.isArray(t))throw new l("The filter query parameter should be in string format when using searchGet")},a=i(i({q:t},e),{filter:o(null==e?void 0:e.filter),sort:(null==e?void 0:e.sort)?e.sort.join(","):void 0,facetsDistribution:(null==e?void 0:e.facetsDistribution)?e.facetsDistribution.join(","):void 0,attributesToRetrieve:(null==e?void 0:e.attributesToRetrieve)?e.attributesToRetrieve.join(","):void 0,attributesToCrop:(null==e?void 0:e.attributesToCrop)?e.attributesToCrop.join(","):void 0,attributesToHighlight:(null==e?void 0:e.attributesToHighlight)?e.attributesToHighlight.join(","):void 0}),[4,this.httpRequest.get(r,d(a),n)];case 1:return[2,s.sent()]}})})},t.prototype.getRawInfo=function(){return r(this,void 0,void 0,function(){var t,e;return s(this,function(n){switch(n.label){case 0:return t="indexes/"+this.uid,[4,this.httpRequest.get(t)];case 1:return e=n.sent(),this.primaryKey=e.primaryKey,[2,e]}})})},t.prototype.fetchInfo=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return[4,this.getRawInfo()];case 1:return t.sent(),[2,this]}})})},t.prototype.fetchPrimaryKey=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t=this,[4,this.getRawInfo()];case 1:return t.primaryKey=e.sent().primaryKey,[2,this.primaryKey]}})})},t.create=function(e,n,o){return void 0===n&&(n={}),r(this,void 0,void 0,function(){var r;return s(this,function(s){switch(s.label){case 0:return"indexes",[4,new p(o).post("indexes",i(i({},n),{uid:e}))];case 1:return r=s.sent(),[2,new t(o,e,r.primaryKey)]}})})},t.prototype.update=function(t){return r(this,void 0,void 0,function(){var e,n;return s(this,function(i){switch(i.label){case 0:return e="indexes/"+this.uid,[4,this.httpRequest.put(e,t)];case 1:return n=i.sent(),this.primaryKey=n.primaryKey,[2,this]}})})},t.prototype.delete=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid,[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.deleteIfExists=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return e.trys.push([0,2,,3]),[4,this.delete()];case 1:return e.sent(),[2,!0];case 2:if("index_not_found"===(t=e.sent()).code)return[2,!1];throw t;case 3:return[2]}})})},t.prototype.getAllUpdateStatus=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/updates",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.getUpdateStatus=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/updates/"+t,[4,this.httpRequest.get(e)];case 1:return[2,n.sent()]}})})},t.prototype.getStats=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/stats",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.getDocuments=function(t){return r(this,void 0,void 0,function(){var e,n;return s(this,function(r){switch(r.label){case 0:return e="indexes/"+this.uid+"/documents",void 0!==t&&Array.isArray(t.attributesToRetrieve)&&(n=t.attributesToRetrieve.join(",")),[4,this.httpRequest.get(e,i(i({},t),void 0!==n?{attributesToRetrieve:n}:{}))];case 1:return[2,r.sent()]}})})},t.prototype.getDocument=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/documents/"+t,[4,this.httpRequest.get(e)];case 1:return[2,n.sent()]}})})},t.prototype.addDocuments=function(t,e){return r(this,void 0,void 0,function(){var n;return s(this,function(i){switch(i.label){case 0:return n="indexes/"+this.uid+"/documents",[4,this.httpRequest.post(n,t,e)];case 1:return[2,i.sent()]}})})},t.prototype.addDocumentsInBatches=function(t,e,n){return void 0===e&&(e=1e3),r(this,void 0,void 0,function(){var i,r,o,a;return s(this,function(s){switch(s.label){case 0:i=[],r=0,s.label=1;case 1:return r<t.length?(a=(o=i).push,[4,this.addDocuments(t.slice(r,r+e),n)]):[3,4];case 2:a.apply(o,[s.sent()]),s.label=3;case 3:return r+=e,[3,1];case 4:return[2,i]}})})},t.prototype.updateDocuments=function(t,e){return r(this,void 0,void 0,function(){var n;return s(this,function(i){switch(i.label){case 0:return n="indexes/"+this.uid+"/documents",[4,this.httpRequest.put(n,t,e)];case 1:return[2,i.sent()]}})})},t.prototype.updateDocumentsInBatches=function(t,e,n){return void 0===e&&(e=1e3),r(this,void 0,void 0,function(){var i,r,o,a;return s(this,function(s){switch(s.label){case 0:i=[],r=0,s.label=1;case 1:return r<t.length?(a=(o=i).push,[4,this.updateDocuments(t.slice(r,r+e),n)]):[3,4];case 2:a.apply(o,[s.sent()]),s.label=3;case 3:return r+=e,[3,1];case 4:return[2,i]}})})},t.prototype.deleteDocument=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/documents/"+t,[4,this.httpRequest.delete(e)];case 1:return[2,n.sent()]}})})},t.prototype.deleteDocuments=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/documents/delete-batch",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.deleteAllDocuments=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/documents",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getSettings=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateSettings=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetSettings=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getSynonyms=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/synonyms",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateSynonyms=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/synonyms",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetSynonyms=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/synonyms",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getStopWords=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/stop-words",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateStopWords=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/stop-words",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetStopWords=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/stop-words",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getRankingRules=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/ranking-rules",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateRankingRules=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/ranking-rules",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetRankingRules=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/ranking-rules",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getDistinctAttribute=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/distinct-attribute",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateDistinctAttribute=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/distinct-attribute",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetDistinctAttribute=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/distinct-attribute",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getFilterableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/filterable-attributes",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateFilterableAttributes=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/filterable-attributes",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetFilterableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/filterable-attributes",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getSortableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/sortable-attributes",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateSortableAttributes=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/sortable-attributes",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetSortableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/sortable-attributes",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getSearchableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/searchable-attributes",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateSearchableAttributes=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/searchable-attributes",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetSearchableAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/searchable-attributes",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t.prototype.getDisplayedAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/displayed-attributes",[4,this.httpRequest.get(t)];case 1:return[2,e.sent()]}})})},t.prototype.updateDisplayedAttributes=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="indexes/"+this.uid+"/settings/displayed-attributes",[4,this.httpRequest.post(e,t)];case 1:return[2,n.sent()]}})})},t.prototype.resetDisplayedAttributes=function(){return r(this,void 0,void 0,function(){var t;return s(this,function(e){switch(e.label){case 0:return t="indexes/"+this.uid+"/settings/displayed-attributes",[4,this.httpRequest.delete(t)];case 1:return[2,e.sent()]}})})},t}(),v=function(){function t(t){t.host=m(t.host),t.host=p.addTrailingSlash(t.host),this.config=t,this.httpRequest=new p(t)}return t.prototype.index=function(t){return new g(this.config,t)},t.prototype.getIndex=function(t){return r(this,void 0,void 0,function(){return s(this,function(e){return[2,new g(this.config,t).fetchInfo()]})})},t.prototype.getRawIndex=function(t){return r(this,void 0,void 0,function(){return s(this,function(e){return[2,new g(this.config,t).getRawInfo()]})})},t.prototype.getOrCreateIndex=function(t,e){return void 0===e&&(e={}),r(this,void 0,void 0,function(){var n;return s(this,function(i){switch(i.label){case 0:return i.trys.push([0,2,,3]),[4,this.getIndex(t)];case 1:return[2,i.sent()];case 2:if("index_not_found"===(n=i.sent()).code)return[2,this.createIndex(t,e)];throw n;case 3:return[2]}})})},t.prototype.getIndexes=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"indexes",[4,this.httpRequest.get("indexes")];case 1:return[2,t.sent()]}})})},t.prototype.createIndex=function(t,e){return void 0===e&&(e={}),r(this,void 0,void 0,function(){return s(this,function(n){switch(n.label){case 0:return[4,g.create(t,e,this.config)];case 1:return[2,n.sent()]}})})},t.prototype.updateIndex=function(t,e){return void 0===e&&(e={}),r(this,void 0,void 0,function(){return s(this,function(n){return[2,new g(this.config,t).update(e)]})})},t.prototype.deleteIndex=function(t){return r(this,void 0,void 0,function(){return s(this,function(e){return[2,new g(this.config,t).delete()]})})},t.prototype.deleteIndexIfExists=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,this.deleteIndex(t)];case 1:return n.sent(),[2,!0];case 2:if("index_not_found"===(e=n.sent()).code)return[2,!1];throw e;case 3:return[2]}})})},t.prototype.getKeys=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"keys",[4,this.httpRequest.get("keys")];case 1:return[2,t.sent()]}})})},t.prototype.health=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"health",[4,this.httpRequest.get("health")];case 1:return[2,t.sent()]}})})},t.prototype.isHealthy=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return t.trys.push([0,2,,3]),"health",[4,this.httpRequest.get("health")];case 1:return t.sent(),[2,!0];case 2:return t.sent(),[2,!1];case 3:return[2]}})})},t.prototype.getStats=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"stats",[4,this.httpRequest.get("stats")];case 1:return[2,t.sent()]}})})},t.prototype.getVersion=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"version",[4,this.httpRequest.get("version")];case 1:return[2,t.sent()]}})})},t.prototype.createDump=function(){return r(this,void 0,void 0,function(){return s(this,function(t){switch(t.label){case 0:return"dumps",[4,this.httpRequest.post("dumps")];case 1:return[2,t.sent()]}})})},t.prototype.getDumpStatus=function(t){return r(this,void 0,void 0,function(){var e;return s(this,function(n){switch(n.label){case 0:return e="dumps/"+t+"/status",[4,this.httpRequest.get(e)];case 1:return[2,n.sent()]}})})},t}();t.HttpRequests=p,t.Index=g,t.MeiliSearch=v,t.MeiliSearchApiError=a,t.MeiliSearchCommunicationError=o,t.MeiliSearchError=l,t.MeiliSearchTimeOutError=h,t.addProtocolIfNotPresent=m,t.default=v,t.httpErrorHandler=c,t.httpResponseErrorHandler=u,t.removeUndefinedFromObject=d,t.sleep=f,Object.defineProperty(t,"__esModule",{value:!0})}(e,n(905))},547:t=>{"use strict";var e=Function.prototype.bind;t.exports=function(t){var n=function(){for(var n=arguments.length,i=Array(n),r=0;r<n;r++)i[r]=arguments[r];return new(e.apply(t,[null].concat(i)))};return n.__proto__=t,n.prototype=t.prototype,n}},525:()=>{}},e={};function n(i){var r=e[i];if(void 0!==r)return r.exports;var s=e[i]={exports:{}};return t[i].call(s.exports,s,s.exports,n),s.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var i in e)n.o(e,i)&&!n.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var i={};return(()=>{"use strict";n.d(i,{default:()=>S});var t=n(547),e=n.n(t),r=n(639),s=n.n(r),o="docs-searchbar",a="".concat(o,"-suggestion"),u="".concat(o,"-footer");const c={suggestion:function(t){return'\n  <a class="'.concat(a,"\n    ").concat(t.isCategoryHeader?"".concat(a,"__main"):"","\n    ").concat(t.isSubCategoryHeader?"".concat(a,"__secondary"):"",'\n    "\n    aria-label="Link to the result"\n    href="').concat(t.url,'"\n    >\n    <div class="').concat(a,'--category-header">\n      <span class="').concat(a,'--category-header-lvl0">\n        ').concat(t.category,'\n      </span>\n    </div>\n    <div class="').concat(a,'--wrapper">\n      <div class="').concat(a,'--subcategory-column">\n        <span class="').concat(a,'--subcategory-column-text">\n          ').concat(t.subcategory,"\n        </span>\n      </div>\n      ").concat(t.isTextOrSubcategoryNonEmpty?'\n        <div class="'.concat(a,'--content">\n          <div class="').concat(a,'--subcategory-inline">\n            ').concat(t.subcategory,'\n          </div>\n          <div class="').concat(a,'--title">').concat(t.title,"</div>\n          ").concat(t.text?'<div class="'.concat(a,'--text">').concat(t.text,"</div>"):"","\n        </div>"):"","\n    </div>\n  </a>\n  ")},suggestionSimple:function(t){return'\n  <div class="'.concat(a,"\n    ").concat(t.isCategoryHeader?"".concat(a,"__main"):"","\n    ").concat(t.isSubCategoryHeader?"".concat(a,"__secondary"):"",'\n    suggestion-layout-simple\n  ">\n    <div class="').concat(a,'--category-header">\n        ').concat(t.isLvl0?"":'\n            <span class="'.concat(a,"--category-header-lvl0 ").concat(a,'--category-header-item">\n                ').concat(t.category,"\n            </span>\n            ").concat(t.isLvl1||t.isLvl1EmptyOrDuplicate?"":'\n            <span class="'.concat(a,"--category-header-lvl1 ").concat(a,'--category-header-item">\n                ').concat(t.subcategory,"\n            </span>")),'\n        <div class="').concat(a,"--title ").concat(a,'--category-header-item">\n            ').concat(t.isLvl2?t.title:"","\n            ").concat(t.isLvl1?t.subcategory:"","\n            ").concat(t.isLvl0?t.category:"",'\n        </div>\n    </div>\n    <div class="').concat(a,'--wrapper">\n      ').concat(t.text?'\n      <div class="'.concat(a,'--content">\n        <div class="').concat(a,'--text">').concat(t.text,"</div>\n      </div>"):"","\n    </div>\n  </div>\n  ")},footer:'\n    <div class="'.concat(u,'">\n      Powered by\n      <a href="https://www.meilisearch.com" target="_blank">\n        <img src="https://res.cloudinary.com/meilisearch/image/upload/v1582710599/Logo_fumols.svg" height="20" width="97" class="').concat(u,'-logo"/>\n       </a>\n    </div>\n  '),empty:function(t){return'\n  <div class="'.concat(a,'">\n    <div class="').concat(a,'--wrapper">\n        <div class="').concat(a,"--content ").concat(a,'--no-results">\n            <div class="').concat(a,'--title">\n                <div class="').concat(a,'--text">\n                No results found for query <b>"').concat(t.query,'"</b>\n                </div>\n            </div>\n        </div>\n    </div>\n  </div>\n  ')},searchBox:'\n  <form novalidate="novalidate" onsubmit="return false;" class="searchbox">\n    <div role="search" class="searchbox__wrapper">\n      <input id="'.concat(a,'" type="search" name="search" placeholder="Search the docs" autocomplete="off" required="required" class="searchbox__input"/>\n      <button type="submit" title="Submit your search query." class="searchbox__submit" >\n        <svg width=12 height=12 role="img" aria-label="Search">\n          <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>\n        </svg>\n      </button>\n      <button type="reset" title="Clear the search query." class="searchbox__reset hide">\n        <svg width=12 height=12 role="img" aria-label="Reset">\n          <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>\n        </svg>\n      </button>\n    </div>\n</form>\n\n<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">\n  <svg xmlns="http://www.w3.org/2000/svg">\n    <symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"></symbol>\n    <symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"></symbol>\n  </svg>\n</div>\n  ')};var l=n(939);const h=n.n(l)();function p(t){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}const d={mergeKeyWithParent:function(t,e){if(void 0===t[e])return t;if("object"!==p(t[e]))return t;var n=h.extend({},t,t[e]);return delete n[e],n},renameKeysWithLevels:function(t,e){return Object.keys(t).reduce(function(n,i){var r=n;return i.startsWith(e)?r[i.substring(i.indexOf("lvl"))]=t[i]:r[i]=t[i],r},{})},replaceNullString:function(t){return Object.keys(t).reduce(function(e,n){var i=e;return"string"==typeof t[n]&&"null"===t[n]?i[n]=null:i[n]=t[n],i},{})},groupBy:function(t,e){var n={};return h.each(t,function(t,i){if(void 0===i[e])throw new Error("[groupBy]: Object has no key ".concat(e));var r=i[e];"string"==typeof r&&(r=r.toLowerCase()),Object.prototype.hasOwnProperty.call(n,r)||(n[r]=[]),n[r].push(i)}),n},values:function(t){return Object.keys(t).map(function(e){return t[e]})},flatten:function(t){var e=[];return t.forEach(function(t){Array.isArray(t)?t.forEach(function(t){e.push(t)}):e.push(t)}),e},flattenAndFlagFirst:function(t,e){var n=this.values(t).map(function(t){return t.map(function(t,n){return t[e]=0===n,t})});return this.flatten(n)},compact:function(t){var e=[];return t.forEach(function(t){t&&e.push(t)}),e},getHighlightedValue:function(t,e){return t._formatted&&t._formatted[e]&&"string"==typeof t._formatted[e]?this.replaceHtmlTagsToHighlight(t._formatted[e]):t[e]},replaceHtmlTagsToHighlight:function(t){return t.replace(/<em>/g,'<span class="docs-searchbar-suggestion--highlight">').replace(/<\/em>/g,"</span>")},getSnippetedValue:function(t,e){if(!t._formatted||!t._formatted[e]||"string"!=typeof t._formatted[e])return t[e];var n=this.replaceHtmlTagsToHighlight(t._formatted[e]);return n[0]!==n[0].toUpperCase()&&(n="…".concat(n)),-1===[".","!","?"].indexOf(n[n.length-1])&&(n="".concat(n,"…")),n},deepClone:function(t){return JSON.parse(JSON.stringify(t))}};var f=n(229);function m(t){return(m="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function g(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);e&&(i=i.filter(function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable})),n.push.apply(n,i)}return n}function v(t){for(var e=1;e<arguments.length;e++){var n=null!=arguments[e]?arguments[e]:{};e%2?g(Object(n),!0).forEach(function(e){y(t,e,n[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(n)):g(Object(n)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(n,e))})}return t}function y(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function b(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}const w=function(){function t(e){var n=e.hostUrl,i=e.apiKey,r=e.indexUid,o=e.inputSelector,a=e.debug,u=void 0!==a&&a,l=e.meilisearchOptions,p=void 0===l?{}:l,d=e.queryDataCallback,m=void 0===d?null:d,g=e.autocompleteOptions,y=void 0===g?{}:g,b=e.transformData,w=void 0!==b&&b,x=e.queryHook,S=void 0!==x&&x,C=e.handleSelected,E=void 0!==C&&C,_=e.enhancedSearchInput,A=void 0!==_&&_,O=e.layout,T=void 0===O?"columns":O,k=e.enableDarkMode,D=void 0!==k&&k;!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),t.checkArguments({hostUrl:n,apiKey:i,indexUid:r,inputSelector:o,debug:u,meilisearchOptions:p,queryDataCallback:m,autocompleteOptions:y,transformData:w,queryHook:S,handleSelected:E,enhancedSearchInput:A,layout:T,enableDarkMode:D}),this.apiKey=i,this.hostUrl=n,this.indexUid=r,this.input=t.getInputFromSelector(o),this.meilisearchOptions=v({limit:5,attributesToHighlight:["*"],attributesToCrop:["content"],cropLength:30},p),this.queryDataCallback=m||null,this.autocompleteOptions=v({debug:u,hint:!1,autoselect:!0},y);var R=this.input&&"function"==typeof this.input.attr&&this.input.attr("aria-label");this.autocompleteOptions.ariaLabel=this.autocompleteOptions.ariaLabel||R||"search input",this.autocompleteOptions.cssClasses=this.autocompleteOptions.cssClasses||{},this.autocompleteOptions.cssClasses.prefix=this.autocompleteOptions.cssClasses.prefix||"dsb",this.autocompleteOptions.cssClasses.root=this.autocompleteOptions.cssClasses.root||"meilisearch-autocomplete",this.autocompleteOptions.keyboardShortcuts=this.parseHotkeysAutocompleteOptions(this.autocompleteOptions.keyboardShortcuts)||["s",191],this.isSimpleLayout="simple"===T,this.enableDarkMode=D,this.client=new f.MeiliSearch({host:n,apiKey:this.apiKey}),t.addThemeWrapper(o,this.enableDarkMode),A&&(this.input=t.injectSearchBox(this.input)),this.autocomplete=s()(this.input,this.autocompleteOptions,[{source:this.getAutocompleteSource(w,S),templates:{suggestion:t.getSuggestionTemplate(this.isSimpleLayout),footer:c.footer,empty:t.getEmptyTemplate()}}]),h(".".concat(this.autocompleteOptions.cssClasses.root," > [role='listbox']")).css({left:!1,right:!1});var q=E;this.handleSelected=q||this.handleSelected,q&&h(".meilisearch-autocomplete").on("click",".".concat(this.autocompleteOptions.cssClasses.prefix,"-suggestions a"),function(t){t.preventDefault()}),this.autocomplete.on("autocomplete:selected",this.handleSelected.bind(null,this.autocomplete.autocomplete)),this.autocomplete.on("autocomplete:shown",this.handleShown.bind(null,this.input)),A&&t.bindSearchBoxEvent()}var e,n,i;return e=t,i=[{key:"addThemeWrapper",value:function(t,e){var n=document.querySelector(t),i=n.parentNode,r=document.createElement("div");r.className+="docs-searchbar-js",i.replaceChild(r,n),r.appendChild(n);var s=Boolean(e);if("auto"===e&&window.matchMedia){var o=window.matchMedia("(prefers-color-scheme: dark)");s=o.matches;var a=function t(e){document.body.contains(r)?r.setAttribute("data-ds-theme",e.matches?"dark":"light"):o.removeEventListener?o.removeEventListener("change",t):o.removeListener&&o.removeListener(t)};o.addEventListener?o.addEventListener("change",a):o.addListener&&o.addListener(a)}r.setAttribute("data-ds-theme",s?"dark":"light")}},{key:"checkArguments",value:function(e){if(!e.inputSelector||!e.indexUid||!e.hostUrl)throw new Error("Usage:\n  documentationSearch({\n  hostUrl,\n  apiKey,\n  indexUid,\n  inputSelector,\n  [ debug ],\n  [ meilisearchOptions ],\n  [ queryDataCallback ],\n  [ autocompleteOptions ],\n  [ transformData ],\n  [ queryHook ],\n  [ handleSelected ],\n  [ enhancedSearchInput ],\n  [ layout ],\n  [ enableDarkMode ]\n})");if("string"!=typeof e.inputSelector)throw new Error("Error: inputSelector:".concat(e.inputSelector,"  must be a string. Each selector must match only one element and separated by ','"));if(!t.getInputFromSelector(e.inputSelector))throw new Error("Error: No input element in the page matches ".concat(e.inputSelector));if(t.typeCheck(e,["meilisearchOptions","autocompleteOptions"],"object",!0),"auto"!==e.enableDarkMode&&!1!==e.enableDarkMode&&!0!==e.enableDarkMode)throw new Error("Error: \"enableDarkMode\" must be either true, false, or 'auto'. Supplied value: ".concat(e.enableDarkMode));if(t.typeCheck(e,["debug","enhancedSearchInput"],"boolean",!1),t.typeCheck(e,["queryDataCallback","transformData","queryHook","handleSelected"],"function",!0),e.layout&&!["simple","columns"].includes(e.layout))throw new Error("Error: \"layout\" must be either 'columns' or 'simple'. Supplied value: ".concat(e.layout))}},{key:"typeCheck",value:function(t,e,n,i){e.filter(function(e){return!i||t[e]}).forEach(function(e){var i=t[e];if(m(t[e])!==n)throw new Error('Error: "'.concat(e,'" must be of type: ').concat(n,". Found type: ").concat(m(i)))})}},{key:"injectSearchBox",value:function(t){t.before(c.searchBox);var e=t.prev().prev().find("input");return t.remove(),e}},{key:"bindSearchBoxEvent",value:function(){h('.searchbox [type="reset"]').on("click",function(){h("input#docs-searchbar").focus(),h(this).addClass("hide"),s().autocomplete.setVal("")}),h("input#docs-searchbar").on("keyup",function(){var t=document.querySelector("input#docs-searchbar"),e=document.querySelector('.searchbox [type="reset"]');e.className="searchbox__reset",0===t.value.length&&(e.className+=" hide")})}},{key:"getInputFromSelector",value:function(t){var e=h(t).filter("input");return e.length?h(e[0]):null}},{key:"formatHits",value:function(e){var n=d.deepClone(e).map(function(t){if(t._formatted){var e=d.replaceNullString(t._formatted);t._formatted=d.renameKeysWithLevels(e,"hierarchy_")}var n=d.replaceNullString(t);return d.renameKeysWithLevels(n,"hierarchy_")}),i=d.groupBy(n,"lvl0");return h.each(i,function(t,e){var n=d.groupBy(e,"lvl1"),r=d.flattenAndFlagFirst(n,"isSubCategoryHeader");i[t]=r}),(i=d.flattenAndFlagFirst(i,"isCategoryHeader")).map(function(e){var n=t.formatURL(e),i=d.getHighlightedValue(e,"lvl0"),r=d.getHighlightedValue(e,"lvl1")||i,s=d.compact([d.getHighlightedValue(e,"lvl2")||r,d.getHighlightedValue(e,"lvl3"),d.getHighlightedValue(e,"lvl4"),d.getHighlightedValue(e,"lvl5"),d.getHighlightedValue(e,"lvl6")]).join('<span class="aa-suggestion-title-separator" aria-hidden="true"> › </span>'),o=d.getSnippetedValue(e,"content"),a=r&&""!==r||s&&""!==s,u=s&&""!==s&&s!==r,c=!u&&r&&""!==r&&r!==i;return{isLvl0:!c&&!u,isLvl1:c,isLvl2:u,isLvl1EmptyOrDuplicate:!r||""===r||r===i,isCategoryHeader:e.isCategoryHeader,isSubCategoryHeader:e.isSubCategoryHeader,isTextOrSubcategoryNonEmpty:a,category:i,subcategory:r,title:s,text:o,url:n}})}},{key:"formatURL",value:function(t){var e=t.url,n=t.anchor;return e?-1!==e.indexOf("#")?e:n?"".concat(t.url,"#").concat(t.anchor):e:n?"#".concat(t.anchor):(console.warn("no anchor nor url for : ",JSON.stringify(t)),null)}},{key:"getEmptyTemplate",value:function(){return c.empty}},{key:"getSuggestionTemplate",value:function(t){return t?c.suggestionSimple:c.suggestion}}],(n=[{key:"getAutocompleteSource",value:function(e,n){var i=this;return function(r,s){n&&(r=n(r)||r),i.client.index(i.indexUid).search(r,i.meilisearchOptions).then(function(n){i.queryDataCallback&&"function"==typeof i.queryDataCallback&&i.queryDataCallback(n);var r=n.hits;e&&(r=e(r)||r),s(t.formatHits(r))})}}},{key:"handleSelected",value:function(t,e,n,i){var r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};"click"!==r.selectionMethod&&(t.setVal(""),window.location.assign(n.url))}},{key:"handleShown",value:function(t){var e=t.offset().left+t.width()/2,n=h(document).width()/2;isNaN(n)&&(n=900);var i=e-n>=0?"meilisearch-autocomplete-right":"meilisearch-autocomplete-left",r=e-n<0?"meilisearch-autocomplete-right":"meilisearch-autocomplete-left",s=h(".meilisearch-autocomplete");s.hasClass(i)||s.addClass(i),s.hasClass(r)&&s.removeClass(r)}},{key:"parseHotkeysAutocompleteOptions",value:function(t){return null==t?null:t.map(function(t){return"/"===t?191:t})}}])&&b(e.prototype,n),i&&b(e,i),t}();var x=e()(w);x.version="2.0.1";const S=x})(),i.default})()});
\ No newline at end of file
index ca2511b84e4d86ab634a014f552daef025f69099..2546b5f9e34dd89edaea700bdf1ff6ba37b8b28e 100644 (file)
@@ -33,7 +33,7 @@ var initPhotoSwipeFromDOM = function(gallerySelector) {
                 continue;
             }
 
-            linkEl = figureEl.children[0]; // <a> element
+            linkEl = figureEl.querySelector('a'); // <a> element
 
             size = linkEl.getAttribute('data-size').split('x');
 
@@ -45,10 +45,9 @@ var initPhotoSwipeFromDOM = function(gallerySelector) {
             };
 
 
-
             if(figureEl.children.length > 1) {
                 // <figcaption> content
-                item.title = figureEl.children[1].innerHTML; 
+                item.title = figureEl.querySelector('figcaption').innerHTML; 
             }
 
             if(linkEl.children.length > 0) {