Skip to content

Commit 14b3d66

Browse files
authored
Merge pull request #634 from keymanapp/feat/app-download-stats
feat: collect app download statistics
2 parents e02cbed + 767a2b4 commit 14b3d66

File tree

5 files changed

+112
-47
lines changed

5 files changed

+112
-47
lines changed

_includes/includes/ui/downloads.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ function downloadLargeCTA($product, $platform, $tier, $filepattern) {
7373
$fileData = $versions->$platform->$tier->files->$file;
7474
$fileSize = formatSizeUnits($fileData->size);
7575
$host = KeymanHosts::Instance()->downloads_keyman_com;
76+
$downloadSiteUrl = "$host/$platform/$tier/{$versions->$platform->$tier->version}/$file";
77+
$downloadUrl = htmlentities("/go/app/download/$platform/{$versions->$platform->$tier->version}/$tier?url=".
78+
rawurlencode($downloadSiteUrl));
7679

7780
echo <<<END
78-
<div class="download-cta-big selected" id="cta-big-Windows" data-url='$host/$platform/$tier/{$versions->$platform->$tier->version}/$file' data-version='{$versions->$platform->$tier->version}'>
81+
<div class="download-cta-big selected" id="cta-big-Windows" data-url='$downloadUrl' data-version='{$versions->$platform->$tier->version}'>
7982
<div class="download-stable-email">
8083
<h3>$product {$versions->$platform->$tier->version}</h3>
8184
<p>Released: {$fileData->date}</p>

go/.htaccess

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ RewriteRule "^package/download/(keyboard/)?([^/]+)$" "package/download.php?type=
3131
# keyboard/id/share
3232
RedirectMatch "/go/keyboard/([^/?]+)/share$" "/keyboards/share/$1"
3333

34+
#
35+
# go/app/download
36+
#
37+
38+
# download-app?url=...
39+
RewriteRule "^app/download/([^/?]+)/([^/?]+)/(.*)" "app/download.php?product=$1&version=$2&tier=$3" [END,QSA]
40+
3441
#
3542
# Non-app-specific endpoints
3643
#

go/app/download.php

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Keyman\Site\com\keyman;
5+
6+
require __DIR__ . '/../../_includes/autoload.php';
7+
8+
const DEBUG=0;
9+
10+
use Keyman\Site\com\keyman\KeymanComSentry;
11+
use Keyman\Site\com\keyman\Validation;
12+
use Keyman\Site\Common\KeymanHosts;
13+
use Keyman\Site\Common\JsonApiFailure;
14+
15+
$env = getenv();
16+
KeymanComSentry::init();
17+
18+
AppDownloadPage::redirect_to_file(
19+
isset($_REQUEST['url']) ? $_REQUEST['url'] : null,
20+
isset($_REQUEST['product']) ? $_REQUEST['product'] : null,
21+
isset($_REQUEST['version']) ? $_REQUEST['version'] : null,
22+
isset($_REQUEST['tier']) ? $_REQUEST['tier'] : null,
23+
// TODO: should we? or just leave it encoded in URL for now?
24+
// isset($_REQUEST['bcp47']) ? $_REQUEST['bcp47'] : null,
25+
// isset($_REQUEST['update']) ? $_REQUEST['update'] : null
26+
);
27+
28+
class AppDownloadPage {
29+
30+
public static function redirect_to_file($url, $product, $version, $tier) {
31+
if(empty($url)) {
32+
JsonApiFailure::InvalidParameters("url");
33+
}
34+
35+
if(empty($product)) {
36+
$product = 'unknown';
37+
}
38+
39+
if(empty($version)) {
40+
$version = '0.0';
41+
}
42+
43+
if(empty($tier)) {
44+
$tier = 'stable';
45+
}
46+
47+
$tier = Validation::validate_tier($tier, 'stable');
48+
49+
$url_e = htmlentities($url);
50+
51+
if(DEBUG) {
52+
header('Content-Type: text/plain');
53+
}
54+
55+
if(KeymanHosts::Instance()->Tier() !== KeymanHosts::TIER_TEST) {
56+
self::report_app_download_event($product, $version, $tier);
57+
58+
if(DEBUG) {
59+
echo "\n\nLocation: $url\n";
60+
exit;
61+
}
62+
63+
// We don't do a redirect for Test tier because a test instance of the
64+
// downloads server is not available and so it gives us an error
65+
header("HTTP/1.1 302 Found");
66+
header("Cache-Control: no-store");
67+
header("Location: $url");
68+
}
69+
70+
echo "<a href='$url_e'>Download Link</a>";
71+
}
72+
73+
private static function report_app_download_event($product, $version, $tier) {
74+
global $env;
75+
$url = KeymanHosts::Instance()->SERVER_api_keyman_com . "/app-downloads-increment/".rawurlencode($product).
76+
"/".rawurlencode($version)."/".rawurlencode($tier);
77+
78+
if(KeymanHosts::Instance()->Tier() !== KeymanHosts::TIER_TEST) {
79+
if(KeymanHosts::Instance()->Tier() === KeymanHosts::TIER_DEVELOPMENT)
80+
$key = 'local';
81+
else
82+
$key = $env['API_KEYMAN_COM_INCREMENT_DOWNLOAD_KEY'];
83+
84+
$c = curl_init($url);
85+
curl_setopt($c, CURLOPT_HEADER, 0);
86+
curl_setopt($c, CURLOPT_POSTFIELDS, 'key='.rawurlencode($key));
87+
curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
88+
curl_setopt($c, CURLOPT_USERAGENT , "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
89+
$data = curl_exec($c);
90+
curl_close($c);
91+
92+
if(DEBUG) {
93+
var_dump("app-downloads-increment ($url):",$data);
94+
}
95+
} else
96+
$data = TRUE;
97+
return $data !== FALSE;
98+
}
99+
}

go/package/download.php

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -117,48 +117,3 @@ private static function report_download_event($id, $platform, $tier, $bcp47, $up
117117
return $data !== FALSE;
118118
}
119119
}
120-
121-
/**
122-
* Returns a GUIDv4 string
123-
*
124-
* Uses the best cryptographically secure method
125-
* for all supported pltforms with fallback to an older,
126-
* less secure version.
127-
* https://www.php.net/manual/en/function.com-create-guid.php#119168
128-
*
129-
* @param bool $trim
130-
* @return string
131-
*/
132-
function GUIDv4 ($trim = true)
133-
{
134-
// Windows
135-
if (function_exists('com_create_guid') === true) {
136-
if ($trim === true)
137-
return trim(com_create_guid(), '{}');
138-
else
139-
return com_create_guid();
140-
}
141-
142-
// OSX/Linux
143-
if (function_exists('openssl_random_pseudo_bytes') === true) {
144-
$data = openssl_random_pseudo_bytes(16);
145-
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
146-
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
147-
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
148-
}
149-
150-
// Fallback (PHP 4.2+)
151-
mt_srand((double)microtime() * 10000);
152-
$charid = strtolower(md5(uniqid(rand(), true)));
153-
$hyphen = chr(45); // "-"
154-
$lbrace = $trim ? "" : chr(123); // "{"
155-
$rbrace = $trim ? "" : chr(125); // "}"
156-
$guidv4 = $lbrace.
157-
substr($charid, 0, 8).$hyphen.
158-
substr($charid, 8, 4).$hyphen.
159-
substr($charid, 12, 4).$hyphen.
160-
substr($charid, 16, 4).$hyphen.
161-
substr($charid, 20, 12).
162-
$rbrace;
163-
return $guidv4;
164-
}

keyboards/install.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,12 @@ protected static function WriteWindowsBoxes() {
122122

123123
// This maps to buildStandardWindowsDownloadUrl in install.js (which we don't use here for those
124124
// few users who have JS disabled -- although this is not really a tested/supported scenario)
125-
$downloadLink = KeymanHosts::Instance()->downloads_keyman_com .
125+
$downloadServerLink = KeymanHosts::Instance()->downloads_keyman_com .
126126
"/windows/{$hu['tier']}/{$hu['version']}/keyman-setup" .
127127
self::BOOTSTRAP_SEPARATOR . "{$hu['id']}" .
128128
(empty($hu['bcp47']) ? "" : ".{$hu['bcp47']}") .
129129
".exe";
130+
$downloadLink = "/go/app/download/windows/{$hu['version']}/{$hu['tier']}?url=".rawurlencode($downloadServerLink);
130131

131132
$helpLink = KeymanHosts::Instance()->help_keyman_com . "/products/windows/current-version/start/download-and-install-keyman";
132133

0 commit comments

Comments
 (0)