jahresbericht2017/index.php

221 lines
5.4 KiB
PHP

<?php
/**
* Wikimedia Jahresbericht 2017
*
* Copyright (c) 2017 Atelier Disko - All rights reserved.
*
* Use of this source code is governed by the AGPL v3
* license that can be found in the LICENSE file.
*/
define('PROJECT_CONTEXT', 'dev');
define('PROJECT_APP_PATH', __DIR__);
define('PROJECT_DOMAIN', 'wmde-bericht2017.test');
if (PROJECT_CONTEXT === 'dev') {
error_reporting(E_ALL);
ini_set('display_errors', true);
} else {
ini_set('display_errors', false);
}
$routes = [];
$helpers = [];
$viewVars = [];
$scripts = [];
$lang = 'de';
$path = $_SERVER['REQUEST_URI'];
$isCanonical = false;
// Configure g11n stack early and strip locale from URL.
if (preg_match('#^/(de|en)(/.*)?#', $path, $matches)) {
$isCanonical = true;
$lang = $matches[1];
if (isset($matches[2])) {
$path = '/' . ltrim($matches[2], '/');
} else {
$path = '/';
}
}
//
// Helpers
//
// Used to construct URLs in the view.
$helpers['url'] = function($goal, $lang) {
$path = $lang . '/' . trim($goal, '/');
return '/' . trim($path, '/');
};
// Used to construct a URL for switching the language, but
// staying on the same page.
$helpers['switchLanguageFromUrl'] = function($lang) use ($path) {
return rtrim('/' . ($lang === 'de' ? 'en' : 'de') . '' . $path, '/');
};
$helpers['formatNumber'] = function($number, $lang) {
if ($lang !== 'de') {
return number_format($number);
}
return number_format($number, 0, ',', '.');
};
//
// Scripts
//
$scripts[] = 'views/layouts/default';
// Constructs a script path for the current page path. And will check if a script under
// that name is present in the filesystem. If not simply returns `null` otherwise
// it returns the script path, good for constructing the full script URL.
//
// Does not support pages with underscores.
$fragment = null;
if (!ltrim($path, '/')) {
$fragment = 'pages/home';
} elseif (strpos($path, '/report') === 0) {
// Sub-pages all use the same JS file, so we don't need to duplicate logic.
$fragment = 'pages/report';
} elseif (strpos($path, '/finance') === 0) {
// Sub-pages all use the same JS file, so we don't need to duplicate logic.
$fragment = 'pages/finance';
} else {
$fragment = 'pages/' . ltrim($path, '/');
}
if (file_exists(PROJECT_APP_PATH . '/assets/js/views/' . $fragment . '.js')) {
$scripts[] = "views/{$fragment}";
}
//
// Routes
//
$routes['#^/$#'] = function() {
$reports = require PROJECT_APP_PATH .'/data/reports.php';
$achievements = require PROJECT_APP_PATH .'/data/achievements.php';
return compact('reports', 'achievements') + [
'hasBlackHeader' => false
];
};
$routes['#^/finance#'] = function($path) {
return ['hasBlackHeader' => $path !== '/finance'];
};
$routes['#^/report$#'] = function($path, $query, $matches) {
$filter = isset($query['filter']) ? $query['filter'] : null;
$reports = require PROJECT_APP_PATH .'/data/reports.php';
$filters = require PROJECT_APP_PATH .'/data/report_filters.php';
if ($filter) {
$reports = array_filter($reports, function($v) use ($filter) {
return $v['category'] === $filter;
});
foreach ($filters as $f) {
if ($f['name'] === $filter) {
$filter = $f;
break;
}
}
}
return compact('reports', 'filter', 'filters') + [
'hasBlackHeader' => false
];
};
$routes['#^/report/(.*)$#'] = function($path, $query, $matches) {
$achievements = require PROJECT_APP_PATH .'/data/achievements.php';
$achievements = array_values(array_filter($achievements, function($v) use ($path) {
return $v['url'] === $path;
}));
$reports = require PROJECT_APP_PATH .'/data/reports.php';
$report = null;
foreach ($reports as $_report) {
if ($_report['name'] === $matches[1]) {
$report = $_report;
break;
}
}
return compact('report', 'achievements') + [
'hasBlackHeader' => true
];
};
//
// Functions
//
// Matches current request against registered routes and
// returns view variables.
$matchRoute = function($path, $routes) {
$query = parse_url($path, PHP_URL_QUERY);
parse_str($query, $query);
$path = parse_url($path, PHP_URL_PATH);
foreach ($routes as $regex => $handler) {
if (preg_match($regex, $path, $matches)) {
return $handler($path, $query, $matches);
}
}
return false;
};
$renderView = function($viewVars, $scripts, $helpers, $_path, $_lang) {
$_viewFileFromURI = function($path, $lang) {
// $path may contain query string
$path = parse_url($path, PHP_URL_PATH);
$viewBase = PROJECT_APP_PATH . '/views/pages/' . $lang;
$viewName = str_replace('/', '_', trim($path, '/'));
$viewFile = $viewBase . '/' . $viewName . '.php';
$viewFile = realpath($viewFile);
if ($viewName === '') {
return $viewBase . '/home.php';
}
// Prevent directory traversal attack
// see http://stackoverflow.com/a/4205278
if ($viewFile === false || strpos($viewFile, $viewBase) !== 0) {
return false;
}
return $viewFile;
};
$_viewFile = $_viewFileFromURI($_path, $_lang);
if ($_viewFile === false || trim($_path, '/') === 'home') {
return false;
}
extract($helpers, EXTR_SKIP);
extract($viewVars, EXTR_SKIP);
require PROJECT_APP_PATH . '/views/elements/' . $_lang . '/header.php';
require $_viewFile;
require PROJECT_APP_PATH . '/views/elements/' . $_lang . '/footer.php';
};
//
// Handle the request
//
$viewVars += compact('lang', 'isCanonical', 'path');
if (($viewVars += $matchRoute($path, $routes)) === false) {
header('HTTP/1.1 404 Not Found');
exit();
}
if ($renderView($viewVars, $scripts, $helpers, $path, $lang) === false) {
header('HTTP/1.1 404 Not Found');
exit();
}
?>