From 514b59acc3a887add7ed5f7a8fb5aaee6484c200 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 7 Jan 2026 08:14:48 +1100 Subject: [PATCH 1/4] feat: add keyboard download stats by period Returns list of keyboard id and name, with keyboard download count and daily average, in JSON or CSV format Usage: * JSON: /script/statistics/keyboards.php?startDate=2025-01-01&endDate=2026-01-01 * CSV: /script/statistics/keyboards.php?startDate=2025-01-01&endDate=2026-01-01&csv=1 Test-bot: skip --- script/statistics/annual-statistics.inc.php | 52 ++++++++++----------- script/statistics/keyboards.php | 49 +++++++++++++++++++ tools/db/build/annual-statistics.sql | 35 +++++++++++++- 3 files changed, 109 insertions(+), 27 deletions(-) create mode 100644 script/statistics/keyboards.php diff --git a/script/statistics/annual-statistics.inc.php b/script/statistics/annual-statistics.inc.php index 96f50d8..98ca7c6 100644 --- a/script/statistics/annual-statistics.inc.php +++ b/script/statistics/annual-statistics.inc.php @@ -7,44 +7,44 @@ namespace Keyman\Site\com\keyman\api; - // strip out repeated columns with numeric keys (by default the results returned - // give each column twice, once with a column name, and once with a column index) - function filter_columns_by_name($data) { - $result = []; - foreach($data as $row) { - $r = []; - foreach($row as $id => $val) { - if(!is_numeric($id)) { - $r[$id] = intval($val); - } - } - array_push($result, $r); - } - return $result; - } - class AnnualStatistics { function execute($mssql, $startDate, $endDate) { + return $this->_execute('sp_annual_statistics', $mssql, $startDate, $endDate); + } - $stmt = $mssql->prepare('EXEC sp_annual_statistics :prmStartDate, :prmEndDate'); - - $stmt->bindParam(":prmStartDate", $startDate); - $stmt->bindParam(":prmEndDate", $endDate); + function executeDownloadsByMonth($mssql, $startDate, $endDate) { + return $this->_execute('sp_keyboard_downloads_by_month_statistics', $mssql, $startDate, $endDate); + } - $stmt->execute(); - $data = $stmt->fetchAll(); - return filter_columns_by_name($data); + function executeKeyboards($mssql, $startDate, $endDate) { + return $this->_execute('sp_statistics_keyboard_downloads_by_id', $mssql, $startDate, $endDate); } - function executeDownloadsByMonth($mssql, $startDate, $endDate) { - $stmt = $mssql->prepare('EXEC sp_keyboard_downloads_by_month_statistics :prmStartDate, :prmEndDate'); + private function _execute($proc, $mssql, $startDate, $endDate) { + $stmt = $mssql->prepare("EXEC $proc :prmStartDate, :prmEndDate"); $stmt->bindParam(":prmStartDate", $startDate); $stmt->bindParam(":prmEndDate", $endDate); $stmt->execute(); $data = $stmt->fetchAll(); - return filter_columns_by_name($data); + return $this->filter_columns_by_name($data); + } + + // strip out repeated columns with numeric keys (by default the results returned + // give each column twice, once with a column name, and once with a column index) + private function filter_columns_by_name($data) { + $result = []; + foreach($data as $row) { + $r = []; + foreach($row as $id => $val) { + if(!is_numeric($id)) { + $r[$id] = is_numeric($val) ? intval($val) : $val; + } + } + array_push($result, $r); + } + return $result; } } diff --git a/script/statistics/keyboards.php b/script/statistics/keyboards.php new file mode 100644 index 0000000..5fc7fcd --- /dev/null +++ b/script/statistics/keyboards.php @@ -0,0 +1,49 @@ +executeKeyboards($mssql, $startDate, $endDate); + + if(isset($_REQUEST['csv'])) { + $out = fopen('php://output', 'w'); + + if(sizeof($keyboards) > 0) { + $data = array_keys($keyboards[0]); + fputcsv($out, $data, ',', '"', ''); + foreach($keyboards as $row) { + $data = array_values($row); + fputcsv($out, $data, ',', '"', ''); + } + } + + fclose($out); + } else { + $data = ["keyboards" => $keyboards]; + json_print($data); + } diff --git a/tools/db/build/annual-statistics.sql b/tools/db/build/annual-statistics.sql index 8307055..ec632ed 100644 --- a/tools/db/build/annual-statistics.sql +++ b/tools/db/build/annual-statistics.sql @@ -22,6 +22,8 @@ users or keyboards in use, but gives a rough volume. */ +/* TODO(lowpri): rename these procs to sp_statistics_xxxx */ + DROP PROCEDURE IF EXISTS sp_annual_statistics; GO @@ -40,6 +42,8 @@ SELECT (select sum(count) from kstats.t_keyboard_downloads WHERE statdate >= @prmStartDate AND statdate < @prmEndDate) RawKeyboardDownloadCount GO +/* ======================================================================== */ + DROP PROCEDURE IF EXISTS sp_keyboard_downloads_by_month_statistics; GO @@ -57,4 +61,33 @@ CREATE PROCEDURE sp_keyboard_downloads_by_month_statistics ( WHERE statdate >= @prmStartDate AND statdate < @prmEndDate group by month(statdate), year(statdate) order by 2, 1 -GO \ No newline at end of file +GO + +/* ======================================================================== */ + +DROP PROCEDURE IF EXISTS sp_statistics_keyboard_downloads_by_id; +GO + +CREATE PROCEDURE sp_statistics_keyboard_downloads_by_id ( + @prmStartDate DATE, + @prmEndDate DATE +) AS + + DECLARE @DayCount INT = DATEDIFF(day,@prmStartDate,@prmEndDate) + 1 + + SELECT + k.keyboard_id, + k.name, + SUM(count) RawKeyboardDownloadCount, + SUM(count)/@DayCount DownloadsPerDay + FROM + k0.t_keyboard k LEFT JOIN + kstats.t_keyboard_downloads d ON d.keyboard_id = k.keyboard_id + WHERE + (d.statdate >= @prmStartDate AND d.statdate < @prmEndDate) OR (d.keyboard_id IS NULL) + GROUP BY + k.keyboard_id, + k.name + ORDER BY + k.keyboard_id +GO From 2c6473a6abf361261ab700b43c8a1cfcfa05359b Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 12 Jan 2026 17:31:07 +1100 Subject: [PATCH 2/4] feat: collect raw application download statistics This adds download statistics collection for Keyman apps hosted on downloads.keyman.com - specifically Keyman for Windows, Keyman for Mac, and Keyman Developer. Other platforms are distributed through stores or other mechanisms, so will not generally be visible here. For now we collect app, version, and tier data, by day. This can be expanded as needed in the future. Test-bot: skip --- .htaccess | 4 ++ .../1.0/app-downloads-increment.json | 23 ++++++ .../app-downloads-increment.inc.php | 28 ++++++++ .../app-downloads-increment.php | 72 +++++++++++++++++++ tools/db/build/search.sql | 20 +++++- tools/db/build/sp_app_downloads_increment.sql | 39 ++++++++++ tools/db/build/sp_increment_download.sql | 1 + 7 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 schemas/app-downloads-increment/1.0/app-downloads-increment.json create mode 100644 script/app-downloads-increment/app-downloads-increment.inc.php create mode 100644 script/app-downloads-increment/app-downloads-increment.php create mode 100644 tools/db/build/sp_app_downloads_increment.sql diff --git a/.htaccess b/.htaccess index 6e6005b..2464153 100644 --- a/.htaccess +++ b/.htaccess @@ -32,6 +32,10 @@ RewriteRule "^keyboard/(.*)$" "/script/keyboard/keyboard.php?id=$1" [END] RewriteRule "^increment-download/(.*)$" "/script/increment-download/increment-download.php?id=$1" [END] +#### Rewrites for /script folder: /app-downloads-increment + +RewriteRule "^app-downloads-increment/([^/]+)/([^/]+)/(.*)$" "/script/app-downloads-increment/app-downloads-increment.php?product=$1&version=$2&tier=$3" [END] + #### Rewrites for /script folder: /model RewriteRule "^model(/)?$" "/script/model-search/model-search.php" [END] diff --git a/schemas/app-downloads-increment/1.0/app-downloads-increment.json b/schemas/app-downloads-increment/1.0/app-downloads-increment.json new file mode 100644 index 0000000..0ae5fe2 --- /dev/null +++ b/schemas/app-downloads-increment/1.0/app-downloads-increment.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$ref": "#/definitions/app-downloads-increment", + + "definitions": { + "app-downloads-increment": { + "type": "object", + "required": [ + "product", + "version", + "tier", + "count" + ], + "additionalProperties": true, + "properties": { + "product": { "type": "string" }, + "version": { "type": "string" }, + "tier": { "type": "string" }, + "count": { "type": "integer" } + } + } + } +} \ No newline at end of file diff --git a/script/app-downloads-increment/app-downloads-increment.inc.php b/script/app-downloads-increment/app-downloads-increment.inc.php new file mode 100644 index 0000000..887fea6 --- /dev/null +++ b/script/app-downloads-increment/app-downloads-increment.inc.php @@ -0,0 +1,28 @@ +prepare('EXEC sp_app_downloads_increment :product, :version, :tier'); + $stmt->bindParam(":product", $product); + $stmt->bindParam(":version", $version); + $stmt->bindParam(":tier", $tier); + $stmt->execute(); + $data = $stmt->fetchAll(); + if(count($data) == 0) { + return NULL; + } + + $obj = [ + 'product' => $data[0]['product'], + 'version' => $data[0]['version'], + 'tier' => $data[0]['tier'], + 'count' => intval($data[0]['count']) + ]; + + return $obj; + } + } diff --git a/script/app-downloads-increment/app-downloads-increment.php b/script/app-downloads-increment/app-downloads-increment.php new file mode 100644 index 0000000..3e5ff27 --- /dev/null +++ b/script/app-downloads-increment/app-downloads-increment.php @@ -0,0 +1,72 @@ +api_keyman_com .'/schemas/app-downloads-increment/1.0/app-downloads-increment.json#>; rel="describedby"'); + + $AllowGet = isset($_REQUEST['debug']); + + if(!$AllowGet && $_SERVER['REQUEST_METHOD'] != 'POST') { + fail('POST required'); + } + + if(!isset($_REQUEST['key'])) { + fail('key parameter must be set'); + } + + // Note: we don't currently unit-test this one + if(KeymanHosts::Instance()->Tier() === KeymanHosts::TIER_DEVELOPMENT) + $key = 'local'; + else + $key = $env['API_KEYMAN_COM_INCREMENT_DOWNLOAD_KEY']; + + if($_REQUEST['key'] !== $key) { + fail('Invalid key'); + } + + if(!isset($_REQUEST['product']) || + !isset($_REQUEST['version']) || + !isset($_REQUEST['tier']) + ) { + // We don't constrain what the product / version / tier may be here, because + // we may add other products in the future + fail('product, version, tier parameters must be set'); + } + + $product = $_REQUEST['product']; + $version = $_REQUEST['version']; + $tier = $_REQUEST['tier']; + + /** + * POST https://api.keyman.com/app-downloads-increment/product/version/tier + * + * Increments the download counter for a single product identified by + * `product`, `version`, and `tier`. Returns the new total count for the + * product/version/tier for the day + * + * https://api.keyman.com/schemas/app-downloads-increment.json is JSON schema + * + * @param product the name of the product to increment ( "android", "ios", + * "linux", "macos", "web", "windows", "developer"...) + * @param version the version number ("1.2.3") + * @param tier the tier of the product ("alpha", "beta", "stable") + * @param key internal key to allow endpoint to run + */ + + $json = \Keyman\Site\com\keyman\api\AppDownloads::increment($mssql, $product, $version, $tier); + if($json === NULL) { + fail("Failed to increment stat, invalid parameters [$product, $version, $tier]?", 401); + } + + echo json_encode($json, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES); \ No newline at end of file diff --git a/tools/db/build/search.sql b/tools/db/build/search.sql index 547e929..d3aaf68 100644 --- a/tools/db/build/search.sql +++ b/tools/db/build/search.sql @@ -306,7 +306,10 @@ CREATE TABLE t_dbdatasources ( DROP TABLE IF EXISTS t_keyboard_downloads; GO ---add a new schema for kstats here so we can use it in search.sql +-- tables in the kstats schema are persistent on the production +-- infrastructure, unlike the other tables in the database, which +-- are recreated on each deployment. + IF SCHEMA_ID('kstats') IS NULL BEGIN EXEC sp_executesql N'CREATE SCHEMA kstats' @@ -325,3 +328,18 @@ BEGIN keyboard_id, statdate ) INCLUDE (count) END + +IF OBJECT_ID('kstats.t_app_downloads', 'U') IS NULL +BEGIN + CREATE TABLE kstats.t_app_downloads ( + product NVARCHAR(64) NOT NULL, -- "android", "ios", "linux", "macos", "web", "windows", "developer"... + version NVARCHAR(64) NOT NULL, -- "123.456.789" + tier NVARCHAR(16) NOT NULL, -- "alpha", "beta", "stable" + statdate DATE, + count INT NOT NULL + ) + + CREATE INDEX ix_app_downloads ON kstats.t_app_downloads ( + product, version, tier, statdate + ) INCLUDE (count) +END diff --git a/tools/db/build/sp_app_downloads_increment.sql b/tools/db/build/sp_app_downloads_increment.sql new file mode 100644 index 0000000..8b7ea71 --- /dev/null +++ b/tools/db/build/sp_app_downloads_increment.sql @@ -0,0 +1,39 @@ +/* + sp_app_downloads_increment +*/ + +DROP PROCEDURE IF EXISTS sp_app_downloads_increment; +GO + +CREATE PROCEDURE sp_app_downloads_increment ( + @prmProduct NVARCHAR(64), + @prmVersion NVARCHAR(64), + @prmTier NVARCHAR(16) +) AS +BEGIN + SET NOCOUNT ON; + + BEGIN TRANSACTION; + + DECLARE @date DATE, @count INT; + + SET @date = CONVERT(date, GETDATE()); + + UPDATE kstats.t_app_downloads + WITH (UPDLOCK, SERIALIZABLE) -- ensure that this statement is atomic with following INSERT + SET count = count + 1, @count = count + 1 + WHERE product = @prmProduct AND version = @prmVersion AND tier = @prmTier AND statdate = @date; + + IF @@ROWCOUNT = 0 + BEGIN + INSERT kstats.t_app_downloads (product, version, tier, statdate, count) + SELECT @prmProduct, @prmVersion, @prmTier, @date, 1 + SET @count = 1 + END + + SET NOCOUNT OFF + + SELECT @prmProduct product, @prmVersion version, @prmTier tier, @count count + + COMMIT TRANSACTION; +END diff --git a/tools/db/build/sp_increment_download.sql b/tools/db/build/sp_increment_download.sql index c628bcd..2f6f6c8 100644 --- a/tools/db/build/sp_increment_download.sql +++ b/tools/db/build/sp_increment_download.sql @@ -1,5 +1,6 @@ /* sp_increment_download + TODO: rename to sp_keyboard_downloads_increment */ DROP PROCEDURE IF EXISTS sp_increment_download; From 2f867f48225a61fa6c14777b8af89361b41b89ca Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 13 Jan 2026 15:19:07 +1100 Subject: [PATCH 3/4] feat: add app downloads by month stats report Test-bot: skip --- script/statistics/annual-statistics.inc.php | 4 ++++ script/statistics/annual.php | 4 +++- tools/db/build/annual-statistics.sql | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/script/statistics/annual-statistics.inc.php b/script/statistics/annual-statistics.inc.php index 98ca7c6..ce57995 100644 --- a/script/statistics/annual-statistics.inc.php +++ b/script/statistics/annual-statistics.inc.php @@ -21,6 +21,10 @@ function executeKeyboards($mssql, $startDate, $endDate) { return $this->_execute('sp_statistics_keyboard_downloads_by_id', $mssql, $startDate, $endDate); } + function executeAppDownloadsByMonth($mssql, $startDate, $endDate) { + return $this->_execute('sp_app_downloads_by_month_statistics', $mssql, $startDate, $endDate); + } + private function _execute($proc, $mssql, $startDate, $endDate) { $stmt = $mssql->prepare("EXEC $proc :prmStartDate, :prmEndDate"); diff --git a/script/statistics/annual.php b/script/statistics/annual.php index c9b2df0..6fdd38e 100644 --- a/script/statistics/annual.php +++ b/script/statistics/annual.php @@ -30,5 +30,7 @@ $stats = new \Keyman\Site\com\keyman\api\AnnualStatistics(); $summary = $stats->execute($mssql, $startDate, $endDate); $downloads = $stats->executeDownloadsByMonth($mssql, $startDate, $endDate); - $data = ["summary" => $summary, "keyboardDownloadsByMonth" => $downloads]; + $appDownloads = $stats->executeAppDownloadsByMonth($mssql, $startDate, $endDate); + $data = ["summary" => $summary, "keyboardDownloadsByMonth" => $downloads, "appDownloadsByMonth" => $appDownloads]; json_print($data); + diff --git a/tools/db/build/annual-statistics.sql b/tools/db/build/annual-statistics.sql index ec632ed..98e2247 100644 --- a/tools/db/build/annual-statistics.sql +++ b/tools/db/build/annual-statistics.sql @@ -91,3 +91,24 @@ CREATE PROCEDURE sp_statistics_keyboard_downloads_by_id ( ORDER BY k.keyboard_id GO + +/* ======================================================================== */ + +DROP PROCEDURE IF EXISTS sp_app_downloads_by_month_statistics; +GO + +CREATE PROCEDURE sp_app_downloads_by_month_statistics ( + @prmStartDate DATE, + @prmEndDate DATE +) AS + select + month(statdate) Month, + year(statdate) Year, + product Product, + sum(count) RawAppDownloadCount, + sum(count)/day(eomonth(datefromparts(year(statdate),month(statdate),1))) DownloadsPerDay + from kstats.t_app_downloads + WHERE statdate >= @prmStartDate AND statdate < @prmEndDate + group by month(statdate), year(statdate), product + order by 3, 2, 1 +GO From cd2dc0ee7b75d1757c91ea001d62f8f765aa3a16 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 15 Jan 2026 13:52:20 +1100 Subject: [PATCH 4/4] feat: use app go/ url for online update checks to capture download stats In the online update check response, wrap download.keyman.com urls in the keyman.com/go/app/download url forwarding pattern. Test-bot: skip --- schemas/windows-update/17.0/sample.json | 178 ++++++++++++++++++ .../14.0/update/WindowsUpdateCheck.php | 4 +- script/windows/14.0/update/index.php | 28 +++ tests/fixtures/WindowsUpdateCheck.14.0.json | 6 +- 4 files changed, 212 insertions(+), 4 deletions(-) create mode 100644 schemas/windows-update/17.0/sample.json diff --git a/schemas/windows-update/17.0/sample.json b/schemas/windows-update/17.0/sample.json new file mode 100644 index 0000000..95426a0 --- /dev/null +++ b/schemas/windows-update/17.0/sample.json @@ -0,0 +1,178 @@ +{ + "msi": { + "name": "Keyman for Windows MSI installer", + "version": "18.0.245", + "date": "2025-12-03", + "platform": "win", + "stability": "stable", + "file": "keymandesktop.msi", + "md5": "98EABC9C8F4803B11C5EF2EF64F0D802", + "type": "msi", + "build": "245", + "size": 110870528, + "url": "https://downloads.keyman.com/windows/stable/18.0.245/keymandesktop.msi" + }, + "setup": { + "name": "Keyman for Windows setup bootstrap", + "version": "18.0.245", + "date": "2025-12-03", + "platform": "win", + "stability": "stable", + "file": "setup.exe", + "md5": "C32FD8926909F1E447BBC72AF8B16380", + "type": "exe", + "build": "245", + "size": 4032904, + "url": "https://downloads.keyman.com/windows/stable/18.0.245/setup.exe" + }, + "bundle": { + "name": "Keyman for Windows", + "version": "18.0.245", + "date": "2025-12-03", + "platform": "win", + "stability": "stable", + "file": "keyman-18.0.245.exe", + "md5": "2BE0290AF6DF49BA736CEDCE50D1E763", + "type": "exe", + "build": "245", + "size": 111668424, + "url": "https://downloads.keyman.com/windows/stable/18.0.245/keyman-18.0.245.exe" + }, + "keyboards": { + "khmer_angkor": { + "id": "khmer_angkor", + "name": "Khmer Angkor", + "license": "mit", + "authorName": "Makara Sok", + "authorEmail": "makara_sok@sil.org", + "description": "

Khmer Unicode keyboard layout based on the NiDA keyboard layout.\nAutomatically corrects many common keying errors.

", + "languages": { + "km": { + "examples": [ + { + "keys": "x j m E r", + "note": "Name of language", + "text": "\u1781\u17d2\u1798\u17c2\u179a" + } + ], + "font": { + "family": "Busra", + "source": [ + "Busra-Regular.ttf" + ] + }, + "oskFont": { + "family": "KbdKhmr", + "source": [ + "KbdKhmr.ttf" + ] + }, + "languageName": "Khmer", + "displayName": "Khmer" + } + }, + "lastModifiedDate": "2025-12-16T08:51:56.000Z", + "packageFilename": "khmer_angkor.kmp", + "packageFileSize": 2541924, + "jsFilename": "khmer_angkor.js", + "jsFileSize": 74154, + "packageIncludes": [ + "visualKeyboard", + "welcome", + "documentation", + "fonts" + ], + "version": "2.4", + "encodings": [ + "unicode" + ], + "platformSupport": { + "windows": "full", + "macos": "full", + "linux": "full", + "desktopWeb": "full", + "ios": "full", + "android": "full", + "mobileWeb": "full" + }, + "minKeymanVersion": "10.0", + "sourcePath": "release/k/khmer_angkor", + "helpLink": "https://help.keyman.com/keyboard/khmer_angkor", + "related": { + "khmer10": { + "deprecates": true + }, + "basic_kbdkni": { + "deprecates": false + } + }, + "url": "https://keyman.com/go/package/download/khmer_angkor?version=2.4&platform=windows&tier=stable&update=0" + }, + "sil_ipa": { + "id": "sil_ipa", + "name": "IPA (SIL)", + "license": "mit", + "authorName": "Martin Hosken, Lorna Evans", + "authorEmail": "fonts@sil.org", + "description": "

The keyboard layout is described in terms of an IPA chart rather than a\nkeyboard. This is because many base characters are typed as a sequence\nof a letter followed by one of <, > or = which are characters used to\nchange a base character to another base character. Diacritics are typed\nas sequences of an appropriate key.

", + "languages": { + "und-latn": { + "examples": [], + "font": { + "family": "Charis", + "source": [ + "Charis-Regular.ttf" + ] + }, + "oskFont": { + "family": "Charis", + "source": [ + "Charis-Regular.ttf" + ] + }, + "languageName": "Undetermined", + "scriptName": "Latin", + "displayName": "Undetermined (Latin)" + } + }, + "lastModifiedDate": "2025-06-09T21:19:30.000Z", + "packageFilename": "sil_ipa.kmp", + "packageFileSize": 3626354, + "jsFilename": "sil_ipa.js", + "jsFileSize": 88661, + "packageIncludes": [ + "documentation", + "welcome", + "fonts" + ], + "version": "2.0.2", + "encodings": [ + "unicode" + ], + "platformSupport": { + "windows": "full", + "macos": "full", + "linux": "full", + "desktopWeb": "full", + "ios": "full", + "android": "full", + "mobileWeb": "full" + }, + "minKeymanVersion": "17.0", + "sourcePath": "release/sil/sil_ipa", + "helpLink": "https://help.keyman.com/keyboard/sil_ipa", + "related": { + "ipauni11": { + "deprecates": true + }, + "ipauni111": { + "deprecates": true + }, + "ipa93_km5": { + "deprecates": false + } + }, + "url": "https://keyman.com/go/package/download/sil_ipa?version=2.0.2&platform=windows&tier=stable&update=0" + } + } +} \ No newline at end of file diff --git a/script/windows/14.0/update/WindowsUpdateCheck.php b/script/windows/14.0/update/WindowsUpdateCheck.php index 59b0f8b..94faff1 100644 --- a/script/windows/14.0/update/WindowsUpdateCheck.php +++ b/script/windows/14.0/update/WindowsUpdateCheck.php @@ -133,7 +133,9 @@ private function CheckVersionResponse($tier, $tiers, $InstalledVersion, $regex) } } - $filedata->url = KeymanHosts::Instance()->downloads_keyman_com . "/windows/$tier/{$filedata->version}/{$file}"; + $filedata->url = + KeymanHosts::Instance()->keyman_com . "/go/app/download/windows/{$filedata->version}/$tier?url=" . + rawurlencode(KeymanHosts::Instance()->downloads_keyman_com . "/windows/$tier/{$filedata->version}/{$file}"); return $filedata; } } diff --git a/script/windows/14.0/update/index.php b/script/windows/14.0/update/index.php index 9a536c3..f79fcb3 100644 --- a/script/windows/14.0/update/index.php +++ b/script/windows/14.0/update/index.php @@ -1,4 +1,32 @@