Let there be code!

This commit is contained in:
Dale Davies
2022-02-04 09:53:55 +00:00
commit 46fffeb46e
35 changed files with 1297 additions and 0 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
*/vendor

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*/vendor
docker-compose.yaml

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
# Start with the official composer image, copy application files and install
# dependencies.
FROM composer AS composer
COPY web/ /app
RUN composer install --no-dev \
--optimize-autoloader \
--no-interaction \
--no-progress
# Switch to trafex/php-nginx image and copy application files into it.
FROM trafex/php-nginx
COPY --chown=nginx --from=composer /app /var/www/html
# The trafex/php-nginx image runs as "nobody" user so we need to switch to root
# so we can make changes inside the container.
USER root
# We need the following PHP extensions.
RUN apk add php8-fileinfo
# Create the cache directories.
RUN mkdir -p /var/www/cache/application \
&& chown nobody:nobody /var/www/cache/application \
&& mkdir -p /var/www/cache/icons \
&& chown nobody:nobody /var/www/cache/icons
# Switch back to the nobody user so we're not running as root forever.
USER nobody

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

100
web/assets/css/styles.css Normal file
View File

@@ -0,0 +1,100 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
color: #fff;
font-family: -apple-system,system-ui,Ubuntu,Roboto,"Open Sans","Segoe UI","Helvetica Neue";
font-size: 1em;
text-align: center;
background: #000;
}
.fixed {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
.background {
filter: brightness(0.55) blur(13px);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
transform: scale(1.07);
z-index: 1;
}
body::after {
content: '';
position:fixed;
top: 0;
left: 0;
right:0;
bottom:0;
background-image: url(../images/overlay.png);
opacity: 0.6;
z-index: 2;
}
.content {
z-index: 10;
display: flex;
flex-direction: column;
justify-content:center;
}
.greeting {
font-size: 2.5em;
text-transform: capitalize;
text-shadow: 1px 2px 14px #000000;
margin-bottom: 30px;
}
.sites, .sites li {
padding: 0;
margin: 0;
list-style-type: none;
font-weight: 300;
font-size: 14px;
}
.sites li {
display: inline-block;
margin-bottom:20px;
}
.sites li a {
color: inherit;
text-decoration: none;
}
.sites .icon {
display: block;
background-color: #fff;
width: 75px;
height: 75px;
border-radius: 10px;
border: .2em solid #fff;
box-shadow: 0 1px 5px rgba(0,0,0,.3);
margin: 0 11px 7px 11px;
padding: 15px;
}
.sites .icon:hover {
border-color: #007aff;
transition: border-color .1s;
}
.sites .icon img {
width:100%;
}
.sites .name {
display: block;
width: 100%;
max-height: 3.3em;
overflow: hidden;
word-wrap: break-word;
text-shadow: 1px 1px 2px #000000
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

1
web/assets/js/index.js Normal file
View File

@@ -0,0 +1 @@
console.log('yay');

11
web/background-css.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
// Provided by composer for psr-4 style autoloading.
require __DIR__ .'/vendor/autoload.php';
$config = new Jump\Config();
$backgroundfile = (new Jump\Background($config))->get_random_background_file();
$backgroundgradient = 'linear-gradient(to bottom, #FC466B40, #425df530)';
header('Content-Type: text/css');
echo '.background {background-image: '.$backgroundgradient.', url("'.$backgroundfile.'");}';

View File

@@ -0,0 +1,26 @@
<?php
namespace Jump;
class Background {
private string $backgroundsdirectory;
private array $backgroundfiles;
public function __construct(Config $config) {
$this->config = $config;
$this->backgroundsdirectory = $config->get('backgroundsdir');
$this->webaccessibledir = str_replace($config->get('wwwroot'), '', $config->get('backgroundsdir'));
$this->enumerate_files();
}
private function enumerate_files(): void {
$this->backgroundfiles = array_diff(scandir($this->backgroundsdirectory), array('..', '.'));
}
public function get_random_background_file(bool $includepath = true): string {
return ($includepath ? $this->webaccessibledir : '')
. '/'. $this->backgroundfiles[array_rand($this->backgroundfiles)];
}
}

77
web/classes/Cache.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
namespace Jump;
use Nette\Caching;
class Cache {
private Caching\Storages\FileStorage $storage;
/**
* The definition of various caches used throughout the application.
*
* @var array Multidimensional array
*/
private array $caches;
private Config $config;
/**
* Creates file storage for cache and initialises cache objects for each
* name/type specified in $caches definition.
*/
public function __construct(Config $config) {
$this->config = $config;
// Define the various caches used throughout the app.
$this->caches = [
'sites' => [
'cache' => null,
'expirationtype' => Caching\Cache::FILES,
'expirationparams' => $config->get('sitesfile')
],
'templates/sites' => [
'cache' => null,
'expirationtype' => Caching\Cache::FILES,
'expirationparams' => [
__DIR__.'/../config.php',
$config->get('sitesfile'),
$config->get('templatedir').'/sites.mustache'
]
]
];
// Inititalise file storage for cache using cachedir path from config.
$this->storage = new Caching\Storages\FileStorage($this->config->get('cachedir').'/application');
// Initialise a cache object for each cache name/type specified in caches array.
array_walk($this->caches, function(&$cachedef, $cachename) {
$cachedef['cache'] = new Caching\Cache($this->storage, $cachename);
});
}
/**
* Read the specified item from the cache or generate it, mostly a wrapper
* around Nette\Caching\Cache::load().
*
* @param string $cachename The name of a cache type, must match a key in $caches definition.
* @param callable $callback The code from which the result should be stored in cache.
* @return mixed The result of callback function retreieved from cache.
*/
public function load(string $cachename, callable $callback): mixed {
// If cachebypass has been set in config.php then just execute the callback.
if ($this->config->parse_bool($this->config->get('cachebypass'))) {
return $callback();
}
// We can only work with caches that have already been defined.
if (!array_key_exists($cachename, $this->caches)) {
throw new \Exception('Cache name not found ('.$cachename.')');
}
// Retrieve the initialised cache object from $caches, defines the caches expiry
// and executes the callback.
return $this->caches[$cachename]['cache']->load($cachename,
function (&$dependencies) use ($callback, $cachename) {
$dependencies[$this->caches[$cachename]['expirationtype']] = $this->caches[$cachename]['expirationparams'];
return $callback();
}
);
}
}

92
web/classes/Config.php Normal file
View File

@@ -0,0 +1,92 @@
<?php
namespace Jump;
use Exception;
class Config {
private \PHLAK\Config\Config $config;
/**
* Required files and directories need that should not be configurable.
*/
private const BASE_APPLICATION_PATHS = [
'backgroundsdir' => '/assets/backgrounds',
'sitesfile' => '/sites/sites.json',
'templatedir' => '/templates',
];
/**
* Configurable params we do expect to find in config.php
*/
private const CONFIG_PARAMS = [
'sitename',
'wwwroot',
'cachebypass',
'cachedir',
'noindex',
];
public function __construct() {
$this->config = new \PHLAK\Config\Config(__DIR__.'/../config.php');
$this->add_wwwroot_to_base_paths();
if ($this->config_params_missing()) {
throw new Exception('Config.php must always contain... '.implode(', ', self::CONFIG_PARAMS));
}
}
/**
* Prefixes the wwwroot string from config.php to the base application paths
* so they can be located in the file system correctly.
*
* @return void
*/
private function add_wwwroot_to_base_paths(): void {
$wwwroot = $this->config->get('wwwroot');
foreach(self::BASE_APPLICATION_PATHS as $key => $value) {
$this->config->set($key, $wwwroot.$value);
}
}
/**
* Determine if any configuration params are missing in the list loaded
* from the config.php.
*
* @return boolean
*/
private function config_params_missing(): bool {
return !!array_diff(
array_keys($this->config->toArray()),
array_merge(
array_keys(self::BASE_APPLICATION_PATHS),
self::CONFIG_PARAMS
));
}
/**
* Retrieves the config parameter provided in $key, first checks for its
* existence.
*
* @param string $key The config parameter required.
* @return mixed The selected value from the configuration array.
*/
public function get(string $key): mixed {
if (!$this->config->has($key)) {
throw new Exception('Config key does not exist... ('.$key.')');
}
return $this->config->get($key);
}
/**
* Attempt to converts a string to a boolean correctly, will return the parsed boolean
* or null on failure.
*
* @param mixed $input A string representing a boolean value... "true", "yes", "no", "false" etc.
* @return mixed Returns a proper boolean or null on failure.
*/
public function parse_bool(mixed $input): mixed {
return filter_var($input,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE);
}
}

25
web/classes/Greeting.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
namespace Jump;
class Greeting {
private array $greetings;
public function __construct() {
$this->greetings = [
03 => 'morning',
12 => 'afternoon',
16 => 'evening',
19 => 'night'
];
}
public function get_greeting(): string {
krsort($this->greetings);
foreach ($this->greetings as $key => $value) {
if (date('H', time()) >= $key) {
return $value;
}
}
}
}

66
web/classes/Main.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
namespace Jump;
class Main {
private Cache $cache;
private Greeting $greeting;
private \Mustache_Engine $mustache;
private array $outputarray;
private Sites $sites;
public function __construct() {
$this->config = new Config();
$this->greeting = new Greeting();
$this->mustache = new \Mustache_Engine([
'loader' => new \Mustache_Loader_FilesystemLoader($this->config->get('templatedir'))
]);
$this->cache = new Cache($this->config);
$this->sites = new Sites($this->config, $this->cache);
}
private function render_header(): string {
$template = $this->mustache->loadTemplate('header');
return $template->render([
'noindex' => $this->config->parse_bool($this->config->get('noindex')),
'sitename' => $this->config->get('sitename'),
]);
}
private function render_greeting(): string {
$template = $this->mustache->loadTemplate('greeting');
return $template->render([
'greeting' => $this->greeting->get_greeting(),
]);
}
private function render_sites(): string {
return $this->cache->load('templates/sites', function() {
$template = $this->mustache->loadTemplate('sites');
return $template->render([
'hassites' => !empty($this->sites->get_sites()),
'sites' => $this->sites->get_sites()
]);
});
}
private function render_footer(): string {
$template = $this->mustache->loadTemplate('footer');
return $template->render();
}
public function build_index_page(): void {
$this->outputarray = [
$this->render_header(),
$this->render_greeting(),
$this->render_sites(),
$this->render_footer(),
];
}
public function get_output(): string {
return implode('', $this->outputarray);
}
}

40
web/classes/Site.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace Jump;
class Site {
public string $name;
public bool $nofollow;
public string $icon;
public string $url;
public function __construct(array $sitearray) {
if (!isset($sitearray['name'], $sitearray['url'])) {
throw new \Exception('The array passed to Site() must contain the keys "name" and "url"!');
}
$this->name = $sitearray['name'];
$this->url = $sitearray['url'];
$this->nofollow = isset($sitearray['nofollow']) ? $sitearray['nofollow'] : false;
$this->icon = isset($sitearray['icon']) ? $this->get_favicon_datauri($sitearray['icon']) : $this->get_favicon_datauri();
}
public function get_favicon_datauri(?string $icon = null): string {
if ($icon === null) {
$favicon = new \Favicon\Favicon();
$favicon->cache([
'dir' => '/var/www/cache/icons/',
'timeout' => 86400
]);
$rawimage = $favicon->get($this->url, \Favicon\FaviconDLType::RAW_IMAGE);
if (!$rawimage) {
$rawimage = file_get_contents(__DIR__ . '/../assets/images/default-icon.png');
}
} else {
$rawimage = file_get_contents(__DIR__ . '/../sites/icons/'.$icon);
}
$mimetype = (new \finfo(FILEINFO_MIME_TYPE))->buffer($rawimage);
return 'data:'.$mimetype.';base64,'.base64_encode($rawimage);
}
}

82
web/classes/Sites.php Normal file
View File

@@ -0,0 +1,82 @@
<?php
namespace Jump;
use Exception;
/**
* Represents the sites defined in sites.json
*
* @author Dale Davies {@link https://www.daledavies.co.uk}
*/
class Sites {
private Cache $cache;
private Config $config;
private string $sitesfilelocation;
private array $loadedsites;
/**
* Automatically load sites.json on instantiation.
*/
public function __construct(Config $config, Cache $cache) {
$this->config = $config;
$this->loadedsites = [];
$this->sitesfilelocation = $this->config->get('sitesfile');
$this->cache = $cache;
$this->load_sites_from_json();
}
/**
* Try to load the list of sites from site.json.
*
* Throws an exception if the file cannot be loaded, is empty, or cannot
* be decoded to an array,
*
* @return void
* @throws Exception if sites.json cannot be found
*/
private function load_sites_from_json(): void {
$this->loadedsites = $this->cache->load('sites', function() {
$sites = [];
$rawjson = file_get_contents($this->sitesfilelocation);
if ($rawjson === false) {
throw new Exception('There was a problem loading the sites.json file');
}
if ($rawjson === '') {
throw new Exception('The sites.json file is empty');
}
$decodedjson = json_decode($rawjson, true);
if (!is_array($decodedjson)) {
throw new Exception('Provided sites json does not contain a top-level array');
}
foreach ($decodedjson as $array) {
$sites[] = new Site($array);
}
return $sites;
});
}
/**
* Return the loaded sites.
*
* @return array of sites loaded from sites.json
*/
public function get_sites(): array {
return $this->loadedsites;
}
/**
* Given a URL, does that site exist in our list of sites?
*
* @param string $url The URL to search for.
* @return Site|null
*/
public function get_site_by_url(string $url): Site {
$found = array_search($url, array_column($this->get_sites(), 'url'));
if (!$found) {
throw new Exception('The site could not be found ('.$url.')');
}
return $this->loadedsites[$found];
}
}

16
web/composer.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "daledavies/jump",
"description": "A startpage for stuff",
"type": "project",
"autoload": {
"psr-4": {
"Jump\\": "classes/"
}
},
"require": {
"mustache/mustache": "~2.5",
"arthurhoaro/favicon": "~1.0",
"nette/caching": "^3.1",
"phlak/config": "^7.0"
}
}

647
web/composer.lock generated Normal file
View File

@@ -0,0 +1,647 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "7ea3804d37d3b0050903f4604d6a82ab",
"packages": [
{
"name": "arthurhoaro/favicon",
"version": "v1.3.3",
"source": {
"type": "git",
"url": "https://github.com/ArthurHoaro/favicon.git",
"reference": "b1acd8b87d6b37e5251fe0559ed488a95078f5b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ArthurHoaro/favicon/zipball/b1acd8b87d6b37e5251fe0559ed488a95078f5b9",
"reference": "b1acd8b87d6b37e5251fe0559ed488a95078f5b9",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-fileinfo": "*",
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "~4.8",
"squizlabs/php_codesniffer": "^3.5",
"weew/helpers-filesystem": "~1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Favicon\\": "src/Favicon/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Chris Shiflett",
"homepage": "http://shiflett.org/"
},
{
"name": "Arthur Hoaro",
"homepage": "http://hoa.ro"
}
],
"description": "PHP Library used to discover favicon from given URL",
"homepage": "https://github.com/ArthurHoaro/favicon",
"keywords": [
"favicon",
"finder",
"icon"
],
"time": "2021-08-06T05:41:25+00:00"
},
{
"name": "mustache/mustache",
"version": "v2.14.1",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/mustache.php.git",
"reference": "579ffa5c96e1d292c060b3dd62811ff01ad8c24e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/579ffa5c96e1d292c060b3dd62811ff01ad8c24e",
"reference": "579ffa5c96e1d292c060b3dd62811ff01ad8c24e",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~1.11",
"phpunit/phpunit": "~3.7|~4.0|~5.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Mustache": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Justin Hileman",
"email": "justin@justinhileman.info",
"homepage": "http://justinhileman.com"
}
],
"description": "A Mustache implementation in PHP.",
"homepage": "https://github.com/bobthecow/mustache.php",
"keywords": [
"mustache",
"templating"
],
"time": "2022-01-21T06:08:36+00:00"
},
{
"name": "nette/caching",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/nette/caching.git",
"reference": "27d8f0048eb1a9c7e49e0268f39b2db7d3ce7ae9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/caching/zipball/27d8f0048eb1a9c7e49e0268f39b2db7d3ce7ae9",
"reference": "27d8f0048eb1a9c7e49e0268f39b2db7d3ce7ae9",
"shasum": ""
},
"require": {
"nette/finder": "^2.4 || ^3.0",
"nette/utils": "^2.4 || ^3.0",
"php": ">=7.2 <8.2"
},
"require-dev": {
"latte/latte": "^2.10",
"nette/di": "^v3.0",
"nette/tester": "^2.0",
"phpstan/phpstan": "^0.12",
"tracy/tracy": "^2.4"
},
"suggest": {
"ext-pdo_sqlite": "to use SQLiteStorage or SQLiteJournal"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"description": "⏱ Nette Caching: library with easy-to-use API and many cache backends.",
"homepage": "https://nette.org",
"keywords": [
"cache",
"journal",
"memcached",
"nette",
"sqlite"
],
"time": "2021-08-24T23:45:03+00:00"
},
{
"name": "nette/finder",
"version": "v2.5.3",
"source": {
"type": "git",
"url": "https://github.com/nette/finder.git",
"reference": "64dc25b7929b731e72a1bc84a9e57727f5d5d3e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/finder/zipball/64dc25b7929b731e72a1bc84a9e57727f5d5d3e8",
"reference": "64dc25b7929b731e72a1bc84a9e57727f5d5d3e8",
"shasum": ""
},
"require": {
"nette/utils": "^2.4 || ^3.0",
"php": ">=7.1"
},
"conflict": {
"nette/nette": "<2.2"
},
"require-dev": {
"nette/tester": "^2.0",
"phpstan/phpstan": "^0.12",
"tracy/tracy": "^2.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"description": "🔍 Nette Finder: find files and directories with an intuitive API.",
"homepage": "https://nette.org",
"keywords": [
"filesystem",
"glob",
"iterator",
"nette"
],
"time": "2021-12-12T17:43:24+00:00"
},
{
"name": "nette/utils",
"version": "v3.2.7",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/0af4e3de4df9f1543534beab255ccf459e7a2c99",
"reference": "0af4e3de4df9f1543534beab255ccf459e7a2c99",
"shasum": ""
},
"require": {
"php": ">=7.2 <8.2"
},
"conflict": {
"nette/di": "<3.0.6"
},
"require-dev": {
"nette/tester": "~2.0",
"phpstan/phpstan": "^1.0",
"tracy/tracy": "^2.3"
},
"suggest": {
"ext-gd": "to use Image",
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
"ext-json": "to use Nette\\Utils\\Json",
"ext-mbstring": "to use Strings::lower() etc...",
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause",
"GPL-2.0-only",
"GPL-3.0-only"
],
"authors": [
{
"name": "David Grudl",
"homepage": "https://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "https://nette.org/contributors"
}
],
"description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
"homepage": "https://nette.org",
"keywords": [
"array",
"core",
"datetime",
"images",
"json",
"nette",
"paginator",
"password",
"slugify",
"string",
"unicode",
"utf-8",
"utility",
"validation"
],
"time": "2022-01-24T11:29:14+00:00"
},
{
"name": "phlak/config",
"version": "7.0.0",
"source": {
"type": "git",
"url": "https://github.com/PHLAK/Config.git",
"reference": "a67af04eef18eabfad06442c86ddf692ca5bab0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHLAK/Config/zipball/a67af04eef18eabfad06442c86ddf692ca5bab0e",
"reference": "a67af04eef18eabfad06442c86ddf692ca5bab0e",
"shasum": ""
},
"require": {
"php": ">=7.1",
"symfony/yaml": "^3.0 || ^4.0 || ^5.0",
"yosymfony/toml": "^1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16.1",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"psy/psysh": "^0.10",
"vimeo/psalm": "^3.7"
},
"type": "library",
"autoload": {
"psr-4": {
"PHLAK\\Config\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Chris Kankiewicz",
"email": "Chris@ChrisKankiewicz.com"
}
],
"description": "Config loading and management",
"time": "2020-03-16T18:49:13+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "v2.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"time": "2021-07-12T14:48:14+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/yaml",
"version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "e80f87d2c9495966768310fc531b487ce64237a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2",
"reference": "e80f87d2c9495966768310fc531b487ce64237a2",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"symfony/console": "<5.3"
},
"require-dev": {
"symfony/console": "^5.3|^6.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"bin": [
"Resources/bin/yaml-lint"
],
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"time": "2022-01-26T16:32:32+00:00"
},
{
"name": "yosymfony/parser-utils",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/yosymfony/parser-utils.git",
"reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yosymfony/parser-utils/zipball/00bec9a12722b21f2baf7f9db35f127e90c162c9",
"reference": "00bec9a12722b21f2baf7f9db35f127e90c162c9",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"Yosymfony\\ParserUtils\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Victor Puertas",
"email": "vpgugr@gmail.com",
"homepage": "http://yosymfony.com"
}
],
"description": "Parser utilities",
"homepage": "http://github.com/yosymfony/toml",
"keywords": [
"lexer",
"parser"
],
"time": "2018-06-29T15:31:11+00:00"
},
{
"name": "yosymfony/toml",
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/yosymfony/toml.git",
"reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yosymfony/toml/zipball/bdab92ad920d0e36810a3a3e4a998d23f3498f8e",
"reference": "bdab92ad920d0e36810a3a3e4a998d23f3498f8e",
"shasum": ""
},
"require": {
"php": ">=7.1",
"yosymfony/parser-utils": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Yosymfony\\Toml\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Victor Puertas",
"email": "vpgugr@gmail.com",
"homepage": "http://yosymfony.com"
}
],
"description": "A PHP parser for TOML compatible with specification 0.4.0",
"homepage": "http://github.com/yosymfony/toml",
"keywords": [
"mojombo",
"parser",
"toml"
],
"time": "2018-08-08T15:08:14+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

9
web/config.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
// Edit the configuration below to suit your requirements.
return [
'sitename' => getenv('SITENAME') ?: 'Jump',
'wwwroot' => getenv('WWWROOT') ?: '/var/www/html',
'cachebypass' => getenv('CACHEBYPASS') ?: false,
'cachedir' => getenv('CACHEDIR') ?: '/var/www/cache',
'noindex' => getenv('NOINDEX') ?: true,
];

8
web/index.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
// Provided by composer for psr-4 style autoloading.
require __DIR__ .'/vendor/autoload.php';
// Initialise the application, then render and output its index page.
$jumpapp = new Jump\Main();
$jumpapp->build_index_page();
echo $jumpapp->get_output();

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 B

BIN
web/sites/icons/gitea.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

30
web/sites/sites.json Normal file
View File

@@ -0,0 +1,30 @@
[
{
"name": "Bitwarden",
"url" : "https://bitwarden.leannedale.co.uk",
"nofollow": true,
"icon": "bitwarden.png"
},
{
"name": "Gitea",
"url" : "https://git.leannedale.co.uk",
"nofollow": true,
"icon": "gitea.png"
},
{
"name": "Nextcloud",
"url" : "https://cloud.leannedale.co.uk",
"nofollow": true,
"icon": "nextcloud.png"
},
{
"name": "Paperless",
"url" : "https://paperless.leannedale.co.uk",
"nofollow": true,
"icon": "paperless.jpg"
},
{
"name": "Google",
"url" : "https://www.google.com"
}
]

View File

@@ -0,0 +1,5 @@
</div>
<div class="background fixed"></div>
{{! <script src="/assets/js/index.js"></script> }}
</body>
</html>

View File

@@ -0,0 +1 @@
<div class="greeting">Good {{greeting}}</div>

View File

@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{# noindex}}<meta name="robots" content="noindex">{{/ noindex}}
<title>{{sitename}}</title>
<link rel="stylesheet" href="/assets/css/styles.css">
<link rel="stylesheet" href="background-css.php">
</head>
<body>
<div class="content fixed">

View File

@@ -0,0 +1,14 @@
{{# hassites}}
<ul class="sites">
{{# sites}}
<li>
<a {{# nofollow}}rel="nofollow"{{/ nofollow}} href="{{url}}">
<span class="icon">
<img src="{{icon}}">
</span>
<span class="name">{{name}}</span>
</a>
</li>
{{/ sites}}
</ul>
{{/ hassites}}