From 288072177d305b5b5a0d9453553caeb4bf522808 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 9 Dec 2024 10:18:26 +0700 Subject: [PATCH 01/12] feat: keyboard authoring service directory This is in draft, relating to a discussion on a forum: https://community.software.sil.org/t/keyman-keyboard-creator-directory/9408/5 Once we get a name or two, we'll publish --- developer/authoring-services.md | 45 +++++++++++++++++++++++++++++++++ developer/index.php | 1 + 2 files changed, 46 insertions(+) create mode 100644 developer/authoring-services.md diff --git a/developer/authoring-services.md b/developer/authoring-services.md new file mode 100644 index 00000000..95b89317 --- /dev/null +++ b/developer/authoring-services.md @@ -0,0 +1,45 @@ +--- +title: Keyboard authoring service directory +--- + +If you wish to create a keyboard layout but do not have the technical expertise or time to create it yourself, the people and companies listed on this page +may be able to assist you. + +--- + +### EXAMPLE: Code Hive Tx, LLC + +Contact: Steven Loomis, [codehivetx.us](https://codehivetx.us) + +--- + +## Request addition to this directory + +If you would like to be added to this directory, please complete the following form: + +* [Keyboard authoring service signup](https://forms.gle/i7S1Yb5ck4nUr8mn6) + +## Disclaimer + +The information within Keyboard authoring service directory is provided as a +public service to our community. The information is supplied by independent +third parties. + +While SIL Global makes every effort to ensure the accuracy of all the +information, it makes no warranty as to the accuracy or reliability of any such +information, content or other material contained on, distributed through, or +linked, downloaded or accessed through this directory. Any reliance upon any +information, content, materials, products, services included on or found through +this directory shall be at the user’s sole risk. + +Listing in this directory does not imply endorsement by SIL Global. Nor does SIL +Global endorse products or services that such listings may offer. SIL Global +disclaims all representations or warranties of any kind, express or implied, +with respect to this directory or the information, content, materials or +products included including, without limitation, warranties of quality. + +The directory may include link to external websites. When users click on such +links, they are leaving the Keyman website and are subject to the privacy and +security policies of the owners/sponsors of the external site. SIL Global cannot +authorize the use of copyrighted materials published on linked, external +websites. Users must request authorization from the owners of those websites. \ No newline at end of file diff --git a/developer/index.php b/developer/index.php index d98d71b5..992280b6 100644 --- a/developer/index.php +++ b/developer/index.php @@ -26,6 +26,7 @@
  • See the features.
  • Keyman Developer Downloads
  • Keyman Developer Support
  • +
  • Keyboard authoring service providers
  • Date: Mon, 9 Dec 2024 11:00:42 +0700 Subject: [PATCH 02/12] chore: add Enabling Languages to directory --- developer/authoring-services.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/developer/authoring-services.md b/developer/authoring-services.md index 95b89317..ccec553e 100644 --- a/developer/authoring-services.md +++ b/developer/authoring-services.md @@ -7,9 +7,11 @@ may be able to assist you. --- -### EXAMPLE: Code Hive Tx, LLC +### Enabling Languages -Contact: Steven Loomis, [codehivetx.us](https://codehivetx.us) +Advise on layout requirements; developing new keyboard layouts; creating word frequency lists from a corpus; assisting in publishing a completed keyboard to keyboard repository on github; introductory training on using git and github to enable self-publishing. + +Contact: [enabling.languages@gmail.com](mailto:enabling.languages@gmail.com) --- From f14157ce245dc28833b47851312baefbe18bab59 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 11 Dec 2024 12:57:46 +1000 Subject: [PATCH 03/12] chore: add Katelem Edeh to directory --- developer/authoring-services.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/developer/authoring-services.md b/developer/authoring-services.md index ccec553e..e07fe4e8 100644 --- a/developer/authoring-services.md +++ b/developer/authoring-services.md @@ -15,6 +15,14 @@ Contact: [enabling.languages@gmail.com](mailto:enabling.languages@gmail.com) --- +### Katelem Edeh + +Creating new keyboards and lexical models. + +Contact: [katelem247@gmail.com](mailto:katelem247@gmail.com) + +--- + ## Request addition to this directory If you would like to be added to this directory, please complete the following form: From e56de628d55fbc4e4d02108d7d64afaf59891165 Mon Sep 17 00:00:00 2001 From: Meng-Heng Date: Fri, 13 Dec 2024 14:19:38 +0700 Subject: [PATCH 04/12] fix: 404 Not Found link in docs --- iphone-and-ipad/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iphone-and-ipad/index.php b/iphone-and-ipad/index.php index 4d2c1520..3f0c4efe 100644 --- a/iphone-and-ipad/index.php +++ b/iphone-and-ipad/index.php @@ -228,7 +228,7 @@ You can develop your own keyboard layouts for Keyman for iPhone and iPad with Keyman Developer. If you have existing keyboards, they can be ported to iOS with just a recompile. And of course, we include support for touch-oriented features such as touch-and-hold menus, dynamic keyboard layers and more!

    - Keyman Engine for iPhone and iPad Documentation + Keyman Engine for iPhone and iPad Documentation

    Download the latest Keyman Engine for iOS From 69385cd43dcb0921e7285999019fa1a191c3d120 Mon Sep 17 00:00:00 2001 From: Meng-Heng Date: Fri, 13 Dec 2024 14:43:04 +0700 Subject: [PATCH 05/12] fix: 404 Not Found link in docs --- iphone-and-ipad/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iphone-and-ipad/index.php b/iphone-and-ipad/index.php index 3f0c4efe..d59d92c2 100644 --- a/iphone-and-ipad/index.php +++ b/iphone-and-ipad/index.php @@ -228,7 +228,7 @@ You can develop your own keyboard layouts for Keyman for iPhone and iPad with Keyman Developer. If you have existing keyboards, they can be ported to iOS with just a recompile. And of course, we include support for touch-oriented features such as touch-and-hold menus, dynamic keyboard layers and more!

    - Keyman Engine for iPhone and iPad Documentation + Keyman Engine for iPhone and iPad Documentation

    Download the latest Keyman Engine for iOS From 424a1df652fb6590c8cbcb004a9037d7facb9aee Mon Sep 17 00:00:00 2001 From: MengHeng <90595388+Meng-Heng@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:30:04 +0700 Subject: [PATCH 06/12] Apply suggestions from code review Co-authored-by: Marc Durdin --- iphone-and-ipad/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iphone-and-ipad/index.php b/iphone-and-ipad/index.php index d59d92c2..03f0cab1 100644 --- a/iphone-and-ipad/index.php +++ b/iphone-and-ipad/index.php @@ -228,7 +228,7 @@ You can develop your own keyboard layouts for Keyman for iPhone and iPad with Keyman Developer. If you have existing keyboards, they can be ported to iOS with just a recompile. And of course, we include support for touch-oriented features such as touch-and-hold menus, dynamic keyboard layers and more!

    - Keyman Engine for iPhone and iPad Documentation + Keyman Engine for iPhone and iPad Documentation

    Download the latest Keyman Engine for iOS From 6bbd9591371427c5157dedf21d7116f4b8107db3 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Tue, 17 Dec 2024 11:38:05 +0700 Subject: [PATCH 07/12] chore: Include part-time volunteers on /jobs --- jobs/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jobs/index.md b/jobs/index.md index 62ab13d8..a9c84794 100644 --- a/jobs/index.md +++ b/jobs/index.md @@ -6,6 +6,9 @@ title: Join the Keyman team Do you have a passion for software development? Would you be interested in joining SIL and working full-time on developing software that serves the needs of language communities around the world? + +We also welcome part-time volunteers. + [Write us](https://software.sil.org/about/contact/), or ping us on [@Keyman](https://twitter.com/keyman) and introduce yourself — we'd love to meet with you! From 2fb50c9dadaef3e9dc3beb6eca9eac0ac87a01cf Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Sat, 4 Jan 2025 11:00:06 +0700 Subject: [PATCH 08/12] fix: avoid error when jsFilename is missing or mismatches package id Ensures that the keymanweb keyboard reference is correct for the package, as it will not always match the package identifier, or may not be available as a standalone keyboard. This then prevents the error in #525 from presenting. This fix works around a data issue documented in keymanapp/keyman#12853. Fixes: #525 Relates-to: keymanapp/keyman#12853 --- _includes/includes/ui/keyboard-details.php | 93 +++++++++++++--------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/_includes/includes/ui/keyboard-details.php b/_includes/includes/ui/keyboard-details.php index 98d4d881..02e9485a 100644 --- a/_includes/includes/ui/keyboard-details.php +++ b/_includes/includes/ui/keyboard-details.php @@ -55,7 +55,7 @@ class KeyboardDetails /** * render_keyboard_details - display keyboard download boxes and details - * @param $id - keyboard ID + * @param $id - keyboard package ID * @param string $tier - ['stable', 'alpha', or 'beta'] * @param bool $landingPage - when true, details won't display keyboard search box or title * @param string $bcp47 - BCP 47 tag to pass as a hint to download links for apps to make connection @@ -125,41 +125,48 @@ protected static function download_box($platform) { protected static function WriteWebBoxes($useDescription) { global $embed_target; global $KeymanHosts; - if (isset(self::$keyboard->platformSupport->desktopWeb) && self::$keyboard->platformSupport->desktopWeb != 'none' && empty(self::$deprecatedBy)) { - if(empty(self::$bcp47)) { - if (isset(self::$keyboard->languages)) { - if (is_array(self::$keyboard->languages)) { - if (count(self::$keyboard->languages) > 0) { - $lang = self::$keyboard->languages[0]; - } - } else { - $langs = array_keys(get_object_vars(self::$keyboard->languages)); - if (count($langs) > 0) { - $lang = $langs[0]; - } + + // only show if the jsFilename property is present in the .keyboard_info + if(empty(self::$keyboard->jsFilename)) { + return FALSE; + } + + if (!isset(self::$keyboard->platformSupport->desktopWeb) || self::$keyboard->platformSupport->desktopWeb == 'none' || !empty(self::$deprecatedBy)) { + return FALSE; + } + + if(empty(self::$bcp47)) { + if (isset(self::$keyboard->languages)) { + if (is_array(self::$keyboard->languages)) { + if (count(self::$keyboard->languages) > 0) { + $lang = self::$keyboard->languages[0]; + } + } else { + $langs = array_keys(get_object_vars(self::$keyboard->languages)); + if (count($langs) > 0) { + $lang = $langs[0]; } } - } else { - $lang = self::$bcp47; - } - if (!isset($lang)) $lang = 'en'; - $url = "{$KeymanHosts->keymanweb_com}/#$lang,Keyboard_" . self::$keyboard->id; - if($useDescription) { - $description = htmlentities(self::$keyboard->name); - $description = "

    Use $description in your web browser. No need to install anything.
    "; - $linktext = 'Use keyboard online'; - } else { - $description = ''; - $linktext = 'Full online editor'; } - return << - $linktext - $description - -END; + } else { + $lang = self::$bcp47; + } + if (!isset($lang)) $lang = 'en'; + $url = "{$KeymanHosts->keymanweb_com}/#$lang,Keyboard_" . self::GetWebKeyboardId(); + if($useDescription) { + $description = htmlentities(self::$keyboard->name); + $description = "
    Use $description in your web browser. No need to install anything.
    "; + $linktext = 'Use keyboard online'; + } else { + $description = ''; + $linktext = 'Full online editor'; } - return FALSE; + return << + $linktext + $description + +END; } protected static function LoadData() { @@ -170,7 +177,7 @@ protected static function LoadData() { if ($s === FALSE) { // Will fail later in the script self::$error .= error_get_last()['message'] . "\n"; - self::$title = 'Failed to load keyboard ' . self::$id; + self::$title = 'Failed to load keyboard package ' . self::$id; header('HTTP/1.0 404 Keyboard not found'); } else { $s = json_decode($s); @@ -184,7 +191,7 @@ protected static function LoadData() { self::$license = self::map_license(isset(self::$keyboard->license) ? self::$keyboard->license : 'Unknown'); } else { self::$error .= "Error returned from {$KeymanHosts->api_keyman_com}: $s\n"; - self::$title = 'Failed to load keyboard ' . self::$id; + self::$title = 'Failed to load keyboard package ' . self::$id; header('HTTP/1.0 500 Internal Server Error'); } } @@ -300,7 +307,7 @@ protected static function WriteTitle() { // If parameters are missing ... ?>

    -

    Keyboard not found.

    +

    Keyboard package not found.

    Important note: @@ -393,6 +399,13 @@ protected static function WriteDownloadBoxes() { } } + private static function GetWebKeyboardId() { + if(empty(self::$keyboard->jsFilename)) { + return ""; + } + return preg_replace("/\.js$/", "", self::$keyboard->jsFilename); + } + protected static function GetWebDeviceFromPageDevice() { global $pageDevice; switch($pageDevice) { @@ -418,12 +431,18 @@ protected static function WriteKeymanWebBox() { return; } + // only show if the jsFilename property is present in the .keyboard_info + if(empty(self::$keyboard->jsFilename)) { + return; + } + // only inject on desktop platforms $webDevice = self::GetWebDeviceFromPageDevice(); if(!$webDevice) { return; } + $kmwid = self::GetWebKeyboardId(); $webtext = self::WriteWebBoxes(false); $cdnUrlBase = KeymanWebHost::getKeymanWebUrlBase(); ?> @@ -452,7 +471,7 @@ function() { document.getElementById('osk-host').appendChild(newOSK.element); } ); - keyman.addKeyboards(''); + keyman.addKeyboards(''); })(); Date: Mon, 13 Jan 2025 05:23:15 +0100 Subject: [PATCH 09/12] chore: Apply suggestions from code review Co-authored-by: Darcy Wong --- _includes/includes/ui/keyboard-details.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_includes/includes/ui/keyboard-details.php b/_includes/includes/ui/keyboard-details.php index 02e9485a..7fa37230 100644 --- a/_includes/includes/ui/keyboard-details.php +++ b/_includes/includes/ui/keyboard-details.php @@ -442,7 +442,7 @@ protected static function WriteKeymanWebBox() { return; } - $kmwid = self::GetWebKeyboardId(); + $keymanWebId = self::GetWebKeyboardId(); $webtext = self::WriteWebBoxes(false); $cdnUrlBase = KeymanWebHost::getKeymanWebUrlBase(); ?> @@ -471,7 +471,7 @@ function() { document.getElementById('osk-host').appendChild(newOSK.element); } ); - keyman.addKeyboards(''); + keyman.addKeyboards(''); })(); Date: Tue, 14 Jan 2025 07:33:18 +0700 Subject: [PATCH 10/12] fix: Append query string for keyboards/languages searches --- .htaccess | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.htaccess b/.htaccess index 1342041a..5ab52712 100644 --- a/.htaccess +++ b/.htaccess @@ -108,7 +108,7 @@ RewriteRule "^keyboards/(?!index\.php|install|keyboard|session|share)([^/]+)$" " # RewriteRule "^keyboards$" "/keyboards/index.php" [L] # /keyboards/languages to /keyboards/index.php -RewriteRule "^keyboards/languages/(.*)" "/keyboards/index.php?q=l:id:$1" [END] +RewriteRule "^keyboards/languages/(.*)" "/keyboards/index.php?q=l:id:$1" [END,QSA] # /keyboards/download to /keyboards/download.php RewriteRule "^keyboards/download(.php)?" "/keyboards/download.php" [END,QSA] From c817759e47404e158a5f582da1229c4bf0265f60 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 17 Jan 2025 15:14:45 +0700 Subject: [PATCH 11/12] chore: Add Keyman is free and open source blurb This was a bit buried. Make it much more visible! Fixes: #528 --- _includes/2020/templates/Menu.php | 2 + about/index.md | 1 + cdn/dev/css/template.css | 20 +++++--- free/index.md | 28 +++++++++++ free/index.php | 84 ------------------------------- index.php | 5 +- 6 files changed, 47 insertions(+), 93 deletions(-) create mode 100644 free/index.md delete mode 100644 free/index.php diff --git a/_includes/2020/templates/Menu.php b/_includes/2020/templates/Menu.php index c47ba8e2..9374ed58 100644 --- a/_includes/2020/templates/Menu.php +++ b/_includes/2020/templates/Menu.php @@ -106,6 +106,8 @@ private static function render_top_menu(object $fields): void { " alt='Header bottom' />
    + Keyman is free and open source +
    diff --git a/about/index.md b/about/index.md index 99d9f854..2e3fd390 100644 --- a/about/index.md +++ b/about/index.md @@ -10,6 +10,7 @@ Keyboard layouts are defined with a clear and easy to understand keyboard gramma means keyboard layouts can be intelligent and make it simple to type even the most complex languages. Keyboard layouts are distributed through an open catalog to all major desktop and mobile platforms. +Keyman is [free and open source!](/free) ## About SIL Global diff --git a/cdn/dev/css/template.css b/cdn/dev/css/template.css index 522192b1..68433034 100644 --- a/cdn/dev/css/template.css +++ b/cdn/dev/css/template.css @@ -328,7 +328,7 @@ html { .button a { color: inherit; - text-decoration: none; + text-decoration: none; align-items: center; } @@ -368,17 +368,17 @@ span.button.disabled{ .version-type { margin-bottom: 10px; height: 300px; - width: 30%; + width: 30%; max-width: 300px; padding: 10px; - box-sizing: border-box; + box-sizing: border-box; box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; border-radius: 5px; - overflow-y: auto; + overflow-y: auto; } .version-type h5 { - opacity: 0.6; + opacity: 0.6; } .version-item { @@ -388,7 +388,7 @@ span.button.disabled{ .version-item a { text-decoration: none; color: #B92034; - display: block; + display: block; width: 100%; } @@ -530,6 +530,12 @@ span.button.disabled{ text-decoration: none; } +#help #free { + float: left; + margin-right: 1em; + padding-top: 7px; +} + #help form { float: left; } @@ -537,7 +543,7 @@ span.button.disabled{ #help #donate { padding-top: 5px; margin-right: 1em; - margin-left: 1em; + margin-left: 0em; } #help #donate a { diff --git a/free/index.md b/free/index.md new file mode 100644 index 00000000..9cff2408 --- /dev/null +++ b/free/index.md @@ -0,0 +1,28 @@ +--- +title: Keyman is free and open source +--- + +Keyman is completely free to use on all devices! SIL Global does not charge +any license fees for using Keyman. + +* This change took place in August 2017 after SIL Global acquired Keyman from + Tavultesoft Pty Ltd. + +# Open Source + +Keyman is licensed under the [MIT License](https://github.com/keymanapp/keyman/blob/master/LICENSE.md). +The MIT License is a short, permissive software license. Basically, you can +do whatever you want as long as you include the original copyright and license +notice in any copy of the software/source. + +SIL Global have chosen the MIT License in order to provide you, the users of +Keyman with the greatest possible freedom to work in your language. + +All 'release' quality Keyman keyboards in the [Keyman keyboard repository](https://github.com/keymanapp/keyboards) +are licensed under the MIT License. All lexical models in the +[Keyman lexical model repository](https://github.com/keymanapp/lexical-models) +are also licensed under the MIT License. Copyright for these keyboards and +lexical models belongs to many different authors. + +* [About SIL Global](https://www.sil.org/about) +* [About Keyman](/about) \ No newline at end of file diff --git a/free/index.php b/free/index.php deleted file mode 100644 index 2b5cb5e4..00000000 --- a/free/index.php +++ /dev/null @@ -1,84 +0,0 @@ -'Keyman is free! | Keyman', - 'css' => ['template.css', 'product-grid.css'], - 'showMenu' => true - ]); -?> - - -

    Keyman is free on all platforms!

    - -

    Following SIL International’s purchase of the Keyman intellectual property in September 2015, SIL has now completed the acquisition of Keyman from Tavultesoft.

    - -

    About SIL International

    - -

    As of August 2017, SIL has now made the entire Keyman product suite freely available to the world.

    - -

    Read the full announcement on our blog!

    - -

    Frequently Asked Questions

    - -
    -
    If I bought a license of Keyman from Tavultesoft before the free version was available, can I get a refund?
    -
    No. Tavultesoft is not offering refunds.
    - -
    Who do I go to for technical support?
    -
    -

    Support has transitioned to be primarily community based. We have two new locations for Keyman technical support:

    - - -
    -

    The Tavultesoft Forums are now read only.

    -
    - -
    I have an existing license for Keyman Desktop. What happens to that license?
    -
    Your license will continue to work. SIL and Tavultesoft are committed to keeping the license servers running. However, we would encourage you to upgrade to version , which is completely free.
    - -
    What are SIL’s long-term plans for Keyman?
    -
    -

    SIL will continue to provide Keyman as a user-friendly downloadable program with the existing and growing catalogue of high quality keyboard layouts.

    - -

    SIL’s long-term goal is to provide a freely available and extensible keyboarding platform, using Keyman as a foundation. SIL is working with standards organisations to create a global keyboarding standard, and will update Keyman to be the open source reference implementation for all major operating systems.

    -
    - -
    What is Marc Durdin doing?
    -
    You may know Marc as the primary developer of Keyman. Marc is leading the SIL team developing the Keyman projects within SIL.
    - -
    What is happening to Tavultesoft?
    -
    Tavultesoft will continue to operate as a consulting microbusiness, mostly for Keyman-related projects. As SIL now owns the Keyman IP, Tavultesoft has ceased all development and direct technical support on Keyman itself.
    - -
    How can I support the development of Keyman?
    -
    -

    You can support the Keyman project through financially partnering with Marc’s family. SIL members are funded through partnership with individuals and organisations that support the vision of SIL. Please visit https://durdin.net/ to learn more about the Durdin family’s journey.

    - -

    You can also donate directly to the Keyman project through the Keyman website at keyman_com_host ?>/donate.

    -
    -
    - -

    Keep in touch

    - -

    The Tavultesoft mailing list has now been closed down. If you would like to keep in touch, here are some options:

    - - diff --git a/index.php b/index.php index 28bc2888..8ef288fc 100644 --- a/index.php +++ b/index.php @@ -5,8 +5,8 @@ // Required head([ 'title' =>'Keyman | Type to the world in your language', - 'description' => 'Unlock the power of your language with Keyman customizable keyboard software. - Available for Windows, Mac, Linux, Android, iPhone, and web, + 'description' => 'Unlock the power of your language with Keyman customizable keyboard software. + Available for Windows, Mac, Linux, Android, iPhone, and web, we support over 2,000 languages to make communication seamless and meaningful.', 'css' => ['template.css','index.css'], 'showMenu' => true, @@ -45,6 +45,7 @@

    Keyman helps you type in over 2000 languages on just about any device — Windows, macOS, Linux, iPhone, iPad, Android tablets and phones, and even instantly in your web browser. + Keyman is completely free and open source!

    From b0c94c783328bdcc8956b16f99d05ce20eb87b75 Mon Sep 17 00:00:00 2001 From: Darcy Wong Date: Tue, 21 Jan 2025 09:43:54 +0700 Subject: [PATCH 12/12] chore: Update shared-sites to v0.15 --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index ba06abcf..14b52cfe 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ ## START STANDARD SITE BUILD SCRIPT INCLUDE readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" readonly BOOTSTRAP="$(dirname "$THIS_SCRIPT")/resources/bootstrap.inc.sh" -readonly BOOTSTRAP_VERSION=v0.11 +readonly BOOTSTRAP_VERSION=v0.15 [ -f "$BOOTSTRAP" ] && source "$BOOTSTRAP" || source <(curl -fs https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh) ## END STANDARD SITE BUILD SCRIPT INCLUDE