mirror of
https://github.com/LBRYFoundation/lbry.com.git
synced 2025-08-23 17:47:26 +00:00
Merge branch 'roadmap'
* roadmap: spacing and formatting break out curl caching into separate class roadmap fixes roadmap gtg? nocache flag roadmap ready? light progress better roadmap styling? yep, I store images in Git, come at me @lyoshenka roadmap v1 working asana task loading + curl caching barest of bones
This commit is contained in:
commit
67cc0a2086
28 changed files with 3147 additions and 226 deletions
|
@ -85,6 +85,7 @@ class Controller
|
|||
$router->get(['/osx', 'get-osx'], 'DownloadActions::executeGet');
|
||||
$router->get(['/android', 'get-android'], 'DownloadActions::executeGet');
|
||||
$router->get(['/ios', 'get-ios'], 'DownloadActions::executeGet');
|
||||
$router->get('/roadmap', 'ContentActions::executeRoadmap');
|
||||
|
||||
$router->get(['/press-kit.zip', 'press-kit'], 'ContentActions::executePressKit');
|
||||
|
||||
|
@ -114,9 +115,9 @@ class Controller
|
|||
'/deck.pdf' => 'https://www.dropbox.com/s/0xj4vgucsbi8rtv/lbry-deck.pdf?dl=1',
|
||||
'/pln.pdf' => 'https://www.dropbox.com/s/uevjrwnyr672clj/lbry-pln.pdf?dl=1',
|
||||
'/plan.pdf' => 'https://www.dropbox.com/s/uevjrwnyr672clj/lbry-pln.pdf?dl=1',
|
||||
'/get/lbry.dmg' => DownloadActions::getDownloadUrl(DownloadActions::OS_OSX) ?: '/get',
|
||||
'/get/lbry.deb' => DownloadActions::getDownloadUrl(DownloadActions::OS_LINUX) ?: '/get',
|
||||
'/get/lbry.msi' => DownloadActions::getDownloadUrl(DownloadActions::OS_WINDOWS) ?: '/get',
|
||||
'/get/lbry.dmg' => GitHub::getDownloadUrl(OS::OS_OSX) ?: '/get',
|
||||
'/get/lbry.deb' => GitHub::getDownloadUrl(OS::OS_LINUX) ?: '/get',
|
||||
'/get/lbry.msi' => GitHub::getDownloadUrl(OS::OS_WINDOWS) ?: '/get',
|
||||
];
|
||||
|
||||
foreach ([302 => $tempRedirects, 301 => $permanentRedirects] as $code => $redirects)
|
||||
|
|
|
@ -69,7 +69,6 @@ class ContentActions extends Actions
|
|||
]];
|
||||
}
|
||||
|
||||
|
||||
public static function executeFaq(string $slug = null): array
|
||||
{
|
||||
Response::enableHttpCache();
|
||||
|
@ -190,6 +189,33 @@ class ContentActions extends Actions
|
|||
]];
|
||||
}
|
||||
|
||||
public static function executeRoadmap()
|
||||
{
|
||||
$cache = !Request::getParam('nocache');
|
||||
$githubItems = Github::listRoadmapChangesets($cache);
|
||||
|
||||
$projectMaxVersions = [];
|
||||
$closedGroups = [];
|
||||
foreach($githubItems as $group => $items)
|
||||
{
|
||||
if ($items)
|
||||
{
|
||||
$lastItem = end($items);
|
||||
$project = $lastItem['project'];
|
||||
if (!isset($projectMaxVersions[$project]) || $lastItem['version'] > $projectMaxVersions[$project])
|
||||
{
|
||||
$projectMaxVersions[$project] = $lastItem['version'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$items = array_merge($githubItems, Asana::listRoadmapTasks($cache));
|
||||
return ['content/roadmap', [
|
||||
'projectMaxVersions' => $projectMaxVersions,
|
||||
'items' => $items
|
||||
]];
|
||||
}
|
||||
|
||||
public static function executePressKit(): array
|
||||
{
|
||||
$zipFileName = 'lbry-press-kit-' . date('Y-m-d') . '.zip';
|
||||
|
|
|
@ -2,23 +2,6 @@
|
|||
|
||||
class DownloadActions extends Actions
|
||||
{
|
||||
const OS_ANDROID = 'android',
|
||||
OS_IOS = 'ios',
|
||||
OS_LINUX = 'linux',
|
||||
OS_OSX = 'osx',
|
||||
OS_WINDOWS = 'windows';
|
||||
|
||||
public static function getOses()
|
||||
{
|
||||
return [
|
||||
static::OS_WINDOWS => ['/windows', 'Windows', 'icon-windows', '_windows'],
|
||||
static::OS_OSX => ['/osx', 'OS X', 'icon-apple', '_osx'],
|
||||
static::OS_LINUX => ['/linux', 'Linux', 'icon-linux', '_linux'],
|
||||
static::OS_ANDROID => ['/android', 'Android', 'icon-android', '_android'],
|
||||
static::OS_IOS => ['/ios', 'iOS', 'icon-mobile', '_ios']
|
||||
];
|
||||
}
|
||||
|
||||
public static function executeGet()
|
||||
{
|
||||
$email = Request::getParam('e');
|
||||
|
@ -52,7 +35,7 @@ class DownloadActions extends Actions
|
|||
return ['download/get'];
|
||||
}
|
||||
|
||||
$osChoices = static::getOses();
|
||||
$osChoices = OS::getAll();
|
||||
$os = static::guessOs();
|
||||
|
||||
if ($os && isset($osChoices[$os]))
|
||||
|
@ -64,7 +47,7 @@ class DownloadActions extends Actions
|
|||
'osIcon' => $osIcon,
|
||||
'prefineryUser' => $user ?: [],
|
||||
'downloadHtml' => View::exists('download/' . $partial) ?
|
||||
View::render('download/' . $partial, ['downloadUrl' => static::getDownloadUrl($os)]) :
|
||||
View::render('download/' . $partial, ['downloadUrl' => Github::getDownloadUrl($os)]) :
|
||||
false
|
||||
]];
|
||||
}
|
||||
|
@ -122,8 +105,8 @@ class DownloadActions extends Actions
|
|||
public static function prepareListPartial(array $vars)
|
||||
{
|
||||
return $vars + ['osChoices' => isset($vars['excludeOs']) ?
|
||||
array_diff_key(static::getOses(), [$vars['excludeOs'] => null]) :
|
||||
static::getOses()
|
||||
array_diff_key(OS::getAll(), [$vars['excludeOs'] => null]) :
|
||||
OS::getAll()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -164,7 +147,7 @@ class DownloadActions extends Actions
|
|||
{
|
||||
//if exact OS is requested, use that
|
||||
$uri = Request::getRelativeUri();
|
||||
foreach (static::getOses() as $os => $osChoice)
|
||||
foreach (OS::getAll() as $os => $osChoice)
|
||||
{
|
||||
if ($osChoice[0] == $uri)
|
||||
{
|
||||
|
@ -181,68 +164,15 @@ class DownloadActions extends Actions
|
|||
$ua = Request::getUserAgent();
|
||||
if (stripos($ua, 'OS X') !== false)
|
||||
{
|
||||
return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? static::OS_IOS : static::OS_OSX;
|
||||
return strpos($ua, 'iPhone') !== false || stripos($ua, 'iPad') !== false ? OS::OS_IOS : OS::OS_OSX;
|
||||
}
|
||||
if (stripos($ua, 'Linux') !== false || strpos($ua, 'X11') !== false)
|
||||
{
|
||||
return strpos($ua, 'Android') !== false ? static::OS_ANDROID : static::OS_LINUX;
|
||||
return strpos($ua, 'Android') !== false ? OS::OS_ANDROID : OS::OS_LINUX;
|
||||
}
|
||||
if (stripos($ua, 'Windows') !== false)
|
||||
{
|
||||
return static::OS_WINDOWS;
|
||||
return OS::OS_WINDOWS;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getDownloadUrl($os, $useCache = true)
|
||||
{
|
||||
if (!in_array($os, array_keys(static::getOses())))
|
||||
{
|
||||
throw new DomainException('Unknown OS');
|
||||
}
|
||||
|
||||
$apc = $useCache && extension_loaded('apc') && ini_get('apc.enabled');
|
||||
$key = 'lbry_release_data2';
|
||||
$releaseData = null;
|
||||
|
||||
if ($apc)
|
||||
{
|
||||
$releaseData = apc_fetch($key);
|
||||
}
|
||||
|
||||
if (!$releaseData)
|
||||
{
|
||||
try
|
||||
{
|
||||
$releaseData =
|
||||
json_decode(Curl::get('https://api.github.com/repos/lbryio/lbry/releases/latest', [], ['user_agent' => 'LBRY']), true);
|
||||
if ($apc)
|
||||
{
|
||||
apc_store($key, $releaseData, 600); // cache for 10 min
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!$releaseData)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($releaseData['assets'] as $asset)
|
||||
{
|
||||
if (
|
||||
($os == static::OS_LINUX && substr($asset['name'], -4) == '.deb') ||
|
||||
($os == static::OS_OSX && substr($asset['name'], -4) == '.dmg') ||
|
||||
($os == static::OS_WINDOWS && substr($asset['name'], -4) == '.msi')
|
||||
)
|
||||
{
|
||||
return $asset['browser_download_url'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,3 +248,6 @@ title:
|
|||
publish: Publish
|
||||
refer: Share and Earn
|
||||
what: "Art in the Internet Age: An Introduction to LBRY"
|
||||
roadmap:
|
||||
title: LBRY Roadmap
|
||||
description: See past and future progress on development of LBRY, the world's first user-controlled digital marketplace.
|
15
lib/cache/Apc.class.php
vendored
Normal file
15
lib/cache/Apc.class.php
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: kauffj
|
||||
* Date: 8/13/16
|
||||
* Time: 2:42 PM
|
||||
*/
|
||||
class Apc
|
||||
{
|
||||
public static function isEnabled()
|
||||
{
|
||||
return extension_loaded('apc') && ini_get('apc.enabled');
|
||||
}
|
||||
}
|
85
lib/thirdparty/Asana.class.php
vendored
Normal file
85
lib/thirdparty/Asana.class.php
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
class Asana
|
||||
{
|
||||
protected static $curlOptions = ['json_response' => true, 'cache' => true, 'timeout' => 10];
|
||||
|
||||
public static function listRoadmapTasks($cache = true)
|
||||
{
|
||||
// Use print_r(static::get('/projects')) to get project IDs
|
||||
|
||||
$projects = [
|
||||
158602294500138 => ['LBRY Browser', 'https://github.com/lbryio/lbry-web-ui'],
|
||||
158602294500137 => ['LBRY Data Network', 'https://github.com/lbryio/lbry'],
|
||||
161514803479899 => ['Blockchain and Wallets', 'https://github.com/lbryio/lbrycrd'],
|
||||
136290697597644 => ['Integration and Building', null],
|
||||
158602294500249 => ['Documentation', null],
|
||||
];
|
||||
|
||||
$tasks = [];
|
||||
|
||||
$tags = [
|
||||
192699565737944 => 'Ongoing',
|
||||
192699565737946 => 'Upcoming',
|
||||
192699565737948 => 'Future'
|
||||
];
|
||||
|
||||
foreach ($tags as $tagId => $tagLabel)
|
||||
{
|
||||
$taggedTasks = static::get('/tags/' . $tagId . '/tasks', ['completed_since' => 'now'], $cache);
|
||||
foreach ($taggedTasks as $task)
|
||||
{
|
||||
$fullTask = static::get('/tasks/' . $task['id']);
|
||||
$projectId = $fullTask['memberships'][0]['project']['id'] ?? null;
|
||||
if ($fullTask['name'] && $projectId && isset($projects[$projectId]))
|
||||
{
|
||||
list($projectName, $projectUrl) = $projects[$projectId];
|
||||
$tasks[$tagLabel][] = array_intersect_key($fullTask, ['name' => null]) + [
|
||||
'project_label' => $projectName,
|
||||
'badge' => $projectName,
|
||||
'date' => $fullTask['due_on'] ?? null,
|
||||
'body' => $fullTask['notes'],
|
||||
'url' => $projectUrl,
|
||||
'group' => $tagLabel,
|
||||
'assignee' => $fullTask['assignee'] ? ucwords($fullTask['assignee']['name']) : ''
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tasks as &$groupTasks)
|
||||
{
|
||||
usort($groupTasks, function ($tA, $tB)
|
||||
{
|
||||
if ($tA['group'] != $tB['group'])
|
||||
{
|
||||
return $tA['group'] <= $tB['group'] ? -1 : 1;
|
||||
}
|
||||
if ($tA['date'] xor $tB['date'])
|
||||
{
|
||||
return $tA['date'] ? -1 : 1;
|
||||
}
|
||||
return $tA['date'] < $tB['date'] ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
return $tasks;
|
||||
}
|
||||
|
||||
protected static function get($endpoint, array $data = [], $cache = true)
|
||||
{
|
||||
$apiKey = Config::get('asana_key');
|
||||
|
||||
$options = [
|
||||
'headers' => ['Authorization: Bearer ' . $apiKey],
|
||||
'cache' => $cache
|
||||
] + static::$curlOptions;
|
||||
|
||||
$responseData = CurlWithCache::get('https://app.asana.com/api/1.0' . $endpoint, $data, $options);
|
||||
return $responseData['data'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
class AsanaException extends Exception
|
||||
{
|
||||
}
|
127
lib/thirdparty/Github.class.php
vendored
Normal file
127
lib/thirdparty/Github.class.php
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
class Github
|
||||
{
|
||||
public static function getDownloadUrl($os, $cache = true)
|
||||
{
|
||||
if (!in_array($os, array_keys(OS::getAll())))
|
||||
{
|
||||
throw new DomainException('Unknown OS');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$releaseData = static::get('/repos/lbryio/lbry/releases/latest', $cache);
|
||||
foreach ($releaseData['assets'] as $asset)
|
||||
{
|
||||
if (
|
||||
($os == OS::OS_LINUX && in_array($asset['content_type'], ['application/x-debian-package', 'application/x-deb'])) ||
|
||||
($os == OS::OS_OSX && in_array($asset['content_type'], ['application/x-diskcopy', 'application/x-apple-diskimage'])) ||
|
||||
($os == OS::OS_WINDOWS && substr($asset['name'], -4) == '.msi')
|
||||
)
|
||||
{
|
||||
return $asset['browser_download_url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function get($endpoint, $cache = true)
|
||||
{
|
||||
return CurlWithCache::get('https://api.github.com' . $endpoint, [],
|
||||
['user_agent' => 'LBRY', 'json_response' => true, 'cache' => $cache]);
|
||||
}
|
||||
|
||||
public static function listRoadmapChangesets($cache = true)
|
||||
{
|
||||
$sets = [];
|
||||
$allReleases = [];
|
||||
|
||||
$projects = [
|
||||
'lbry' => ''
|
||||
];
|
||||
|
||||
foreach ($projects as $project => $label)
|
||||
{
|
||||
$page = 1;
|
||||
do
|
||||
{
|
||||
$releases = static::get('/repos/lbryio/' . $project . '/releases?page=' . $page, $cache);
|
||||
$page++;
|
||||
$allReleases = array_merge($allReleases, array_map(function ($release) use ($label, $project)
|
||||
{
|
||||
return $release + ['project_label' => $label, 'project' => $project];
|
||||
}, array_filter($releases, function ($release)
|
||||
{
|
||||
return isset($release['tag_name']) && isset($release['published_at']) && $release['published_at'] &&
|
||||
$release['tag_name'] != 'v0.4.0';
|
||||
})));
|
||||
} while (count($releases) >= 30);
|
||||
}
|
||||
|
||||
foreach ($allReleases as $release)
|
||||
{
|
||||
$group = null;
|
||||
$matches = null;
|
||||
if (isset($release['tag_name']) && preg_match('/v(\d+)\.(\d+).?(\d+)?/', $release['tag_name'], $matches))
|
||||
{
|
||||
$group = $release['project_label'] . ' v' . $matches[1] . '.' . $matches[2];
|
||||
}
|
||||
if ($group)
|
||||
{
|
||||
$sets[$group][] = array_intersect_key($release, [
|
||||
'prerelease' => null, 'tag_name' => null, 'published_at' => null, 'project' => null, 'project_label' => null
|
||||
]) + [
|
||||
'date' => date('Y-m-d', strtotime($release['created_at'])),
|
||||
//I thought published_at, but GitHub displays created_at and published_at is out of sync sometimes (0.3.2, 0.3.3)
|
||||
'name' => $release['name'] ?: $release['tag_name'],
|
||||
'github_url' => $release['url'],
|
||||
'major_version' => $matches[1],
|
||||
'minor_version' => $matches[2],
|
||||
'patch_version' => isset($matches[3]) ? $matches[3] : null,
|
||||
'version' => $matches[1] . '.' . $matches[2] . '.' . (isset($matches[3]) ? $matches[3] : ''),
|
||||
'body' => ParsedownExtra::instance()->text($release['body'])
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
uasort($sets, function ($sA, $sB)
|
||||
{
|
||||
if ($sA[0]['project'] != $sB[0]['project'])
|
||||
{
|
||||
return $sA[0]['project'] < $sB[0]['project'] ? -1 : 1;
|
||||
}
|
||||
if ($sA[0]['major_version'] != $sB[0]['major_version'])
|
||||
{
|
||||
return $sA[0]['major_version'] < $sB[0]['major_version'] ? -1 : 1;
|
||||
}
|
||||
if ($sA[0]['minor_version'] != $sB[0]['minor_version'])
|
||||
{
|
||||
return $sA[0]['minor_version'] < $sB[0]['minor_version'] ? -1 : 1;
|
||||
}
|
||||
return $sA[0]['patch_version'] < $sB[0]['patch_version'] ? -1 : 1;
|
||||
});
|
||||
|
||||
foreach ($sets as $group => &$groupSet)
|
||||
{
|
||||
usort($groupSet, function ($rA, $rB) {
|
||||
if ($rA['major_version'] != $rB['major_version'])
|
||||
{
|
||||
return $rA['major_version'] < $rB['major_version'] ? -1 : 1;
|
||||
}
|
||||
if ($rA['minor_version'] != $rB['minor_version'])
|
||||
{
|
||||
return $rA['minor_version'] < $rB['minor_version'] ? -1 : 1;
|
||||
}
|
||||
return $rA['patch_version'] < $rB['patch_version'] ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
return $sets;
|
||||
}
|
||||
}
|
9
lib/thirdparty/Prefinery.class.php
vendored
9
lib/thirdparty/Prefinery.class.php
vendored
|
@ -17,13 +17,13 @@ class Prefinery
|
|||
'Accept: application/json',
|
||||
'Content-type: application/json'
|
||||
],
|
||||
'json_data' => true
|
||||
'json_data' => true,
|
||||
'json_response' => true
|
||||
];
|
||||
|
||||
|
||||
public static function findUser($emailOrId, $useApc = true)
|
||||
{
|
||||
$apcEnabled = extension_loaded('apc') && ini_get('apc.enabled');
|
||||
$apcEnabled = Apc::isEnabled();
|
||||
if ($useApc && $apcEnabled)
|
||||
{
|
||||
$cached = apc_fetch('prefinery-user-' . $emailOrId, $success);
|
||||
|
@ -113,8 +113,7 @@ class Prefinery
|
|||
{
|
||||
throw new PrefineryException('Update tester must be called with a tester id');
|
||||
}
|
||||
$apcEnabled = extension_loaded('apc') && ini_get('apc.enabled');
|
||||
if ($apcEnabled)
|
||||
if (Apc::isEnabled())
|
||||
{
|
||||
apc_delete('prefinery-user-' . $testerData['id']);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
class Curl
|
||||
{
|
||||
const GET = 'GET',
|
||||
POST = 'POST',
|
||||
PUT = 'PUT';
|
||||
const
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE';
|
||||
|
||||
|
||||
public static function get($url, $params = [], $options = [])
|
||||
{
|
||||
|
@ -24,6 +27,12 @@ class Curl
|
|||
return $body;
|
||||
}
|
||||
|
||||
public static function delete($url, $params = [], $options = [])
|
||||
{
|
||||
list ($status, $headers, $body) = static::doCurl(static::DELETE, $url, $params, $options);
|
||||
return $body;
|
||||
}
|
||||
|
||||
public static function doCurl($method, $url, $params = [], $options = [])
|
||||
{
|
||||
$defaults = [
|
||||
|
@ -36,6 +45,7 @@ class Curl
|
|||
'password' => null,
|
||||
'cookie' => null,
|
||||
'json_data' => false,
|
||||
'json_response' => false,
|
||||
'retry' => false,
|
||||
];
|
||||
|
||||
|
@ -45,13 +55,13 @@ class Curl
|
|||
throw new DomainException('Invalid curl options: ' . join(', ', array_keys($invalid)));
|
||||
}
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
|
||||
if (!in_array($method, [static::GET, static::POST, static::PUT]))
|
||||
{
|
||||
throw new DomainException('Invalid method: ' . $method);
|
||||
}
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
|
||||
if ($options['headers'] && $options['headers'] !== array_values($options['headers'])) // associative array
|
||||
{
|
||||
throw new DomainException('Headers must not be an associative array. Its a simple array with values of the form "Header: value"');
|
||||
|
@ -137,7 +147,16 @@ class Curl
|
|||
return strlen($h);
|
||||
});
|
||||
|
||||
$responseContent = curl_exec($ch);
|
||||
$rawResponse = curl_exec($ch);
|
||||
|
||||
if ($options['json_response'])
|
||||
{
|
||||
$responseContent = $rawResponse ? json_decode($rawResponse, true) : [];
|
||||
}
|
||||
else
|
||||
{
|
||||
$responseContent = $rawResponse;
|
||||
}
|
||||
|
||||
$statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
|
|
32
lib/tools/CurlWithCache.class.php
Normal file
32
lib/tools/CurlWithCache.class.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
class CurlWithCache extends Curl
|
||||
{
|
||||
const DEFAULT_CACHE = 600000;
|
||||
|
||||
public static function doCurl($method, $url, $params = [], $options = [])
|
||||
{
|
||||
$useCache = ($options['cache'] ?? true) && Apc::isEnabled();
|
||||
$cacheTimeout = is_numeric($options['cache'] ?? null) ? $options['cache'] : static::DEFAULT_CACHE;
|
||||
unset($options['cache']);
|
||||
|
||||
if ($useCache)
|
||||
{
|
||||
$cacheKey = md5('z' . $url . $method . serialize($options) . serialize($params));
|
||||
$cachedData = apc_fetch($cacheKey);
|
||||
if ($cachedData)
|
||||
{
|
||||
return $cachedData;
|
||||
}
|
||||
}
|
||||
|
||||
$response = parent::doCurl($method, $url, $params, $options);
|
||||
|
||||
if ($useCache)
|
||||
{
|
||||
apc_store($cacheKey, $response, $cacheTimeout);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
23
lib/tools/OS.class.php
Normal file
23
lib/tools/OS.class.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
class OS
|
||||
{
|
||||
const OS_ANDROID = 'android',
|
||||
OS_IOS = 'ios',
|
||||
OS_LINUX = 'linux',
|
||||
OS_OSX = 'osx',
|
||||
OS_WINDOWS = 'windows';
|
||||
|
||||
public static function getAll()
|
||||
{
|
||||
//url, English name, icon class, partial name
|
||||
//yes, this is probably a bad pattern
|
||||
return [
|
||||
OS::OS_WINDOWS => ['/windows', 'Windows', 'icon-windows', '_windows'],
|
||||
OS::OS_OSX => ['/osx', 'OS X', 'icon-apple', '_osx'],
|
||||
OS::OS_LINUX => ['/linux', 'Linux', 'icon-linux', '_linux'],
|
||||
OS::OS_ANDROID => ['/android', 'Android', 'icon-android', '_android'],
|
||||
OS::OS_IOS => ['/ios', 'iOS', 'icon-mobile', '_ios']
|
||||
];
|
||||
}
|
||||
}
|
2485
lib/vendor/MCAPI.class.php
vendored
Normal file
2485
lib/vendor/MCAPI.class.php
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -71,7 +71,7 @@
|
|||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p><em>{{bounty.list.noresults}}</em></p>
|
||||
<p><em class="no-results">{{bounty.list.noresults}}</em></p>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
</main>
|
||||
|
|
68
view/template/content/roadmap.php
Normal file
68
view/template/content/roadmap.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php Response::setMetaDescription(__('roadmap.description')) ?>
|
||||
<?php Response::addJsAsset('/js/roadmap.js') ?>
|
||||
<?php NavActions::setNavUri('/learn') ?>
|
||||
<?php echo View::render('nav/_header', ['isDark' => false]) ?>
|
||||
<?php js_start() ?>
|
||||
lbry.roadmap('#project-roadmap');
|
||||
<?php js_end() ?>
|
||||
<main>
|
||||
<div class="hero hero-quote hero-img hero-img-short spacer1" title="Here Be Dragons" style="background-image: url(/img/here-be-dragons.jpg)">
|
||||
<div class="hero-content-wrapper">
|
||||
<div class="hero-content text-center">
|
||||
<h1 class="cover-title">{{roadmap.title}}</h1>
|
||||
<h2 class="cover-subtitle">Past successes and future plans for the journey into the land of dragons.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="max-width: 800px; margin: 0 auto">
|
||||
<div class="roadmap-container" id="project-roadmap">
|
||||
<div class="text-center"><a href="javascript:;" class="link-primary show-all-roadmap-groups">Show Earlier Releases</a></div>
|
||||
<?php foreach($items as $group => $groupItems): ?>
|
||||
<?php $lastItem = end($groupItems) ?>
|
||||
<?php $isOpen = !isset($lastItem['project']) || !isset($lastItem['version']) || $lastItem['version'] === $projectMaxVersions[$lastItem['project']] ?>
|
||||
<h2 class="roadmap-group-title" <?php echo !$isOpen ? 'style="display: none"' : '' ?>">
|
||||
<span class="roadmap-group-title-label">
|
||||
<?php echo $group ?> <?php echo isset($lastItem['version']) && $lastItem['version'] === $projectMaxVersions[$lastItem['project']] ? '(latest )' : '' ?>
|
||||
</span>
|
||||
</h2>
|
||||
<div class="roadmap-group <?php echo !$isOpen ? 'roadmap-group-closed' : '' ?>">
|
||||
<?php $lastItem = end($groupItems) ?>
|
||||
<?php $maxItems = isset($lastItem['version']) ? 3 : count($groupItems) ?>
|
||||
<?php $index = 0 ?>
|
||||
<?php if (count($groupItems) > $maxItems): ?>
|
||||
<div class="text-center spacer1"><a href="javascript:;" class="link-primary show-all-roadmap-group-items">Show All Items for <?php echo $group ?></a></div>
|
||||
<?php endif ?>
|
||||
<?php foreach($groupItems as $item): ?>
|
||||
<?php ++$index ?>
|
||||
<div class="roadmap-item" <?php echo $index <= count($groupItems) - $maxItems ? 'style="display: none"' : '' ?>>
|
||||
<?php if (isset($item['badge']) || isset($item['assignee'])): ?>
|
||||
<div>
|
||||
<?php if (isset($item['assignee'])): ?>
|
||||
<span class="roadmap-item-assignee"><?php echo $item['assignee'] ?></span>
|
||||
<?php endif ?>
|
||||
<?php if (isset($item['badge'])): ?>
|
||||
<span class="badge"><?php echo $item['badge'] ?></span><br/>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<?php endif ?>
|
||||
<h3 class="roadmap-item-title">
|
||||
<?php echo $item['name'] ?>
|
||||
</h3>
|
||||
<div class="roadmap-item-date">
|
||||
<?php echo $item['date'] ? date('m-d-Y', strtotime($item['date'])) : '' ?>
|
||||
</div>
|
||||
<div class="roadmap-item-content">
|
||||
<?php echo $item['body'] ?: '<em class="no-results">No description</em>' ?>
|
||||
<?php if (isset($item['github_url'])): ?>
|
||||
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<?php echo View::render('nav/_footer') ?>
|
|
@ -10,7 +10,6 @@
|
|||
<h1 class="cover-title">{{global.tagline}}</h1>
|
||||
<h2 class="cover-subtitle" style="max-width: 600px; margin-left: auto; margin-right: auto">{{global.sentence}}</h2>
|
||||
</div>
|
||||
|
||||
<div class="control-group spacer2 text-center">
|
||||
<div class="control-item">
|
||||
<a href="/get" class="btn-primary">{{global.get}}</a>
|
||||
|
|
BIN
web/img/facebook-cover.png
Normal file
BIN
web/img/facebook-cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 788 KiB |
BIN
web/img/here-be-dragons.jpg
Normal file
BIN
web/img/here-be-dragons.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 414 KiB |
BIN
web/img/twitter-cover.png
Normal file
BIN
web/img/twitter-cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 829 KiB |
|
@ -19,8 +19,7 @@ jQuery.fn.extend({
|
|||
});
|
||||
|
||||
$(document).ready(function() {
|
||||
var body = $('body'),
|
||||
labelCycles = body.find('.label-cycle'); //should use better pattern but we have so little JS right now
|
||||
var body = $('body');
|
||||
|
||||
body.on('click', 'a', onAnchorClick);
|
||||
|
||||
|
@ -32,22 +31,6 @@ $(document).ready(function() {
|
|||
window.fbAsyncInit = function()
|
||||
{
|
||||
window.FB.Event.subscribe('edge.create', onFacebookLike);
|
||||
};
|
||||
|
||||
//$(window).scroll(onBodyScroll);
|
||||
|
||||
if (labelCycles.length)
|
||||
{
|
||||
setInterval(refreshLabelCycles,5000);
|
||||
labelCycles.each(function() {
|
||||
var labelCycle = $(this),
|
||||
maxHeight = Math.max.apply(Math, labelCycles.find('> *').map(function(){ return $(this).height(); }).get());
|
||||
if (maxHeight)
|
||||
{
|
||||
labelCycle.height(maxHeight);
|
||||
}
|
||||
labelCycle.addClass('label-cycle-init');
|
||||
});
|
||||
}
|
||||
|
||||
function onAnchorClick()
|
||||
|
@ -80,24 +63,6 @@ $(document).ready(function() {
|
|||
}
|
||||
}
|
||||
|
||||
function onBodyScroll()
|
||||
{
|
||||
var header = $('.header');
|
||||
if (header.hasClass('header-scroll'))
|
||||
{
|
||||
if (window.scrollY <= 1)
|
||||
{
|
||||
header.removeClass('header-light');
|
||||
header.addClass('header-dark');
|
||||
}
|
||||
else
|
||||
{
|
||||
header.removeClass('header-dark');
|
||||
header.addClass('header-light');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function resizeVideo(iframe)
|
||||
{
|
||||
var maxWidth = Math.min(iframe.offsetParent().width(), iframe.data('maxWidth')),
|
||||
|
@ -149,21 +114,4 @@ $(document).ready(function() {
|
|||
resizeVideo($(this));
|
||||
})
|
||||
});
|
||||
|
||||
function refreshLabelCycles()
|
||||
{
|
||||
labelCycles.each(function() {
|
||||
var labelCycle = $(this),
|
||||
activeLabel = labelCycle.find(':first-child');
|
||||
|
||||
activeLabel.fadeOut(function() {
|
||||
labelCycle.append(activeLabel);
|
||||
labelCycle.find(':first-child').fadeIn();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('#language-dropdown').on('change', function() {
|
||||
$(this).closest('form').submit();
|
||||
});
|
||||
});
|
||||
|
|
22
web/js/roadmap.js
Normal file
22
web/js/roadmap.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
lbry.roadmap = function(selector)
|
||||
{
|
||||
var roadmap = $(selector);
|
||||
roadmap.on('click', '.show-all-roadmap-groups', function() {
|
||||
roadmap.find('.roadmap-group-title').show();
|
||||
$(this).remove();
|
||||
});
|
||||
roadmap.on('click', '.show-all-roadmap-group-items', function() {
|
||||
$(this).closest('.roadmap-group').find('.roadmap-item').show();
|
||||
$(this).remove();
|
||||
});
|
||||
roadmap.on('click', '.roadmap-group-title', function() {
|
||||
$(this).next('.roadmap-group')
|
||||
.toggleClass('roadmap-group-closed').toggleClass('roadmap-group-open');
|
||||
// .find('.roadmap-item')
|
||||
// .toggleClass('roadmap-item-open', $(this).hasClass('roadmap-group-open'))
|
||||
// .toggleClass('roadmap-item-closed', $(this).hasClass('roadmap-group-closed'))
|
||||
});
|
||||
// roadmap.on('click', '.roadmap-item-header', function() {
|
||||
// $(this).closest('.roadmap-item').toggleClass('roadmap-item-open').toggleClass('roadmap-item-closed');
|
||||
// });
|
||||
}
|
|
@ -18,4 +18,8 @@ $badge-height: $spacing-vertical * 3/4;
|
|||
{
|
||||
background-color: $color-primary;
|
||||
}
|
||||
&.badge-info
|
||||
{
|
||||
background-color: $color-info-bg;
|
||||
}
|
||||
}
|
|
@ -42,10 +42,10 @@ h1, h2, h3, h4
|
|||
margin-bottom: $spacing-vertical / 2;
|
||||
margin-top: $spacing-vertical / 2;
|
||||
}
|
||||
h1 { font-size: 2.4em; }
|
||||
h2 { font-size: 2.0em; }
|
||||
h3 { font-size: 1.75em; }
|
||||
h4 { font-size: 1.4em; }
|
||||
h1 { font-size: $font-size-h1; }
|
||||
h2 { font-size: $font-size-h2; }
|
||||
h3 { font-size: $font-size-h3; }
|
||||
h4 { font-size: $font-size-h4; }
|
||||
nav
|
||||
{
|
||||
font-size: 1.1em;
|
||||
|
|
|
@ -108,74 +108,83 @@
|
|||
{
|
||||
margin-top: $spacing-vertical;
|
||||
}
|
||||
table
|
||||
}
|
||||
.post-content table, table.content
|
||||
{
|
||||
margin-bottom: $spacing-vertical;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
|
||||
th, td
|
||||
{
|
||||
margin-bottom: $spacing-vertical;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
|
||||
th, td
|
||||
padding: $spacing-vertical/2 8px;
|
||||
}
|
||||
th
|
||||
{
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
td
|
||||
{
|
||||
vertical-align: top;
|
||||
}
|
||||
thead th, > tr:first-child th
|
||||
{
|
||||
vertical-align: bottom;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
padding: $spacing-vertical/4+1 8px $spacing-vertical/4-2;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e2e2;
|
||||
img
|
||||
{
|
||||
padding: $spacing-vertical/2 8px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
th
|
||||
}
|
||||
td.center
|
||||
{
|
||||
vertical-align: middle;
|
||||
}
|
||||
tr.thead:not(:first-child) th
|
||||
{
|
||||
border-top: 1px solid #e2e2e2;
|
||||
}
|
||||
tfoot td
|
||||
{
|
||||
padding: $spacing-vertical / 2 8px;
|
||||
font-size: .85em;
|
||||
}
|
||||
tbody
|
||||
{
|
||||
tr
|
||||
{
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
td
|
||||
{
|
||||
vertical-align: top;
|
||||
}
|
||||
thead th, > tr:first-child th
|
||||
{
|
||||
vertical-align: bottom;
|
||||
font-weight: bold;
|
||||
font-size: 0.9em;
|
||||
padding: $spacing-vertical/4+1 8px $spacing-vertical/4-2;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e2e2;
|
||||
img
|
||||
&:nth-child(even):not(.odd)
|
||||
{
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
tr.thead:not(:first-child) th
|
||||
{
|
||||
border-top: 1px solid #e2e2e2;
|
||||
}
|
||||
tfoot td
|
||||
{
|
||||
padding: $spacing-vertical / 2 8px;
|
||||
font-size: .85em;
|
||||
}
|
||||
tbody
|
||||
{
|
||||
tr
|
||||
{
|
||||
&:nth-child(even):not(.odd)
|
||||
{
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
&:nth-child(odd):not(.even)
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
&.thead
|
||||
{
|
||||
background: none;
|
||||
}
|
||||
td
|
||||
{
|
||||
border: 0 none;
|
||||
}
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
&:nth-child(odd):not(.even)
|
||||
{
|
||||
background-color: white;
|
||||
}
|
||||
&.thead
|
||||
{
|
||||
background: none;
|
||||
}
|
||||
td
|
||||
{
|
||||
border: 0 none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child
|
||||
{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&:last-child
|
||||
{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.full-table
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ $color-light-alt: hsl(hue($color-primary), 15, 85);
|
|||
$color-text-dark: #000;
|
||||
$color-money: #216C2A;
|
||||
$color-meta-light: #505050;
|
||||
$color-meta-lighter: #999999;
|
||||
$color-info-bg: #3a779d;
|
||||
|
||||
$font-size: 16px;
|
||||
|
||||
|
@ -18,6 +20,11 @@ $max-post-content-width: 800px;
|
|||
$font-header: 'Raleway', sans-serif;
|
||||
$font-body: 'Raleway', sans-serif;
|
||||
|
||||
$font-size-h1: 2.4em;
|
||||
$font-size-h2: 2.0em;
|
||||
$font-size-h3: 1.75em;
|
||||
$font-size-h4: 1.4em;
|
||||
|
||||
$content-side-padding: 15px;
|
||||
|
||||
@mixin anchor($color)
|
||||
|
|
131
web/scss/_roadmap.scss
Normal file
131
web/scss/_roadmap.scss
Normal file
|
@ -0,0 +1,131 @@
|
|||
@import "global";
|
||||
|
||||
$color-roadmap-border: $color-primary;
|
||||
$width-roadmap: 4px;
|
||||
$width-roadmap-item: $width-roadmap - 1;
|
||||
$width-date: 120px;
|
||||
$radius-roadmap: 10px;
|
||||
|
||||
.roadmap-container
|
||||
{
|
||||
border-left: $width-roadmap solid $color-roadmap-border;
|
||||
margin: $spacing-vertical 0 $spacing-vertical $width-date;
|
||||
&:before, &:after
|
||||
{
|
||||
background: $color-roadmap-border;
|
||||
content: "";
|
||||
display: block;
|
||||
position: relative;
|
||||
height: $width-roadmap;
|
||||
left: -2 * ($width-roadmap + 1);
|
||||
width: $width-roadmap * 4;
|
||||
}
|
||||
&:before
|
||||
{
|
||||
top: -1 * ($width-roadmap);
|
||||
}
|
||||
&:after
|
||||
{
|
||||
bottom: -1 * ($width-roadmap);
|
||||
}
|
||||
}
|
||||
|
||||
.show-all-roadmap-groups
|
||||
{
|
||||
display: inline-block;
|
||||
margin: $spacing-vertical 0 0;
|
||||
}
|
||||
|
||||
.roadmap-group-closed
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
$title-spur: 8 * $width-roadmap;
|
||||
|
||||
.roadmap-group-title
|
||||
{
|
||||
&:before
|
||||
{
|
||||
border-top: $width-roadmap solid $color-roadmap-border;
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: $width-roadmap;
|
||||
margin-right: 10px;
|
||||
vertical-align: middle;
|
||||
width: $title-spur;
|
||||
}
|
||||
cursor: pointer;
|
||||
font-weight: 400;
|
||||
margin: $spacing-vertical * 2 0 $spacing-vertical * 1 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.roadmap-group-title-label
|
||||
{
|
||||
background: $color-roadmap-border;
|
||||
@include border-radius($radius-roadmap);
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
display: inline-block;
|
||||
letter-spacing: 0;
|
||||
padding: 0 10px;
|
||||
text-align: center;
|
||||
width: calc(100% - #{$title-spur} - 20px);
|
||||
}
|
||||
|
||||
.roadmap-item
|
||||
{
|
||||
$left-margin: 20px;
|
||||
$item-padding: $spacing-vertical / 2;
|
||||
margin-bottom: $spacing-vertical;
|
||||
position: relative;
|
||||
margin-left: $left-margin;
|
||||
position: relative;
|
||||
padding: $item-padding;
|
||||
background: #f2f2f2;
|
||||
@include border-radius($radius-roadmap);
|
||||
&:before {
|
||||
border-top: $width-roadmap-item solid $color-roadmap-border;
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -1 * $left-margin;
|
||||
top: 0.5 * ($font-size-h3) + $item-padding;
|
||||
display: inline-block;
|
||||
margin-top: $width-roadmap-item / 2;
|
||||
height: $width-roadmap-item;
|
||||
width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.roadmap-item-date
|
||||
{
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -1 * $width-date;
|
||||
color: $color-meta-light;
|
||||
line-height: 42px;
|
||||
}
|
||||
|
||||
.roadmap-item-title
|
||||
{
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.roadmap-item-content
|
||||
{
|
||||
font-size: 0.9em;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.roadmap-item-assignee
|
||||
{
|
||||
float: right;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
/*
|
||||
.roadmap-item-closed .roadmap-item-content
|
||||
{
|
||||
display: none;
|
||||
}*/
|
|
@ -58,19 +58,6 @@
|
|||
margin: 10px $spacing-vertical / 2 0 $spacing-vertical / 4;
|
||||
}
|
||||
}
|
||||
.label-cycle
|
||||
{
|
||||
visibility: hidden;
|
||||
display: block;
|
||||
&.label-cycle-init
|
||||
{
|
||||
visibility: visible;
|
||||
:not(:first-child)
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sale-level
|
||||
{
|
||||
|
|
|
@ -13,4 +13,5 @@
|
|||
@import "sale";
|
||||
@import "code";
|
||||
@import "blog";
|
||||
@import "bounty";
|
||||
@import "bounty";
|
||||
@import "roadmap";
|
Loading…
Add table
Reference in a new issue