Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c96ba24d6 | ||
|
|
15b5869d5a | ||
|
|
bd2b28ef17 | ||
|
|
6d431717f8 | ||
|
|
4ac18dfc84 | ||
|
|
afbd0b4e20 | ||
|
|
062718dd75 | ||
|
|
20102f0577 | ||
|
|
a69ba3a99b | ||
|
|
66b21ec710 | ||
|
|
28ed02091a | ||
|
|
334df3462b | ||
|
|
01db72796b | ||
|
|
c50c5ed422 | ||
|
|
142c91f36b | ||
|
|
1269c876e9 | ||
|
|
44e441fac0 | ||
|
|
f85340cd00 | ||
|
|
ba83f73c31 | ||
|
|
86f05f7738 | ||
|
|
61ce915ec0 | ||
|
|
2aa3cdc7f3 | ||
|
|
5760e31e29 | ||
|
|
14fd723f26 | ||
|
|
a6d5214166 | ||
|
|
d1d9dede9c | ||
|
|
171064816c | ||
|
|
f8bb7bd092 | ||
|
|
466f065718 | ||
|
|
6d68f247e6 | ||
|
|
e4c7aadc64 | ||
|
|
25262fb5fe | ||
|
|
8e0c1ce08b | ||
|
|
9e196ebf33 | ||
|
|
013668e3d6 | ||
|
|
dbd16d6cbe | ||
|
|
6a191d5c28 | ||
|
|
39e49724da | ||
|
|
6c8a3c6a90 | ||
|
|
7159867db0 | ||
|
|
4d7c86fd1b | ||
|
|
9ca97dccb9 | ||
|
|
2ef67d53f4 | ||
|
|
06a0722899 | ||
|
|
4a9cf8a217 | ||
|
|
b3caf1fb38 | ||
|
|
08d4aec48e | ||
|
|
9c380076fa | ||
|
|
95508ad284 | ||
|
|
7690714b26 | ||
|
|
f732dad1b1 | ||
|
|
49b6f28a9d | ||
|
|
33ec9d3a9c | ||
|
|
6fb122e842 | ||
|
|
90ccdf6de7 | ||
|
|
a869a55b7e | ||
|
|
420ed400a6 | ||
|
|
d6f34b916e |
No files matched your search
@@ -7,3 +7,7 @@ GOOGLE_MAPS_SERVER_API_KEY=your_google_maps_server_api_key
|
|||||||
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
|
GOOGLE_MAPS_JS_API_KEY=your_google_maps_js_api_key
|
||||||
LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url
|
LEAFLET_TILESERVER_URL=a_leaflet_compatible_tileserver_url
|
||||||
LEAFLET_TILESERVER_ATTRIBUTION=attribution_to_be_shown_for_tiles
|
LEAFLET_TILESERVER_ATTRIBUTION=attribution_to_be_shown_for_tiles
|
||||||
|
STATIC_ROOT=/static
|
||||||
|
MAIL_FROM=mapguesser@mapguesser-dev.ch
|
||||||
|
MAIL_HOST=mail
|
||||||
|
MAIL_PORT=2500
|
||||||
+2
-1
@@ -5,7 +5,8 @@
|
|||||||
"license": "GNU GPL 3.0",
|
"license": "GNU GPL 3.0",
|
||||||
"require": {
|
"require": {
|
||||||
"vlucas/phpdotenv": "^4.1",
|
"vlucas/phpdotenv": "^4.1",
|
||||||
"symfony/console": "^5.1"
|
"symfony/console": "^5.1",
|
||||||
|
"phpmailer/phpmailer": "^6.1"
|
||||||
},
|
},
|
||||||
"require-dev": {},
|
"require-dev": {},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|||||||
Generated
+69
-1
@@ -4,8 +4,76 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "13a0eaff2786786caff2be86ac704fc7",
|
"content-hash": "67a75c3149ef859545476427e7f2f686",
|
||||||
"packages": [
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "phpmailer/phpmailer",
|
||||||
|
"version": "v6.1.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||||
|
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
|
||||||
|
"reference": "c2796cb1cb99d7717290b48c4e6f32cb6c60b7b3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"php": ">=5.5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/annotations": "^1.2",
|
||||||
|
"friendsofphp/php-cs-fixer": "^2.2",
|
||||||
|
"phpunit/phpunit": "^4.8 || ^5.7"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "Needed to send email in multibyte encoding charset",
|
||||||
|
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||||
|
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||||
|
"psr/log": "For optional PSR-3 debug logging",
|
||||||
|
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
|
||||||
|
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPMailer\\PHPMailer\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marcus Bointon",
|
||||||
|
"email": "phpmailer@synchromedia.co.uk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jim Jagielski",
|
||||||
|
"email": "jimjag@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Andy Prevost",
|
||||||
|
"email": "codeworxtech@users.sourceforge.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Brent R. Matzelle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/synchro",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2020-05-27T12:24:03+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpoption/phpoption",
|
"name": "phpoption/phpoption",
|
||||||
"version": "1.7.3",
|
"version": "1.7.3",
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Modify;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
|
use MapGuesser\Util\Geo\Bounds;
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'maps');
|
||||||
|
$select->columns(['id', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']);
|
||||||
|
|
||||||
|
$result = $select->execute();
|
||||||
|
|
||||||
|
while ($map = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
|
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'maps');
|
||||||
|
$modify->setId($map['id']);
|
||||||
|
$modify->set('area', $bounds->calculateApproximateArea());
|
||||||
|
$modify->save();
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Modify;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'users');
|
||||||
|
$select->columns(['id']);
|
||||||
|
|
||||||
|
$result = $select->execute();
|
||||||
|
|
||||||
|
while ($map = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'users');
|
||||||
|
$modify->setId($map['id']);
|
||||||
|
$modify->set('active', true);
|
||||||
|
$modify->save();
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE
|
||||||
|
`maps`
|
||||||
|
ADD
|
||||||
|
`area` decimal(13, 4) NOT NULL DEFAULT 0.0;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE `sessions` (
|
||||||
|
`id` varchar(64) NOT NULL,
|
||||||
|
`data` text NOT NULL,
|
||||||
|
`updated` timestamp NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
CREATE TABLE `user_confirmations` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`user_id` int(10) unsigned NOT NULL,
|
||||||
|
`token` varchar(64) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
KEY `token` (`token`),
|
||||||
|
CONSTRAINT `user_confirmations_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
|
ALTER TABLE
|
||||||
|
`users`
|
||||||
|
ADD
|
||||||
|
`active` tinyint(1) NOT NULL DEFAULT 0;
|
||||||
@@ -8,6 +8,7 @@ services:
|
|||||||
- .:/var/www/mapguesser
|
- .:/var/www/mapguesser
|
||||||
links:
|
links:
|
||||||
- 'mariadb'
|
- 'mariadb'
|
||||||
|
- 'mail'
|
||||||
mariadb:
|
mariadb:
|
||||||
image: mariadb:10.1
|
image: mariadb:10.1
|
||||||
volumes:
|
volumes:
|
||||||
@@ -17,5 +18,10 @@ services:
|
|||||||
MYSQL_DATABASE: 'mapguesser'
|
MYSQL_DATABASE: 'mapguesser'
|
||||||
MYSQL_USER: 'mapguesser'
|
MYSQL_USER: 'mapguesser'
|
||||||
MYSQL_PASSWORD: 'mapguesser'
|
MYSQL_PASSWORD: 'mapguesser'
|
||||||
|
mail:
|
||||||
|
image: marcopas/docker-mailslurper:latest
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
- 8085:8085
|
||||||
volumes:
|
volumes:
|
||||||
mysql:
|
mysql:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Hi,
|
||||||
|
<br><br>
|
||||||
|
You recently signed up on MapGuesser with this email address ({{EMAIL}}). To activate your account, please click on the following link:<br>
|
||||||
|
<a href="{{ACTIVATE_LINK}}" title="Account activation">{{ACTIVATE_LINK}}</a>
|
||||||
|
<br><br>
|
||||||
|
If you did not sign up on MapGuesser or changed your mind, no further action is required, your email address will be deleted soon.<br>
|
||||||
|
However if you want to immediately delete it, please click on the following link:<br>
|
||||||
|
<a href="{{CANCEL_LINK}}" title="Sign up cancellation">{{CANCEL_LINK}}</a>
|
||||||
|
<br><br>
|
||||||
|
Have fun on MapGuesser!
|
||||||
|
<br><br>
|
||||||
|
Regards,<br>
|
||||||
|
MapGuesser
|
||||||
@@ -3,25 +3,19 @@
|
|||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
const ROOT = __DIR__;
|
const ROOT = __DIR__;
|
||||||
|
const VERSION = '';
|
||||||
|
const REVISION = '';
|
||||||
|
const REVISION_DATE = '';
|
||||||
|
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(ROOT);
|
$dotenv = Dotenv\Dotenv::createImmutable(ROOT);
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
if (!empty($_ENV['DEV'])) {
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
ini_set('display_errors', '1');
|
|
||||||
} else {
|
|
||||||
ini_set('display_errors', '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
class Container
|
class Container
|
||||||
{
|
{
|
||||||
static MapGuesser\Interfaces\Database\IConnection $dbConnection;
|
static MapGuesser\Interfaces\Database\IConnection $dbConnection;
|
||||||
static MapGuesser\Routing\RouteCollection $routeCollection;
|
static MapGuesser\Routing\RouteCollection $routeCollection;
|
||||||
|
static \SessionHandlerInterface $sessionHandler;
|
||||||
|
static MapGuesser\Interfaces\Request\IRequest $request;
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::$dbConnection = new MapGuesser\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
Container::$dbConnection = new MapGuesser\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
||||||
Container::$routeCollection = new MapGuesser\Routing\RouteCollection();
|
|
||||||
|
|
||||||
session_start();
|
|
||||||
+10
-23
@@ -1,9 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
require '../main.php';
|
require '../web.php';
|
||||||
|
|
||||||
// very basic routing
|
|
||||||
$host = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'];
|
|
||||||
$method = strtolower($_SERVER['REQUEST_METHOD']);
|
$method = strtolower($_SERVER['REQUEST_METHOD']);
|
||||||
$url = substr($_SERVER['REQUEST_URI'], strlen('/'));
|
$url = substr($_SERVER['REQUEST_URI'], strlen('/'));
|
||||||
if (($pos = strpos($url, '?')) !== false) {
|
if (($pos = strpos($url, '?')) !== false) {
|
||||||
@@ -11,32 +9,15 @@ if (($pos = strpos($url, '?')) !== false) {
|
|||||||
}
|
}
|
||||||
$url = rawurldecode($url);
|
$url = rawurldecode($url);
|
||||||
|
|
||||||
Container::$routeCollection->get('index', '', [MapGuesser\Controller\HomeController::class, 'getIndex']);
|
|
||||||
Container::$routeCollection->get('login', 'login', [MapGuesser\Controller\LoginController::class, 'getLoginForm']);
|
|
||||||
Container::$routeCollection->post('login-action', 'login', [MapGuesser\Controller\LoginController::class, 'login']);
|
|
||||||
Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']);
|
|
||||||
Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']);
|
|
||||||
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
|
||||||
$routeCollection->get('game', '{mapId}', [MapGuesser\Controller\GameController::class, 'getGame']);
|
|
||||||
$routeCollection->get('game-json', '{mapId}/json', [MapGuesser\Controller\GameController::class, 'getGameJson']);
|
|
||||||
$routeCollection->get('newPlace-json', '{mapId}/newPlace.json', [MapGuesser\Controller\GameFlowController::class, 'getNewPlace']);
|
|
||||||
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'evaluateGuess']);
|
|
||||||
});
|
|
||||||
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
|
||||||
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
|
||||||
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
|
||||||
$routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
|
|
||||||
});
|
|
||||||
|
|
||||||
$match = Container::$routeCollection->match($method, explode('/', $url));
|
$match = Container::$routeCollection->match($method, explode('/', $url));
|
||||||
|
|
||||||
if ($match !== null) {
|
if ($match !== null) {
|
||||||
list($route, $params) = $match;
|
list($route, $params) = $match;
|
||||||
|
|
||||||
$request = new MapGuesser\Request\Request($_GET, $params, $_POST, $_SESSION);
|
Container::$request->setParsedRouteParams($params);
|
||||||
|
|
||||||
$handler = $route->getHandler();
|
$handler = $route->getHandler();
|
||||||
$controller = new $handler[0]($request);
|
$controller = new $handler[0](Container::$request);
|
||||||
|
|
||||||
if ($controller instanceof MapGuesser\Interfaces\Authorization\ISecured) {
|
if ($controller instanceof MapGuesser\Interfaces\Authorization\ISecured) {
|
||||||
$authorized = $controller->authorize();
|
$authorized = $controller->authorize();
|
||||||
@@ -44,6 +25,12 @@ if ($match !== null) {
|
|||||||
$authorized = true;
|
$authorized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($method === 'post' && Container::$request->post('anti_csrf_token') !== Container::$request->session()->get('anti_csrf_token')) {
|
||||||
|
header('Content-Type: text/html; charset=UTF-8', true, 403);
|
||||||
|
echo json_encode(['error' => 'no_valid_anti_csrf_token']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($authorized) {
|
if ($authorized) {
|
||||||
$response = call_user_func([$controller, $handler[1]]);
|
$response = call_user_func([$controller, $handler[1]]);
|
||||||
|
|
||||||
@@ -53,7 +40,7 @@ if ($match !== null) {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
} elseif ($response instanceof MapGuesser\Interfaces\Response\IRedirect) {
|
} elseif ($response instanceof MapGuesser\Interfaces\Response\IRedirect) {
|
||||||
header('Location: ' . $host . '/' . $response->getUrl(), true, $response->getHttpCode());
|
header('Location: ' . Container::$request->getBase() . '/' . $response->getUrl(), true, $response->getHttpCode());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#cover {
|
#guessCover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 40px;
|
top: 40px;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@@ -1,17 +1,3 @@
|
|||||||
#metadata {
|
|
||||||
position: absolute;
|
|
||||||
top: 50px;
|
|
||||||
left: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border: solid 1px #555555;
|
|
||||||
border-radius: 3px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
opacity: 0.95;
|
|
||||||
z-index: 2;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 40px);
|
height: calc(100% - 40px);
|
||||||
@@ -61,9 +47,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 999px) and (min-height: 600px) {
|
@media screen and (max-width: 999px) and (min-height: 600px) {
|
||||||
#metadata {
|
|
||||||
width: calc(100% - 155px);
|
|
||||||
}
|
|
||||||
#map.selected {
|
#map.selected {
|
||||||
height: calc(50% - 20px);
|
height: calc(50% - 20px);
|
||||||
}
|
}
|
||||||
@@ -79,12 +62,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1000px), (max-height: 599px) {
|
@media screen and (min-width: 1000px), (max-height: 599px) {
|
||||||
#metadata {
|
|
||||||
width: calc(50% - 20px);
|
|
||||||
}
|
|
||||||
#metadata.selected {
|
|
||||||
top: 95px;
|
|
||||||
}
|
|
||||||
#map.selected {
|
#map.selected {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,9 @@ sub {
|
|||||||
bottom: -0.4em;
|
bottom: -0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mono {
|
hr {
|
||||||
font-family: 'Roboto Mono', monospace;
|
border: solid #bbbbbb 1px;
|
||||||
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bold {
|
.bold {
|
||||||
@@ -89,10 +90,18 @@ sub {
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.marginLeft {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.marginBottom {
|
.marginBottom {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.marginRight {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -230,13 +239,41 @@ input.big:focus, select.big:focus, textarea.big:focus {
|
|||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.formError {
|
div.modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 100px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
z-index: 3;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cover {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #000000;
|
||||||
|
opacity: 0.5;
|
||||||
|
z-index: 2;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.error, p.formError {
|
||||||
color: #7f2929;
|
color: #7f2929;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.formError {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header {
|
div.header {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
background-color: #333333;
|
background-color: #333333;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
@@ -244,30 +281,25 @@ div.header {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header>div.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header.small {
|
div.header.small {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header>div.grid>:nth-child(2) {
|
div.header>p.header {
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header>div.grid>:nth-child(2)>span {
|
div.header>p.header>span {
|
||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header>div.grid>:nth-child(2)>span>a:link, div.header>div.grid>:nth-child(2)>span>a:visited {
|
div.header>p.header>span>a:link, div.header>p.header>span>a:visited {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.header>div.grid>:nth-child(2)>span:not(:last-child) {
|
div.header>p.header>span:not(:last-child) {
|
||||||
border-right: solid white 1px;
|
border-right: solid white 1px;
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
@@ -285,7 +317,7 @@ div.buttonContainer>button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#loading {
|
#loading {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -306,14 +338,26 @@ div.box {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 599px) {
|
@media screen and (max-width: 599px) {
|
||||||
div.header.small h1 span {
|
div.header h1 span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
button, a.button {
|
button, a.button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
div.modal {
|
||||||
|
left: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
div.box {
|
div.box {
|
||||||
width: initial;
|
width: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 600px) {
|
||||||
|
div.modal {
|
||||||
|
width: 540px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -270px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,8 +26,15 @@ div.mapItem>div.title>p.title {
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.mapItem>img {
|
div.mapItem>div.imgContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding-top: 50%;
|
||||||
|
background: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.mapItem>div.imgContainer>img {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: -50%
|
||||||
}
|
}
|
||||||
|
|
||||||
div.mapItem>div.inner {
|
div.mapItem>div.inner {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+14
-29
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
document.getElementById('cover').style.visibility = 'visible';
|
document.getElementById('guessCover').style.visibility = 'visible';
|
||||||
document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS);
|
document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS);
|
||||||
document.getElementById('currentScoreSum').innerHTML = '0/0';
|
document.getElementById('currentScoreSum').innerHTML = '0/0';
|
||||||
|
|
||||||
@@ -25,11 +25,9 @@
|
|||||||
});
|
});
|
||||||
Game.map.fitBounds(mapBounds);
|
Game.map.fitBounds(mapBounds);
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('GET', '/game/' + mapId + '/newPlace.json', function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
document.getElementById('cover').style.visibility = 'hidden';
|
document.getElementById('guessCover').style.visibility = 'hidden';
|
||||||
|
|
||||||
if (this.response.error) {
|
if (this.response.error) {
|
||||||
//TODO: handle this error
|
//TODO: handle this error
|
||||||
@@ -51,10 +49,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game.startNewRound();
|
Game.startNewRound();
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.open('GET', '/game/' + mapId + '/newPlace.json', true);
|
|
||||||
xhr.send();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
reset: function () {
|
reset: function () {
|
||||||
@@ -103,7 +98,7 @@
|
|||||||
lastRound.line.setVisible(false);
|
lastRound.line.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('cover').style.visibility = 'hidden';
|
document.getElementById('guessCover').style.visibility = 'hidden';
|
||||||
document.getElementById('showGuessButton').style.visibility = null;
|
document.getElementById('showGuessButton').style.visibility = null;
|
||||||
document.getElementById('guess').style.visibility = null;
|
document.getElementById('guess').style.visibility = null;
|
||||||
document.getElementById('guess').classList.remove('result')
|
document.getElementById('guess').classList.remove('result')
|
||||||
@@ -127,16 +122,11 @@
|
|||||||
handleErrorResponse: function (error) {
|
handleErrorResponse: function (error) {
|
||||||
// for the time being we only handle the "no_session_found" error and reset the game
|
// for the time being we only handle the "no_session_found" error and reset the game
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('GET', '/game/' + mapId + '/json', function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
mapBounds = this.response.bounds;
|
mapBounds = this.response.bounds;
|
||||||
|
|
||||||
Game.reset();
|
Game.reset();
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.open('GET', '/game/' + mapId + '/json', true);
|
|
||||||
xhr.send();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPano: function (panoId) {
|
loadPano: function (panoId) {
|
||||||
@@ -162,15 +152,13 @@
|
|||||||
document.getElementById('guess').classList.remove('adapt');
|
document.getElementById('guess').classList.remove('adapt');
|
||||||
}
|
}
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
document.getElementById('cover').style.visibility = 'visible';
|
document.getElementById('guessCover').style.visibility = 'visible';
|
||||||
|
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
data.append('lat', String(guessPosition.lat));
|
data.append('lat', String(guessPosition.lat));
|
||||||
data.append('lng', String(guessPosition.lng));
|
data.append('lng', String(guessPosition.lng));
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('POST', '/game/' + mapId + '/guess.json', function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
if (this.response.error) {
|
if (this.response.error) {
|
||||||
Game.handleErrorResponse(this.response.error);
|
Game.handleErrorResponse(this.response.error);
|
||||||
return;
|
return;
|
||||||
@@ -211,10 +199,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Game.panoId = this.response.panoId;
|
Game.panoId = this.response.panoId;
|
||||||
};
|
}, data);
|
||||||
|
|
||||||
xhr.open('POST', '/game/' + mapId + '/guess.json', true);
|
|
||||||
xhr.send(data);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addRealGuessPair: function (position, guessPosition, hidden) {
|
addRealGuessPair: function (position, guessPosition, hidden) {
|
||||||
@@ -229,7 +214,7 @@
|
|||||||
clickable: true,
|
clickable: true,
|
||||||
draggable: false,
|
draggable: false,
|
||||||
icon: {
|
icon: {
|
||||||
url: '/static/img/markers/marker-green.svg',
|
url: STATIC_ROOT + '/img/markers/marker-green.svg?rev=' + REVISION,
|
||||||
size: new google.maps.Size(24, 32),
|
size: new google.maps.Size(24, 32),
|
||||||
scaledSize: new google.maps.Size(24, 32),
|
scaledSize: new google.maps.Size(24, 32),
|
||||||
anchor: new google.maps.Point(12, 32)
|
anchor: new google.maps.Point(12, 32)
|
||||||
@@ -248,7 +233,7 @@
|
|||||||
clickable: false,
|
clickable: false,
|
||||||
draggable: false,
|
draggable: false,
|
||||||
icon: {
|
icon: {
|
||||||
url: '/static/img/markers/marker-gray-empty.svg',
|
url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION,
|
||||||
size: new google.maps.Size(24, 32),
|
size: new google.maps.Size(24, 32),
|
||||||
scaledSize: new google.maps.Size(24, 32),
|
scaledSize: new google.maps.Size(24, 32),
|
||||||
anchor: new google.maps.Point(12, 32),
|
anchor: new google.maps.Point(12, 32),
|
||||||
@@ -319,7 +304,7 @@
|
|||||||
var round = Game.rounds[i];
|
var round = Game.rounds[i];
|
||||||
|
|
||||||
round.realMarker.setIcon({
|
round.realMarker.setIcon({
|
||||||
url: '/static/img/markers/marker-green-empty.svg',
|
url: STATIC_ROOT + '/img/markers/marker-green-empty.svg?rev=' + REVISION,
|
||||||
size: new google.maps.Size(24, 32),
|
size: new google.maps.Size(24, 32),
|
||||||
scaledSize: new google.maps.Size(24, 32),
|
scaledSize: new google.maps.Size(24, 32),
|
||||||
anchor: new google.maps.Point(12, 32),
|
anchor: new google.maps.Point(12, 32),
|
||||||
@@ -411,7 +396,7 @@
|
|||||||
clickable: false,
|
clickable: false,
|
||||||
draggable: true,
|
draggable: true,
|
||||||
icon: {
|
icon: {
|
||||||
url: '/static/img/markers/marker-gray-empty.svg',
|
url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION,
|
||||||
size: new google.maps.Size(24, 32),
|
size: new google.maps.Size(24, 32),
|
||||||
scaledSize: new google.maps.Size(24, 32),
|
scaledSize: new google.maps.Size(24, 32),
|
||||||
anchor: new google.maps.Point(12, 32),
|
anchor: new google.maps.Point(12, 32),
|
||||||
|
|||||||
@@ -8,14 +8,15 @@
|
|||||||
|
|
||||||
var formData = new FormData(form);
|
var formData = new FormData(form);
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('POST', form.action, function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
if (this.response.error) {
|
if (this.response.error) {
|
||||||
var errorText;
|
var errorText;
|
||||||
switch (this.response.error) {
|
switch (this.response.error) {
|
||||||
case 'user_not_found':
|
case 'user_not_found':
|
||||||
errorText = 'No user found with the given email address.';
|
errorText = 'No user found with the given email address. You can <a href="/signup" title="Sign up">sign up here</a>!';
|
||||||
|
break;
|
||||||
|
case 'user_not_active':
|
||||||
|
errorText = 'User found with the given email address, but the account is not activated. Please check your email and click on the activation link!';
|
||||||
break;
|
break;
|
||||||
case 'password_not_match':
|
case 'password_not_match':
|
||||||
errorText = 'The given password is wrong.'
|
errorText = 'The given password is wrong.'
|
||||||
@@ -34,9 +35,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.location.replace('/');
|
window.location.replace('/');
|
||||||
};
|
}, formData);
|
||||||
|
|
||||||
xhr.open('POST', form.action, true);
|
|
||||||
xhr.send(formData);
|
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
@@ -7,11 +7,13 @@
|
|||||||
description: null
|
description: null
|
||||||
},
|
},
|
||||||
map: null,
|
map: null,
|
||||||
|
markers: null,
|
||||||
panorama: null,
|
panorama: null,
|
||||||
selectedMarker: null,
|
selectedMarker: null,
|
||||||
added: {},
|
added: {},
|
||||||
edited: {},
|
edited: {},
|
||||||
deleted: {},
|
deleted: {},
|
||||||
|
saved: true,
|
||||||
|
|
||||||
editMetadata: function () {
|
editMetadata: function () {
|
||||||
var form = document.getElementById('metadataForm');
|
var form = document.getElementById('metadataForm');
|
||||||
@@ -21,15 +23,14 @@
|
|||||||
|
|
||||||
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
|
document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]';
|
||||||
|
|
||||||
document.getElementById('metadata').style.visibility = 'hidden';
|
MapGuesser.hideModal();
|
||||||
|
|
||||||
|
MapEditor.saved = false;
|
||||||
document.getElementById('saveButton').disabled = false;
|
document.getElementById('saveButton').disabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
getPlace: function (placeId, marker) {
|
getPlace: function (placeId, marker) {
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('GET', '/admin/place.json/' + placeId, function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
if (!this.response.panoId) {
|
if (!this.response.panoId) {
|
||||||
@@ -45,10 +46,7 @@
|
|||||||
places[marker.placeId].noPano = false;
|
places[marker.placeId].noPano = false;
|
||||||
|
|
||||||
MapEditor.loadPano(this.response.panoId, places[marker.placeId].pov);
|
MapEditor.loadPano(this.response.panoId, places[marker.placeId].pov);
|
||||||
};
|
});
|
||||||
|
|
||||||
xhr.open('GET', '/admin/place.json/' + placeId, true);
|
|
||||||
xhr.send();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
loadPano: function (panoId, pov) {
|
loadPano: function (panoId, pov) {
|
||||||
@@ -113,7 +111,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('metadata').classList.add('selected');
|
|
||||||
document.getElementById('map').classList.add('selected');
|
document.getElementById('map').classList.add('selected');
|
||||||
document.getElementById('control').classList.add('selected');
|
document.getElementById('control').classList.add('selected');
|
||||||
document.getElementById('noPano').style.visibility = 'hidden';
|
document.getElementById('noPano').style.visibility = 'hidden';
|
||||||
@@ -129,6 +126,8 @@
|
|||||||
MapEditor.panorama.setVisible(false);
|
MapEditor.panorama.setVisible(false);
|
||||||
|
|
||||||
if (marker.placeId) {
|
if (marker.placeId) {
|
||||||
|
MapEditor.markers.removeLayer(MapEditor.selectedMarker);
|
||||||
|
MapEditor.map.addLayer(MapEditor.selectedMarker);
|
||||||
marker.setIcon(IconCollection.iconBlue);
|
marker.setIcon(IconCollection.iconBlue);
|
||||||
marker.setZIndexOffset(2000);
|
marker.setZIndexOffset(2000);
|
||||||
|
|
||||||
@@ -168,6 +167,8 @@
|
|||||||
var placeId = MapEditor.selectedMarker.placeId
|
var placeId = MapEditor.selectedMarker.placeId
|
||||||
|
|
||||||
if (places[placeId].id && !del) {
|
if (places[placeId].id && !del) {
|
||||||
|
MapEditor.map.removeLayer(MapEditor.selectedMarker);
|
||||||
|
MapEditor.markers.addLayer(MapEditor.selectedMarker);
|
||||||
MapEditor.selectedMarker.setIcon(places[placeId].noPano ? IconCollection.iconRed : IconCollection.iconGreen);
|
MapEditor.selectedMarker.setIcon(places[placeId].noPano ? IconCollection.iconRed : IconCollection.iconGreen);
|
||||||
MapEditor.selectedMarker.setZIndexOffset(1000);
|
MapEditor.selectedMarker.setZIndexOffset(1000);
|
||||||
} else {
|
} else {
|
||||||
@@ -211,11 +212,11 @@
|
|||||||
|
|
||||||
MapEditor.selectedMarker.setLatLng({ lat: places[placeId].lat, lng: places[placeId].lng });
|
MapEditor.selectedMarker.setLatLng({ lat: places[placeId].lat, lng: places[placeId].lng });
|
||||||
|
|
||||||
|
MapEditor.saved = false;
|
||||||
document.getElementById('saveButton').disabled = false;
|
document.getElementById('saveButton').disabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
closePlace: function (del) {
|
closePlace: function (del) {
|
||||||
document.getElementById('metadata').classList.remove('selected')
|
|
||||||
document.getElementById('map').classList.remove('selected');
|
document.getElementById('map').classList.remove('selected');
|
||||||
document.getElementById('control').classList.remove('selected');
|
document.getElementById('control').classList.remove('selected');
|
||||||
document.getElementById('noPano').style.visibility = 'hidden';
|
document.getElementById('noPano').style.visibility = 'hidden';
|
||||||
@@ -245,6 +246,7 @@
|
|||||||
document.getElementById('added').innerHTML = String(Object.keys(MapEditor.added).length);
|
document.getElementById('added').innerHTML = String(Object.keys(MapEditor.added).length);
|
||||||
document.getElementById('edited').innerHTML = String(Object.keys(MapEditor.edited).length);
|
document.getElementById('edited').innerHTML = String(Object.keys(MapEditor.edited).length);
|
||||||
|
|
||||||
|
MapEditor.saved = false;
|
||||||
document.getElementById('saveButton').disabled = false;
|
document.getElementById('saveButton').disabled = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -279,9 +281,7 @@
|
|||||||
data.append('deleted[]', JSON.stringify(MapEditor.deleted[placeId]));
|
data.append('deleted[]', JSON.stringify(MapEditor.deleted[placeId]));
|
||||||
}
|
}
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
MapGuesser.httpRequest('POST', '/admin/saveMap/' + mapId + '/json', function () {
|
||||||
xhr.responseType = 'json';
|
|
||||||
xhr.onload = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
if (this.response.error) {
|
if (this.response.error) {
|
||||||
@@ -304,11 +304,9 @@
|
|||||||
document.getElementById('edited').innerHTML = '0';
|
document.getElementById('edited').innerHTML = '0';
|
||||||
document.getElementById('deleted').innerHTML = '0';
|
document.getElementById('deleted').innerHTML = '0';
|
||||||
|
|
||||||
|
MapEditor.saved = true;
|
||||||
document.getElementById('saveButton').disabled = true;
|
document.getElementById('saveButton').disabled = true;
|
||||||
};
|
}, data);
|
||||||
|
|
||||||
xhr.open('POST', '/admin/saveMap/' + mapId + '/json', true);
|
|
||||||
xhr.send(data);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
replacePlaceIdsToReal: function (addedPlaces) {
|
replacePlaceIdsToReal: function (addedPlaces) {
|
||||||
@@ -322,17 +320,17 @@
|
|||||||
|
|
||||||
var IconCollection = {
|
var IconCollection = {
|
||||||
iconGreen: L.icon({
|
iconGreen: L.icon({
|
||||||
iconUrl: '/static/img/markers/marker-green.svg',
|
iconUrl: STATIC_ROOT + '/img/markers/marker-green.svg?rev' + REVISION,
|
||||||
iconSize: [24, 32],
|
iconSize: [24, 32],
|
||||||
iconAnchor: [12, 32]
|
iconAnchor: [12, 32]
|
||||||
}),
|
}),
|
||||||
iconRed: L.icon({
|
iconRed: L.icon({
|
||||||
iconUrl: '/static/img/markers/marker-red.svg',
|
iconUrl: STATIC_ROOT + '/img/markers/marker-red.svg?rev=' + REVISION,
|
||||||
iconSize: [24, 32],
|
iconSize: [24, 32],
|
||||||
iconAnchor: [12, 32]
|
iconAnchor: [12, 32]
|
||||||
}),
|
}),
|
||||||
iconBlue: L.icon({
|
iconBlue: L.icon({
|
||||||
iconUrl: '/static/img/markers/marker-blue.svg',
|
iconUrl: STATIC_ROOT + '/img/markers/marker-blue.svg?rev=' + REVISION,
|
||||||
iconSize: [24, 32],
|
iconSize: [24, 32],
|
||||||
iconAnchor: [12, 32]
|
iconAnchor: [12, 32]
|
||||||
}),
|
}),
|
||||||
@@ -381,6 +379,10 @@
|
|||||||
|
|
||||||
MapEditor.map.fitBounds(L.latLngBounds({ lat: mapBounds.south, lng: mapBounds.west }, { lat: mapBounds.north, lng: mapBounds.east }));
|
MapEditor.map.fitBounds(L.latLngBounds({ lat: mapBounds.south, lng: mapBounds.west }, { lat: mapBounds.north, lng: mapBounds.east }));
|
||||||
|
|
||||||
|
MapEditor.markers = L.markerClusterGroup({
|
||||||
|
maxClusterRadius: 50
|
||||||
|
});
|
||||||
|
|
||||||
for (var placeId in places) {
|
for (var placeId in places) {
|
||||||
if (!places.hasOwnProperty(placeId)) {
|
if (!places.hasOwnProperty(placeId)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -392,7 +394,7 @@
|
|||||||
icon: place.noPano ? IconCollection.iconRed : IconCollection.iconGreen,
|
icon: place.noPano ? IconCollection.iconRed : IconCollection.iconGreen,
|
||||||
zIndexOffset: 1000
|
zIndexOffset: 1000
|
||||||
})
|
})
|
||||||
.addTo(MapEditor.map)
|
.addTo(MapEditor.markers)
|
||||||
.on('click', function () {
|
.on('click', function () {
|
||||||
MapEditor.select(this);
|
MapEditor.select(this);
|
||||||
});
|
});
|
||||||
@@ -400,6 +402,8 @@
|
|||||||
marker.placeId = place.id;
|
marker.placeId = place.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MapEditor.map.addLayer(MapEditor.markers);
|
||||||
|
|
||||||
MapEditor.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
|
MapEditor.panorama = new google.maps.StreetViewPanorama(document.getElementById('panorama'), {
|
||||||
// switch off fullscreenControl because positioning doesn't work
|
// switch off fullscreenControl because positioning doesn't work
|
||||||
fullscreenControl: false,
|
fullscreenControl: false,
|
||||||
@@ -412,14 +416,8 @@
|
|||||||
document.getElementById('mapName').onclick = function (e) {
|
document.getElementById('mapName').onclick = function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var metadata = document.getElementById('metadata');
|
MapGuesser.showModal('metadata');
|
||||||
|
document.getElementById('metadataForm').elements.name.select();
|
||||||
if (metadata.style.visibility === 'visible') {
|
|
||||||
metadata.style.visibility = 'hidden';
|
|
||||||
} else {
|
|
||||||
metadata.style.visibility = 'visible';
|
|
||||||
document.getElementById('metadataForm').elements.name.select();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('metadataForm').onsubmit = function (e) {
|
document.getElementById('metadataForm').onsubmit = function (e) {
|
||||||
@@ -429,7 +427,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('closeMetadataButton').onclick = function () {
|
document.getElementById('closeMetadataButton').onclick = function () {
|
||||||
document.getElementById('metadata').style.visibility = 'hidden';
|
MapGuesser.hideModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById('saveButton').onclick = function () {
|
document.getElementById('saveButton').onclick = function () {
|
||||||
@@ -447,4 +445,13 @@
|
|||||||
document.getElementById('deleteButton').onclick = function () {
|
document.getElementById('deleteButton').onclick = function () {
|
||||||
MapEditor.deletePlace();
|
MapEditor.deletePlace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', function (e) {
|
||||||
|
if (MapEditor.saved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = '';
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
var MapGuesser = {
|
||||||
|
httpRequest: function (method, url, callback, data) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.responseType = 'json';
|
||||||
|
xhr.onload = callback;
|
||||||
|
|
||||||
|
xhr.open(method, url, true);
|
||||||
|
|
||||||
|
if (method === 'POST') {
|
||||||
|
if (typeof data === 'undefined') {
|
||||||
|
data = new FormData();
|
||||||
|
}
|
||||||
|
|
||||||
|
data.append('anti_csrf_token', ANTI_CSRF_TOKEN);
|
||||||
|
|
||||||
|
xhr.send(data);
|
||||||
|
} else {
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
showModal: function (id) {
|
||||||
|
document.getElementById(id).style.visibility = 'visible';
|
||||||
|
document.getElementById('cover').style.visibility = 'visible';
|
||||||
|
},
|
||||||
|
|
||||||
|
showModalWithContent: function (title, body, extraButtons) {
|
||||||
|
if (typeof extraButtons === 'undefined') {
|
||||||
|
extraButtons = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
MapGuesser.showModal('modal');
|
||||||
|
|
||||||
|
document.getElementById('modalTitle').textContent = title;
|
||||||
|
|
||||||
|
if (typeof body === 'object') {
|
||||||
|
document.getElementById('modalText').appendChild(body);
|
||||||
|
} else {
|
||||||
|
document.getElementById('modalText').textContent = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttons = document.getElementById('modalButtons');
|
||||||
|
buttons.textContent = '';
|
||||||
|
|
||||||
|
for (var i = 0; i < extraButtons.length; i++) {
|
||||||
|
var extraButton = extraButtons[i];
|
||||||
|
var button = document.createElement(extraButton.type);
|
||||||
|
|
||||||
|
if (extraButton.type === 'a') {
|
||||||
|
button.classList.add('button');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < extraButton.classNames.length; i++) {
|
||||||
|
button.classList.add(extraButton.classNames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.classList.add('marginTop');
|
||||||
|
button.classList.add('marginRight');
|
||||||
|
button.textContent = extraButton.text;
|
||||||
|
|
||||||
|
if (extraButton.type === 'a') {
|
||||||
|
button.href = extraButton.href;
|
||||||
|
} else if (extraButton.type === 'button') {
|
||||||
|
button.onclick = extraButton.onclick;
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
var closeButton = document.createElement('button');
|
||||||
|
|
||||||
|
closeButton.classList.add('gray');
|
||||||
|
closeButton.classList.add('marginTop');
|
||||||
|
closeButton.textContent = 'Close';
|
||||||
|
closeButton.onclick = function () {
|
||||||
|
MapGuesser.hideModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
buttons.appendChild(closeButton);
|
||||||
|
},
|
||||||
|
|
||||||
|
hideModal: function () {
|
||||||
|
var modals = document.getElementsByClassName('modal');
|
||||||
|
|
||||||
|
for (var i = 0; i < modals.length; i++) {
|
||||||
|
modals[i].style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('cover').style.visibility = 'hidden';
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleDisableOnChange: function (input, button) {
|
||||||
|
if (input.defaultValue !== input.value) {
|
||||||
|
button.disabled = false;
|
||||||
|
} else {
|
||||||
|
button.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var anchors = document.getElementsByTagName('a');
|
||||||
|
for (var i = 0; i < anchors.length; i++) {
|
||||||
|
var a = anchors[i];
|
||||||
|
if (a.href !== 'javascript:;' && a.target !== '_blank') {
|
||||||
|
a.onclick = function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('cover').onclick = function () {
|
||||||
|
MapGuesser.hideModal();
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
(function () {
|
||||||
|
var form = document.getElementById('profileForm');
|
||||||
|
|
||||||
|
form.elements.password_new.onkeyup = function () {
|
||||||
|
MapGuesser.toggleDisableOnChange(this, form.elements.save);
|
||||||
|
};
|
||||||
|
|
||||||
|
form.elements.password_new_confirm.onkeyup = function () {
|
||||||
|
MapGuesser.toggleDisableOnChange(this, form.elements.save);
|
||||||
|
};
|
||||||
|
|
||||||
|
form.onsubmit = function (e) {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var formData = new FormData(form);
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', form.action, function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
|
if (this.response.error) {
|
||||||
|
var errorText;
|
||||||
|
switch (this.response.error) {
|
||||||
|
case 'password_not_match':
|
||||||
|
errorText = 'The given current password is wrong.'
|
||||||
|
break;
|
||||||
|
case 'passwords_too_short':
|
||||||
|
errorText = 'The given new password is too short. Please choose a password that is at least 6 characters long!'
|
||||||
|
break;
|
||||||
|
case 'passwords_not_match':
|
||||||
|
errorText = 'The given new passwords do not match.'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var profileFormError = document.getElementById('profileFormError');
|
||||||
|
profileFormError.style.display = 'block';
|
||||||
|
profileFormError.innerHTML = errorText;
|
||||||
|
|
||||||
|
form.elements.password_new.select();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('profileFormError').style.display = 'none';
|
||||||
|
form.reset();
|
||||||
|
form.elements.save.disabled = true;
|
||||||
|
form.elements.password_new.focus();
|
||||||
|
}, formData);
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
(function () {
|
||||||
|
var form = document.getElementById('signupForm');
|
||||||
|
|
||||||
|
form.onsubmit = function (e) {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var formData = new FormData(form);
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', form.action, function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
|
if (this.response.error) {
|
||||||
|
var errorText;
|
||||||
|
switch (this.response.error) {
|
||||||
|
case 'passwords_too_short':
|
||||||
|
errorText = 'The given password is too short. Please choose a password that is at least 6 characters long!'
|
||||||
|
break;
|
||||||
|
case 'passwords_not_match':
|
||||||
|
errorText = 'The given passwords do not match.'
|
||||||
|
break;
|
||||||
|
case 'user_found':
|
||||||
|
errorText = 'There is a user already registered with the given email address. Please <a href="/login" title="Login">login here</a>!';
|
||||||
|
break;
|
||||||
|
case 'not_active_user_found':
|
||||||
|
errorText = 'There is a user already registered with the given email address. Please check your email and click on the activation link!';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var signupFormError = document.getElementById('signupFormError');
|
||||||
|
signupFormError.style.display = 'block';
|
||||||
|
signupFormError.innerHTML = errorText;
|
||||||
|
|
||||||
|
form.elements.email.select();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('signupFormError').style.display = 'none';
|
||||||
|
form.reset();
|
||||||
|
form.elements.email.focus();
|
||||||
|
|
||||||
|
MapGuesser.showModalWithContent('Sign up successful', 'Sign up was successful. Please check your email and click on the activation link to activate your account!');
|
||||||
|
}, formData);
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet": "^1.6.0"
|
"leaflet": "^1.6.0",
|
||||||
|
"leaflet.markercluster": "^1.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
leaflet.markercluster@^1.4.1:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz#b53f2c4f2ca7306ddab1dbb6f1861d5e8aa6c5e5"
|
||||||
|
integrity sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw==
|
||||||
|
|
||||||
leaflet@^1.6.0:
|
leaflet@^1.6.0:
|
||||||
version "1.6.0"
|
version "1.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308"
|
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.6.0.tgz#aecbb044b949ec29469eeb31c77a88e2f448f308"
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ def updateAppInWorktree(worktreePath):
|
|||||||
subprocess.call([worktreePath + "/scripts/update.sh"], cwd=worktreePath)
|
subprocess.call([worktreePath + "/scripts/update.sh"], cwd=worktreePath)
|
||||||
|
|
||||||
def updateAppVersionInWorktree(worktreePath):
|
def updateAppVersionInWorktree(worktreePath):
|
||||||
pass
|
subprocess.call([worktreePath + "/scripts/update-version.sh"], cwd=worktreePath)
|
||||||
#subprocess.call([worktreePath + "/scripts/update-version.sh"], cwd=worktreePath)
|
|
||||||
|
|
||||||
worktrees = getDataForWorktrees()
|
worktrees = getDataForWorktrees()
|
||||||
|
|
||||||
@@ -111,10 +110,6 @@ if developmentWorktree.revision != developmentWorktree.newRevision:
|
|||||||
elif developmentWorktree.version != developmentWorktree.newVersion:
|
elif developmentWorktree.version != developmentWorktree.newVersion:
|
||||||
print("-> DEVELOPMENT " + developmentWorktree.path + "'s version info will be UPDATED")
|
print("-> DEVELOPMENT " + developmentWorktree.path + "'s version info will be UPDATED")
|
||||||
|
|
||||||
checkoutWorktree(developmentWorktree.path, developmentWorktree.branch)
|
|
||||||
|
|
||||||
print(developmentWorktree.path + " is checked out to " + developmentWorktree.branch)
|
|
||||||
|
|
||||||
updateAppVersionInWorktree(developmentWorktree.path)
|
updateAppVersionInWorktree(developmentWorktree.path)
|
||||||
|
|
||||||
print("MapGuesser version is updated in " + developmentWorktree.path)
|
print("MapGuesser version is updated in " + developmentWorktree.path)
|
||||||
|
|||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
BRANCH_NAME=$(git symbolic-ref --short HEAD)
|
||||||
|
BRANCH_PATTERN="^(bugfix|feature|hotfix)\/([A-Z]+-[0-9]+).*"
|
||||||
|
|
||||||
|
if [[ "${BRANCH_NAME}" =~ $BRANCH_PATTERN ]]; then
|
||||||
|
TICKET_ID=$(echo $BRANCH_NAME | sed -E "s@$BRANCH_PATTERN@\\2@")
|
||||||
|
|
||||||
|
COMMIT_MESSAGE=$(head -n 1 $1)
|
||||||
|
COMMIT_MESSAGE_REGEX="^$TICKET_ID .*"
|
||||||
|
|
||||||
|
if [[ ! "${COMMIT_MESSAGE}" =~ $COMMIT_MESSAGE_REGEX ]]; then
|
||||||
|
sed -i.bak -e "1s/^/$TICKET_ID /" $1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
Executable
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ROOT_DIR=$(dirname $(readlink -f "$0"))/..
|
||||||
|
|
||||||
|
. ${ROOT_DIR}/.env
|
||||||
|
|
||||||
|
cd ${ROOT_DIR}
|
||||||
|
|
||||||
|
echo "Updating version info..."
|
||||||
|
|
||||||
|
VERSION=$(git describe --tags --always --match "Release_*" HEAD)
|
||||||
|
REVISION=$(git rev-parse --short HEAD)
|
||||||
|
REVISION_DATE=$(git show -s --format=%aI HEAD)
|
||||||
|
|
||||||
|
sed -i -E "s/const VERSION = '(.*)';/const VERSION = '${VERSION}';/" main.php
|
||||||
|
sed -i -E "s/const REVISION = '(.*)';/const REVISION = '${REVISION}';/" main.php
|
||||||
|
sed -i -E "s/const REVISION_DATE = '(.*)';/const REVISION_DATE = '${REVISION_DATE}';/" main.php
|
||||||
@@ -44,7 +44,7 @@ class GameController
|
|||||||
if (!($state = $session->get('state')) || $state['mapId'] !== $mapId) {
|
if (!($state = $session->get('state')) || $state['mapId'] !== $mapId) {
|
||||||
$session->set('state', [
|
$session->set('state', [
|
||||||
'mapId' => $mapId,
|
'mapId' => $mapId,
|
||||||
'area' => $bounds->calculateApproximateArea(),
|
'area' => $map['area'],
|
||||||
'rounds' => []
|
'rounds' => []
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class GameFlowController
|
|||||||
|
|
||||||
private function calculateScore(float $distance, float $area): int
|
private function calculateScore(float $distance, float $area): int
|
||||||
{
|
{
|
||||||
$goodness = 1.0 - ($distance / sqrt($area));
|
$goodness = 1.0 - ($distance / (sqrt($area) * 1000));
|
||||||
|
|
||||||
return (int) round(pow(static::MAX_SCORE, $goodness));
|
return (int) round(pow(static::MAX_SCORE, $goodness));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ class LoginController
|
|||||||
|
|
||||||
$user = new User($userData);
|
$user = new User($userData);
|
||||||
|
|
||||||
|
if (!$user->getActive()) {
|
||||||
|
$data = ['error' => 'user_not_active'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
if (!$user->checkPassword($this->request->post('password'))) {
|
if (!$user->checkPassword($this->request->post('password'))) {
|
||||||
$data = ['error' => 'password_not_match'];
|
$data = ['error' => 'password_not_match'];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
@@ -68,6 +73,6 @@ class LoginController
|
|||||||
{
|
{
|
||||||
$this->request->session()->delete('user');
|
$this->request->session()->delete('user');
|
||||||
|
|
||||||
return new Redirect([\Container::$routeCollection->getRoute('login'), []], IRedirect::TEMPORARY);
|
return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +74,8 @@ class MapAdminController implements ISecured
|
|||||||
{
|
{
|
||||||
$mapId = (int) $this->request->query('mapId');
|
$mapId = (int) $this->request->query('mapId');
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
if (!$mapId) {
|
if (!$mapId) {
|
||||||
$mapId = $this->addNewMap();
|
$mapId = $this->addNewMap();
|
||||||
}
|
}
|
||||||
@@ -118,7 +120,8 @@ class MapAdminController implements ISecured
|
|||||||
'bound_south_lat' => $mapBounds->getSouthLat(),
|
'bound_south_lat' => $mapBounds->getSouthLat(),
|
||||||
'bound_west_lng' => $mapBounds->getWestLng(),
|
'bound_west_lng' => $mapBounds->getWestLng(),
|
||||||
'bound_north_lat' => $mapBounds->getNorthLat(),
|
'bound_north_lat' => $mapBounds->getNorthLat(),
|
||||||
'bound_east_lng' => $mapBounds->getEastLng()
|
'bound_east_lng' => $mapBounds->getEastLng(),
|
||||||
|
'area' => $mapBounds->calculateApproximateArea(),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($_POST['name'])) {
|
if (isset($_POST['name'])) {
|
||||||
@@ -130,10 +133,44 @@ class MapAdminController implements ISecured
|
|||||||
|
|
||||||
$this->saveMapData($mapId, $map);
|
$this->saveMapData($mapId, $map);
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
$data = ['mapId' => $mapId, 'added' => $addedIds];
|
$data = ['mapId' => $mapId, 'added' => $addedIds];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteMap() {
|
||||||
|
$mapId = (int) $this->request->query('mapId');
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
|
$this->deletePlaces($mapId);
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'maps');
|
||||||
|
$modify->setId($mapId);
|
||||||
|
$modify->delete();
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deletePlaces(int $mapId): void
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection, 'places');
|
||||||
|
$select->columns(['id']);
|
||||||
|
$select->where('map_id', '=', $mapId);
|
||||||
|
|
||||||
|
$result = $select->execute();
|
||||||
|
|
||||||
|
while ($place = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'places');
|
||||||
|
$modify->setId($place['id']);
|
||||||
|
$modify->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function calculateMapBounds(int $mapId): Bounds
|
private function calculateMapBounds(int $mapId): Bounds
|
||||||
{
|
{
|
||||||
$select = new Select(\Container::$dbConnection, 'places');
|
$select = new Select(\Container::$dbConnection, 'places');
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use MapGuesser\Interfaces\Authentication\IUser;
|
|||||||
use MapGuesser\Interfaces\Database\IResultSet;
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Util\Geo\Bounds;
|
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
|
|
||||||
class MapsController
|
class MapsController
|
||||||
@@ -29,6 +28,7 @@ class MapsController
|
|||||||
['maps', 'bound_west_lng'],
|
['maps', 'bound_west_lng'],
|
||||||
['maps', 'bound_north_lat'],
|
['maps', 'bound_north_lat'],
|
||||||
['maps', 'bound_east_lng'],
|
['maps', 'bound_east_lng'],
|
||||||
|
['maps', 'area'],
|
||||||
new RawExpression('COUNT(places.id) AS num_places')
|
new RawExpression('COUNT(places.id) AS num_places')
|
||||||
]);
|
]);
|
||||||
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
$select->leftJoin('places', ['places', 'map_id'], '=', ['maps', 'id']);
|
||||||
@@ -39,9 +39,7 @@ class MapsController
|
|||||||
|
|
||||||
$maps = [];
|
$maps = [];
|
||||||
while ($map = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
while ($map = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
$bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']);
|
$map['area'] = $this->formatMapAreaForHuman($map['area']);
|
||||||
|
|
||||||
$map['area'] = $this->formatMapAreaForHuman($bounds->calculateApproximateArea());
|
|
||||||
|
|
||||||
$maps[] = $map;
|
$maps[] = $map;
|
||||||
}
|
}
|
||||||
@@ -53,29 +51,29 @@ class MapsController
|
|||||||
|
|
||||||
private function formatMapAreaForHuman(float $area): array
|
private function formatMapAreaForHuman(float $area): array
|
||||||
{
|
{
|
||||||
if ($area < 100.0) {
|
if ($area < 0.01) {
|
||||||
|
$digits = 0;
|
||||||
|
$rounded = round($area * 1000000.0, -2);
|
||||||
|
$unit = 'm';
|
||||||
|
} elseif ($area < 0.1) {
|
||||||
|
$digits = 0;
|
||||||
|
$rounded = round($area * 1000000.0, -3);
|
||||||
|
$unit = 'm';
|
||||||
|
} elseif ($area < 1.0) {
|
||||||
|
$digits = 2;
|
||||||
|
$rounded = round($area, 2);
|
||||||
|
$unit = 'km';
|
||||||
|
} elseif ($area < 100.0) {
|
||||||
$digits = 0;
|
$digits = 0;
|
||||||
$rounded = round($area, 0);
|
$rounded = round($area, 0);
|
||||||
$unit = 'm';
|
$unit = 'km';
|
||||||
} elseif ($area < 100000.0) {
|
} elseif ($area < 10000.0) {
|
||||||
$digits = 0;
|
$digits = 0;
|
||||||
$rounded = round($area, -2);
|
$rounded = round($area, -2);
|
||||||
$unit = 'm';
|
|
||||||
} elseif ($area < 1000000.0) {
|
|
||||||
$digits = 2;
|
|
||||||
$rounded = round($area / 1000000.0, 2);
|
|
||||||
$unit = 'km';
|
|
||||||
} elseif ($area < 100000000.0) {
|
|
||||||
$digits = 0;
|
|
||||||
$rounded = round($area / 1000000.0, 0);
|
|
||||||
$unit = 'km';
|
|
||||||
} elseif ($area < 10000000000.0) {
|
|
||||||
$digits = 0;
|
|
||||||
$rounded = round($area / 1000000.0, -2);
|
|
||||||
$unit = 'km';
|
$unit = 'km';
|
||||||
} else {
|
} else {
|
||||||
$digits = 0;
|
$digits = 0;
|
||||||
$rounded = round($area / 1000000.0, -4);
|
$rounded = round($area, -4);
|
||||||
$unit = 'km';
|
$unit = 'km';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Modify;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
|
use MapGuesser\Interfaces\Response\IRedirect;
|
||||||
|
use MapGuesser\Mailing\Mail;
|
||||||
|
use MapGuesser\Model\User;
|
||||||
|
use MapGuesser\Response\HtmlContent;
|
||||||
|
use MapGuesser\Response\JsonContent;
|
||||||
|
use MapGuesser\Response\Redirect;
|
||||||
|
|
||||||
|
class SignupController
|
||||||
|
{
|
||||||
|
private IRequest $request;
|
||||||
|
|
||||||
|
public function __construct(IRequest $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignupForm()
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
|
||||||
|
if ($session->get('user')) {
|
||||||
|
return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
return new HtmlContent('signup/signup', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signup(): IContent
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
|
||||||
|
if ($session->get('user')) {
|
||||||
|
//TODO: return with some error
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'users');
|
||||||
|
$select->columns(User::getFields());
|
||||||
|
$select->where('email', '=', $this->request->post('email'));
|
||||||
|
|
||||||
|
$userData = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($userData !== null) {
|
||||||
|
$user = new User($userData);
|
||||||
|
|
||||||
|
if ($user->getActive()) {
|
||||||
|
$data = ['error' => 'user_found'];
|
||||||
|
} else {
|
||||||
|
$data = ['error' => 'not_active_user_found'];
|
||||||
|
}
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($this->request->post('password')) < 6) {
|
||||||
|
$data = ['error' => 'passwords_too_short'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->post('password') !== $this->request->post('password_confirm')) {
|
||||||
|
$data = ['error' => 'passwords_not_match'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = new User([
|
||||||
|
'email' => $this->request->post('email'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user->setPlainPassword($this->request->post('password'));
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'users');
|
||||||
|
$modify->fill($user->toArray());
|
||||||
|
$modify->save();
|
||||||
|
$userId = $modify->getId();
|
||||||
|
|
||||||
|
$token = hash('sha256', serialize($user) . random_bytes(10) . microtime());
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'user_confirmations');
|
||||||
|
$modify->set('user_id', $userId);
|
||||||
|
$modify->set('token', $token);
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
|
$this->sendConfirmationEmail($user->getEmail(), $token);
|
||||||
|
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activate()
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
|
||||||
|
if ($session->get('user')) {
|
||||||
|
return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'user_confirmations');
|
||||||
|
$select->columns(['id', 'user_id']);
|
||||||
|
$select->where('token', '=', $this->request->query('token'));
|
||||||
|
|
||||||
|
$confirmation = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($confirmation === null) {
|
||||||
|
$data = [];
|
||||||
|
return new HtmlContent('signup/activate', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'user_confirmations');
|
||||||
|
$modify->setId($confirmation['id']);
|
||||||
|
$modify->delete();
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'users');
|
||||||
|
$modify->setId($confirmation['user_id']);
|
||||||
|
$modify->set('active', true);
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'users');
|
||||||
|
$select->columns(User::getFields());
|
||||||
|
$select->whereId($confirmation['user_id']);
|
||||||
|
|
||||||
|
$userData = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
$user = new User($userData);
|
||||||
|
|
||||||
|
$session->set('user', $user);
|
||||||
|
|
||||||
|
return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancel()
|
||||||
|
{
|
||||||
|
$session = $this->request->session();
|
||||||
|
|
||||||
|
if ($session->get('user')) {
|
||||||
|
return new Redirect([\Container::$routeCollection->getRoute('index'), []], IRedirect::TEMPORARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
$select = new Select(\Container::$dbConnection, 'user_confirmations');
|
||||||
|
$select->columns(['id', 'user_id']);
|
||||||
|
$select->where('token', '=', $this->request->query('token'));
|
||||||
|
|
||||||
|
$confirmation = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($confirmation === null) {
|
||||||
|
$data = ['success' => false];
|
||||||
|
return new HtmlContent('signup/cancel', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'user_confirmations');
|
||||||
|
$modify->setId($confirmation['id']);
|
||||||
|
$modify->delete();
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'users');
|
||||||
|
$modify->setId($confirmation['user_id']);
|
||||||
|
$modify->delete();
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new HtmlContent('signup/cancel', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendConfirmationEmail($email, $token): void
|
||||||
|
{
|
||||||
|
$mail = new Mail();
|
||||||
|
$mail->addRecipient($email);
|
||||||
|
$mail->setSubject('Welcome to MapGuesser - Activate your account');
|
||||||
|
$mail->setBodyFromTemplate('signup', [
|
||||||
|
'EMAIL' => $email,
|
||||||
|
'ACTIVATE_LINK' => $this->request->getBase() . '/signup/activate/' . $token,
|
||||||
|
'CANCEL_LINK' => $this->request->getBase() . '/signup/cancel/' . $token,
|
||||||
|
]);
|
||||||
|
$mail->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Modify;
|
||||||
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||||
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
|
use MapGuesser\Response\HtmlContent;
|
||||||
|
use MapGuesser\Response\JsonContent;
|
||||||
|
|
||||||
|
class UserController implements ISecured
|
||||||
|
{
|
||||||
|
private IRequest $request;
|
||||||
|
|
||||||
|
public function __construct(IRequest $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
$user = $this->request->user();
|
||||||
|
|
||||||
|
return $user !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProfile(): IContent
|
||||||
|
{
|
||||||
|
$user = $this->request->user();
|
||||||
|
|
||||||
|
$data = ['user' => $user->toArray()];
|
||||||
|
return new HtmlContent('profile', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveProfile(): IContent
|
||||||
|
{
|
||||||
|
$user = $this->request->user();
|
||||||
|
|
||||||
|
if (!$user->checkPassword($this->request->post('password'))) {
|
||||||
|
$data = ['error' => 'password_not_match'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($this->request->post('password_new')) > 0) {
|
||||||
|
if (strlen($this->request->post('password_new')) < 6) {
|
||||||
|
$data = ['error' => 'passwords_too_short'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) {
|
||||||
|
$data = ['error' => 'passwords_not_match'];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->setPlainPassword($this->request->post('password_new'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'users');
|
||||||
|
$modify->fill($user->toArray());
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ class Modify
|
|||||||
|
|
||||||
private array $original = [];
|
private array $original = [];
|
||||||
|
|
||||||
|
private ?string $externalId = null;
|
||||||
|
|
||||||
private bool $autoIncrement = true;
|
private bool $autoIncrement = true;
|
||||||
|
|
||||||
public function __construct(IConnection $connection, string $table)
|
public function __construct(IConnection $connection, string $table)
|
||||||
@@ -31,6 +33,13 @@ class Modify
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setExternalId($id): Modify
|
||||||
|
{
|
||||||
|
$this->externalId = $id;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setAutoIncrement(bool $autoIncrement = true): Modify
|
public function setAutoIncrement(bool $autoIncrement = true): Modify
|
||||||
{
|
{
|
||||||
$this->autoIncrement = $autoIncrement;
|
$this->autoIncrement = $autoIncrement;
|
||||||
@@ -87,7 +96,9 @@ class Modify
|
|||||||
|
|
||||||
private function insert(): void
|
private function insert(): void
|
||||||
{
|
{
|
||||||
if (!$this->autoIncrement) {
|
if ($this->externalId !== null) {
|
||||||
|
$this->attributes[$this->idName] = $this->externalId;
|
||||||
|
} elseif (!$this->autoIncrement) {
|
||||||
$this->attributes[$this->idName] = $this->generateKey();
|
$this->attributes[$this->idName] = $this->generateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +116,14 @@ class Modify
|
|||||||
|
|
||||||
private function update(): void
|
private function update(): void
|
||||||
{
|
{
|
||||||
$diff = $this->generateDiff();
|
/*$diff = $this->generateDiff();
|
||||||
|
|
||||||
if (count($diff) === 0) {
|
if (count($diff) === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
$diff = $this->attributes;
|
||||||
|
unset($diff[$this->idName]);
|
||||||
|
|
||||||
$set = $this->generateColumnsWithBinding(array_keys($diff));
|
$set = $this->generateColumnsWithBinding(array_keys($diff));
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ interface IUser
|
|||||||
const PERMISSION_ADMIN = 1;
|
const PERMISSION_ADMIN = 1;
|
||||||
|
|
||||||
public function hasPermission(int $permission): bool;
|
public function hasPermission(int $permission): bool;
|
||||||
|
|
||||||
|
public function getDisplayName(): string;
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,10 @@ use MapGuesser\Interfaces\Authentication\IUser;
|
|||||||
|
|
||||||
interface IRequest
|
interface IRequest
|
||||||
{
|
{
|
||||||
|
public function setParsedRouteParams(array &$routeParams);
|
||||||
|
|
||||||
|
public function getBase(): string;
|
||||||
|
|
||||||
public function query(string $key);
|
public function query(string $key);
|
||||||
|
|
||||||
public function post(string $key);
|
public function post(string $key);
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<?php namespace MapGuesser\Mailing;
|
||||||
|
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
|
||||||
|
class Mail
|
||||||
|
{
|
||||||
|
private array $recipients = [];
|
||||||
|
|
||||||
|
public string $subject = '';
|
||||||
|
|
||||||
|
public string $body = '';
|
||||||
|
|
||||||
|
public function addRecipient(string $mail, ?string $name = null): void
|
||||||
|
{
|
||||||
|
$this->recipients[] = [$mail, $name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubject(string $subject): void
|
||||||
|
{
|
||||||
|
$this->subject = $subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBody(string $body): void
|
||||||
|
{
|
||||||
|
$this->body = $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBodyFromTemplate(string $template, array $params = []): void
|
||||||
|
{
|
||||||
|
$this->body = file_get_contents(ROOT . '/mail/' . $template . '.html');
|
||||||
|
|
||||||
|
foreach ($params as $key => $param) {
|
||||||
|
$this->body = str_replace('{{' . $key . '}}', $param, $this->body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(): void
|
||||||
|
{
|
||||||
|
$mailer = new PHPMailer(true);
|
||||||
|
|
||||||
|
$mailer->CharSet = 'utf-8';
|
||||||
|
$mailer->Hostname = substr($_ENV['MAIL_FROM'], strpos($_ENV['MAIL_FROM'], '@') + 1);
|
||||||
|
|
||||||
|
if (!empty($_ENV['MAIL_HOST'])) {
|
||||||
|
$mailer->Mailer = 'smtp';
|
||||||
|
$mailer->Host = $_ENV['MAIL_HOST'];
|
||||||
|
$mailer->Port = !empty($_ENV['MAIL_PORT']) ? $_ENV['MAIL_PORT'] : 25;
|
||||||
|
$mailer->SMTPSecure = !empty($_ENV['MAIL_SECURE']) ? $_ENV['MAIL_SECURE'] : '';
|
||||||
|
|
||||||
|
if (!empty($_ENV['MAIL_USER'])) {
|
||||||
|
$mailer->SMTPAuth = true;
|
||||||
|
$mailer->Username = $_ENV['MAIL_USER'];
|
||||||
|
$mailer->Password = $_ENV['MAIL_PASSWORD'];
|
||||||
|
} else {
|
||||||
|
$mailer->SMTPAuth = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$mailer->Mailer = 'mail';
|
||||||
|
}
|
||||||
|
|
||||||
|
$mailer->setFrom($_ENV['MAIL_FROM'], 'MapGuesser');
|
||||||
|
$mailer->addReplyTo($_ENV['MAIL_FROM'], 'MapGuesser');
|
||||||
|
|
||||||
|
$mailer->Sender = !empty($_ENV['MAIL_BOUNCE']) ? $_ENV['MAIL_BOUNCE'] : $_ENV['MAIL_FROM'];
|
||||||
|
$mailer->Subject = $this->subject;
|
||||||
|
$mailer->msgHTML($this->body);
|
||||||
|
|
||||||
|
foreach ($this->recipients as $recipient) {
|
||||||
|
$this->sendMail($mailer, $recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendMail(PHPMailer $mailer, array $recipient)
|
||||||
|
{
|
||||||
|
$mailer->clearAddresses();
|
||||||
|
$mailer->addAddress($recipient[0], $recipient[1]);
|
||||||
|
|
||||||
|
$mailer->send();
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-1
@@ -6,7 +6,7 @@ class User extends BaseModel implements IUser
|
|||||||
{
|
{
|
||||||
private static array $types = ['user', 'admin'];
|
private static array $types = ['user', 'admin'];
|
||||||
|
|
||||||
protected static array $fields = ['email', 'password', 'type'];
|
protected static array $fields = ['email', 'password', 'type', 'active'];
|
||||||
|
|
||||||
private string $email;
|
private string $email;
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ class User extends BaseModel implements IUser
|
|||||||
|
|
||||||
private string $type = 'user';
|
private string $type = 'user';
|
||||||
|
|
||||||
|
private bool $active = false;
|
||||||
|
|
||||||
public function setEmail(string $email): void
|
public function setEmail(string $email): void
|
||||||
{
|
{
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
@@ -36,6 +38,11 @@ class User extends BaseModel implements IUser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setActive($active): void
|
||||||
|
{
|
||||||
|
$this->active = (bool) $active;
|
||||||
|
}
|
||||||
|
|
||||||
public function getEmail(): string
|
public function getEmail(): string
|
||||||
{
|
{
|
||||||
return $this->email;
|
return $this->email;
|
||||||
@@ -51,6 +58,11 @@ class User extends BaseModel implements IUser
|
|||||||
return $this->type;
|
return $this->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getActive(): bool
|
||||||
|
{
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
public function hasPermission(int $permission): bool
|
public function hasPermission(int $permission): bool
|
||||||
{
|
{
|
||||||
switch ($permission) {
|
switch ($permission) {
|
||||||
@@ -63,6 +75,11 @@ class User extends BaseModel implements IUser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDisplayName(): string
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
public function checkPassword(string $password): bool
|
public function checkPassword(string $password): bool
|
||||||
{
|
{
|
||||||
return password_verify($password, $this->password);
|
return password_verify($password, $this->password);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class MapRepository
|
|||||||
public function getById(int $mapId)
|
public function getById(int $mapId)
|
||||||
{
|
{
|
||||||
$select = new Select(\Container::$dbConnection, 'maps');
|
$select = new Select(\Container::$dbConnection, 'maps');
|
||||||
$select->columns(['id', 'name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']);
|
$select->columns(['id', 'name', 'description', 'bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng', 'area']);
|
||||||
$select->whereId($mapId);
|
$select->whereId($mapId);
|
||||||
|
|
||||||
return $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
return $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|||||||
+15
-4
@@ -3,26 +3,37 @@
|
|||||||
use MapGuesser\Interfaces\Authentication\IUser;
|
use MapGuesser\Interfaces\Authentication\IUser;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Interfaces\Request\ISession;
|
use MapGuesser\Interfaces\Request\ISession;
|
||||||
use MapGuesser\Model\User;
|
|
||||||
|
|
||||||
class Request implements IRequest
|
class Request implements IRequest
|
||||||
{
|
{
|
||||||
|
private string $base;
|
||||||
|
|
||||||
private array $get;
|
private array $get;
|
||||||
|
|
||||||
private array $routeParams;
|
private array $routeParams = [];
|
||||||
|
|
||||||
private array $post;
|
private array $post;
|
||||||
|
|
||||||
private Session $session;
|
private Session $session;
|
||||||
|
|
||||||
public function __construct(array &$get, array &$routeParams, array &$post, array &$session)
|
public function __construct(string $base, array &$get, array &$post, array &$session)
|
||||||
{
|
{
|
||||||
|
$this->base = $base;
|
||||||
$this->get = &$get;
|
$this->get = &$get;
|
||||||
$this->routeParams = &$routeParams;
|
|
||||||
$this->post = &$post;
|
$this->post = &$post;
|
||||||
$this->session = new Session($session);
|
$this->session = new Session($session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setParsedRouteParams(array &$routeParams)
|
||||||
|
{
|
||||||
|
$this->routeParams = &$routeParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBase(): string
|
||||||
|
{
|
||||||
|
return $this->base;
|
||||||
|
}
|
||||||
|
|
||||||
public function query($key)
|
public function query($key)
|
||||||
{
|
{
|
||||||
if (isset($this->get[$key])) {
|
if (isset($this->get[$key])) {
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<?php namespace MapGuesser\Session;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use MapGuesser\Database\Query\Modify;
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
|
use SessionHandlerInterface;
|
||||||
|
use SessionIdInterface;
|
||||||
|
use SessionUpdateTimestampHandlerInterface;
|
||||||
|
|
||||||
|
class DatabaseSessionHandler implements SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface
|
||||||
|
{
|
||||||
|
private bool $exists = false;
|
||||||
|
|
||||||
|
private bool $written = false;
|
||||||
|
|
||||||
|
public function open($savePath, $sessionName): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($id): string
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection, 'sessions');
|
||||||
|
$select->columns(['data']);
|
||||||
|
$select->whereId($id);
|
||||||
|
|
||||||
|
$result = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($result === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->exists = true;
|
||||||
|
|
||||||
|
return $result['data'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($id, $data): bool
|
||||||
|
{
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||||
|
|
||||||
|
if ($this->exists) {
|
||||||
|
$modify->setId($id);
|
||||||
|
} else {
|
||||||
|
$modify->setExternalId($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$modify->set('data', $data);
|
||||||
|
$modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
$written = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id): bool
|
||||||
|
{
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||||
|
$modify->setId($id);
|
||||||
|
$modify->delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gc($maxlifetime): int
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection, 'sessions');
|
||||||
|
$select->columns(['id']);
|
||||||
|
$select->where('updated', '<', (new DateTime('-' . $maxlifetime . ' seconds'))->format('Y-m-d H:i:s'));
|
||||||
|
|
||||||
|
$result = $select->execute();
|
||||||
|
|
||||||
|
while ($session = $result->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||||
|
$modify->setId($session['id']);
|
||||||
|
$modify->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create_sid(): string
|
||||||
|
{
|
||||||
|
return hash('sha256', random_bytes(10) . microtime());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateId($id): bool
|
||||||
|
{
|
||||||
|
return preg_match('/^[a-f0-9]{64}$/', $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateTimestamp($id, $data): bool
|
||||||
|
{
|
||||||
|
if ($this->written) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$modify = new Modify(\Container::$dbConnection, 'sessions');
|
||||||
|
|
||||||
|
$modify->setId($id);
|
||||||
|
$modify->set('updated', (new DateTime())->format('Y-m-d H:i:s'));
|
||||||
|
$modify->save();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
class Bounds
|
class Bounds
|
||||||
{
|
{
|
||||||
const ONE_DEGREE_OF_LATITUDE_IN_METER = 111132.954;
|
const ONE_DEGREE_OF_LATITUDE_IN_KM = 111.132954;
|
||||||
|
|
||||||
private float $southLat = 90.0;
|
private float $southLat = 90.0;
|
||||||
private float $westLng = 180.0;
|
private float $westLng = 180.0;
|
||||||
@@ -84,9 +84,9 @@ class Bounds
|
|||||||
$dLat = $this->northLat - $this->southLat;
|
$dLat = $this->northLat - $this->southLat;
|
||||||
$dLng = $this->eastLng - $this->westLng;
|
$dLng = $this->eastLng - $this->westLng;
|
||||||
|
|
||||||
$m = $dLat * static::ONE_DEGREE_OF_LATITUDE_IN_METER;
|
$m = $dLat * static::ONE_DEGREE_OF_LATITUDE_IN_KM;
|
||||||
$a = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_METER * cos(deg2rad($this->northLat));
|
$a = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_KM * cos(deg2rad($this->northLat));
|
||||||
$c = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_METER * cos(deg2rad($this->southLat));
|
$c = $dLng * static::ONE_DEGREE_OF_LATITUDE_IN_KM * cos(deg2rad($this->southLat));
|
||||||
|
|
||||||
return $m * ($a + $c) / 2;
|
return $m * ($a + $c) / 2;
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-44
@@ -1,49 +1,61 @@
|
|||||||
<?php $cssFiles = ['/static/node_modules/leaflet/dist/leaflet.css', '/static/css/map_editor.css']; ?>
|
<?php
|
||||||
|
$cssFiles = [
|
||||||
|
'node_modules/leaflet/dist/leaflet.css',
|
||||||
|
'css/map_editor.css',
|
||||||
|
'node_modules/leaflet.markercluster/dist/MarkerCluster.css',
|
||||||
|
'node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||||
|
];
|
||||||
|
$jsFiles = [
|
||||||
|
'node_modules/leaflet/dist/leaflet.js',
|
||||||
|
'node_modules/leaflet.markercluster/dist/leaflet.markercluster.js',
|
||||||
|
'https://maps.googleapis.com/maps/api/js?key=' . $_ENV['GOOGLE_MAPS_JS_API_KEY'],
|
||||||
|
'js/map_editor.js',
|
||||||
|
];
|
||||||
|
?>
|
||||||
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
<div class="header small">
|
<div class="header small">
|
||||||
<div class="grid">
|
<h1>
|
||||||
<h1>
|
<a href="/maps" title="Back to playable maps">
|
||||||
<a href="/maps" title="Back to playable maps">
|
<img class="inline" width="1em" height="1em" src="<?= $_ENV['STATIC_ROOT'] ?>/img/icon.svg?rev=<?= REVISION ?>">
|
||||||
<img class="inline" src="/static/img/icon.svg">
|
<span>MapGuesser</span>
|
||||||
<span>MapGuesser</span>
|
</a>
|
||||||
</a>
|
</h1>
|
||||||
</h1>
|
<p class="header">
|
||||||
<p>
|
<span><a href="javascript:;" id="mapName" title="Edit map data"><?= $mapName ?></a></span><!--
|
||||||
<span class="bold"><a href="javascript:;" id="mapName"><?= $mapName ?></a></span><!--
|
--><span><!--
|
||||||
--><span><!--
|
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
--><svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<path fill-rule="evenodd" d="M8 3.5a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5H4a.5.5 0 0 1 0-1h3.5V4a.5.5 0 0 1 .5-.5z"/>
|
||||||
<path fill-rule="evenodd" d="M8 3.5a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5H4a.5.5 0 0 1 0-1h3.5V4a.5.5 0 0 1 .5-.5z"/>
|
<path fill-rule="evenodd" d="M7.5 8a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H8.5V12a.5.5 0 0 1-1 0V8z"/>
|
||||||
<path fill-rule="evenodd" d="M7.5 8a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1H8.5V12a.5.5 0 0 1-1 0V8z"/>
|
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
</svg>
|
||||||
</svg>
|
<span id="added" class="bold">0</span><!--
|
||||||
<span id="added" class="bold">0</span><!--
|
--></span><!--
|
||||||
--></span><!--
|
--><span><!--
|
||||||
--><span><!--
|
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
--><svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<path d="M15.502 1.94a.5.5 0 0 1 0 .706a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/> <path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/> <path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
</svg>
|
||||||
</svg>
|
<span id="edited" class="bold">0</span><!--
|
||||||
<span id="edited" class="bold">0</span><!--
|
--></span><!--
|
||||||
--></span><!--
|
--><span><!--
|
||||||
--><span><!--
|
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
--><svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
--><svg class="inline" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<path fill-rule="evenodd" d="M3.5 8a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1H4a.5.5 0 0 1-.5-.5z"/>
|
||||||
<path fill-rule="evenodd" d="M3.5 8a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1H4a.5.5 0 0 1-.5-.5z"/>
|
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
||||||
<path fill-rule="evenodd" d="M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
|
</svg>
|
||||||
</svg>
|
<span id="deleted" class="bold">0</span><!--
|
||||||
<span id="deleted" class="bold">0</span><!--
|
--></span>
|
||||||
--></span>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="metadata">
|
<div id="metadata" class="modal">
|
||||||
<form id="metadataForm">
|
<h2>Edit map data</h2>
|
||||||
|
<form id="metadataForm" class="marginTop">
|
||||||
<input class="fullWidth" type="text" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
|
<input class="fullWidth" type="text" name="name" value="<?= $mapName ?>" placeholder="Name of the map">
|
||||||
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
|
<textarea class="fullWidth marginTop" name="description" rows="4" placeholder="Description of the map"><?= $mapDescription ?></textarea>
|
||||||
<div style="text-align: right;">
|
<div class="right">
|
||||||
<button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
|
<button class="marginTop marginRight" type="submit">Apply</button><!--
|
||||||
<button class="marginTop" type="submit">Apply</button>
|
--><button id="closeMetadataButton" class="gray marginTop" type="button">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,7 +79,4 @@
|
|||||||
var mapBounds = <?= json_encode($bounds) ?>;
|
var mapBounds = <?= json_encode($bounds) ?>;
|
||||||
var places = <?= json_encode($places, JSON_FORCE_OBJECT) ?>;
|
var places = <?= json_encode($places, JSON_FORCE_OBJECT) ?>;
|
||||||
</script>
|
</script>
|
||||||
<script src="/static/node_modules/leaflet/dist/leaflet.js"></script>
|
|
||||||
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
|
|
||||||
<script src="/static/js/map_editor.js"></script>
|
|
||||||
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
+21
-17
@@ -1,21 +1,27 @@
|
|||||||
<?php $cssFiles = ['/static/css/game.css']; ?>
|
<?php
|
||||||
|
$cssFiles = [
|
||||||
|
'css/game.css'
|
||||||
|
];
|
||||||
|
$jsFiles = [
|
||||||
|
'https://maps.googleapis.com/maps/api/js?key=' . $_ENV['GOOGLE_MAPS_JS_API_KEY'],
|
||||||
|
'js/game.js',
|
||||||
|
];
|
||||||
|
?>
|
||||||
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
<div class="header small">
|
<div class="header small">
|
||||||
<div class="grid">
|
<h1>
|
||||||
<h1>
|
<a href="/maps" title="Back to playable maps">
|
||||||
<a href="/maps" title="Back to playable maps">
|
<img class="inline" width="1em" height="1em" src="<?= $_ENV['STATIC_ROOT'] ?>/img/icon.svg?rev=<?= REVISION ?>">
|
||||||
<img class="inline" src="/static/img/icon.svg">
|
<span>MapGuesser</span>
|
||||||
<span>MapGuesser</span>
|
</a>
|
||||||
</a>
|
</h1>
|
||||||
</h1>
|
<p class="header">
|
||||||
<p>
|
<span id="mapName" class="bold"><?= $mapName ?></span><!--
|
||||||
<span id="mapName" class="bold"><?= $mapName ?></span><!--
|
--><span>Round <span id="currentRound" class="bold"></span></span><!--
|
||||||
--><span>Round <span id="currentRound" class="bold"></span></span><!--
|
--><span>Score <span id="currentScoreSum" class="bold"></span></span>
|
||||||
--><span>Score <span id="currentScoreSum" class="bold"></span></span>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="cover"></div>
|
<div id="guessCover"></div>
|
||||||
<div id="panorama"></div>
|
<div id="panorama"></div>
|
||||||
<div id="showGuessButtonContainer">
|
<div id="showGuessButtonContainer">
|
||||||
<button id="showGuessButton" class="fullWidth">Show guess map</button>
|
<button id="showGuessButton" class="fullWidth">Show guess map</button>
|
||||||
@@ -53,6 +59,4 @@
|
|||||||
var mapId = <?= $mapId ?>;
|
var mapId = <?= $mapId ?>;
|
||||||
var mapBounds = <?= json_encode($bounds) ?>;
|
var mapBounds = <?= json_encode($bounds) ?>;
|
||||||
</script>
|
</script>
|
||||||
<script src="https://maps.googleapis.com/maps/api/js?key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>"></script>
|
|
||||||
<script src="/static/js/game.js"></script>
|
|
||||||
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
+6
-2
@@ -1,3 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
$jsFiles = [
|
||||||
|
'js/login.js',
|
||||||
|
];
|
||||||
|
?>
|
||||||
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
<?php require ROOT . '/views/templates/header.php'; ?>
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
@@ -6,12 +11,11 @@
|
|||||||
<form id="loginForm" action="/login" method="post">
|
<form id="loginForm" action="/login" method="post">
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" autofocus>
|
<input class="big fullWidth" type="email" name="email" placeholder="Email address" autofocus>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password">
|
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password">
|
||||||
<p id="loginFormError" class="formError marginTop"></p>
|
<p id="loginFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/js/login.js"></script>
|
|
||||||
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
+50
-8
@@ -1,15 +1,21 @@
|
|||||||
<?php $cssFiles = ['/static/css/maps.css']; ?>
|
<?php
|
||||||
|
$cssFiles = [
|
||||||
|
'css/maps.css'
|
||||||
|
];
|
||||||
|
?>
|
||||||
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
<?php require ROOT . '/views/templates/header.php'; ?>
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<h2>Playable maps</h2>
|
<h2>Playable maps</h2>
|
||||||
<div id="mapContainer">
|
<div id="mapContainer">
|
||||||
<?php foreach ($maps as $map) : ?>
|
<?php foreach ($maps as $map): ?>
|
||||||
<div class="mapItem">
|
<div class="mapItem">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<p class="title"><?= $map['name'] ?></p>
|
<p class="title"><?= $map['name'] ?></p>
|
||||||
</div>
|
</div>
|
||||||
<img src="https://maps.googleapis.com/maps/api/staticmap?size=350x175&visible=<?= $map['bound_south_lat'] . ',' . $map['bound_west_lng'] . '|' . $map['bound_north_lat'] . ',' . $map['bound_east_lng'] ?>&key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>">
|
<div class="imgContainer">
|
||||||
|
<img src="https://maps.googleapis.com/maps/api/staticmap?size=350x175&visible=<?= $map['bound_south_lat'] . ',' . $map['bound_west_lng'] . '|' . $map['bound_north_lat'] . ',' . $map['bound_east_lng'] ?>&key=<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>" alt="Map area">
|
||||||
|
</div>
|
||||||
<div class="inner">
|
<div class="inner">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<p>
|
<p>
|
||||||
@@ -32,18 +38,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<?php if ($isAdmin): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<div class="buttonContainer">
|
<div class="buttonContainer">
|
||||||
<a class="button fullWidth noRightRadius" href="game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
<a class="button fullWidth noRightRadius" href="/game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||||
<a class="button yellow fullWidth noLeftRadius noRightRadius" href="admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit</a>
|
<a class="button yellow fullWidth noLeftRadius noRightRadius" href="/admin/mapEditor/<?= $map['id']; ?>" title="Edit map '<?= $map['name'] ?>'">Edit</a>
|
||||||
<button class="button red fullWidth noLeftRadius" title="Delete map '<?= $map['name'] ?>'">Delete</button>
|
<button class="button red fullWidth noLeftRadius deleteButton" data-map-id="<?= $map['id'] ?>" data-map-name="<?= htmlspecialchars($map['name']) ?>" title="Delete map '<?= $map['name'] ?>'">Delete</button>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<a class="button fullWidth" href="game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
<a class="button fullWidth" href="/game/<?= $map['id']; ?>" title="Play map '<?= $map['name'] ?>'">Play this map</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if ($isAdmin): ?>
|
<?php if ($isAdmin): ?>
|
||||||
<div class="mapItem new">
|
<div class="mapItem new">
|
||||||
<a class="button green fullWidth" href="admin/mapEditor" title="Add new map">
|
<a class="button green fullWidth" href="/admin/mapEditor" title="Add new map">
|
||||||
Add new map
|
Add new map
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,4 +63,40 @@
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if ($isAdmin): ?>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
Maps = {
|
||||||
|
deleteMap: function(mapId, mapName) {
|
||||||
|
MapGuesser.showModalWithContent('Delete map', 'Are you sure you want to delete map \'' + mapName + '\'?', [{
|
||||||
|
type: 'button',
|
||||||
|
classNames: ['red'],
|
||||||
|
text: 'Delete',
|
||||||
|
onclick: function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', '/admin/deleteMap/' + mapId, function () {
|
||||||
|
if (this.response.error) {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
|
||||||
|
//TODO: handle this error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var buttons = document.getElementById('mapContainer').getElementsByClassName('deleteButton');
|
||||||
|
for (var button of buttons) {
|
||||||
|
button.onclick = function() {
|
||||||
|
Maps.deleteMap(this.dataset.mapId, this.dataset.mapName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
$jsFiles = [
|
||||||
|
'js/profile.js',
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
|
<div class="main">
|
||||||
|
<h2>Profile</h2>
|
||||||
|
<div class="box">
|
||||||
|
<form id="profileForm" action="/profile" method="post">
|
||||||
|
<?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
|
||||||
|
<input class="big fullWidth" type="email" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
|
||||||
|
<input class="big fullWidth marginTop" type="password" name="password_new" placeholder="New password" autofocus>
|
||||||
|
<input class="big fullWidth marginTop" type="password" name="password_new_confirm" placeholder="New password confirmation">
|
||||||
|
<hr>
|
||||||
|
<input class="big fullWidth" type="password" name="password" placeholder="Current password">
|
||||||
|
<p id="profileFormError" class="formError justify marginTop"></p>
|
||||||
|
<div class="right marginTop">
|
||||||
|
<button type="submit" name="save" disabled>Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
|
<div class="main">
|
||||||
|
<h2>Account activation</h2>
|
||||||
|
<div class="box">
|
||||||
|
<p class="error justify">Activation failed. Please check the link you entered or retry <a href="/signup" title="Sign up">sign up</a>!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
|
<div class="main">
|
||||||
|
<h2>Account cancellation</h2>
|
||||||
|
<div class="box">
|
||||||
|
<?php if ($success) : ?>
|
||||||
|
<p class="justify">Cancellation was successfull. You can <a href="/signup" title="Sign up">sign up</a> any time if you want!</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="error justify">Cancellation failed. Please check the link you entered! Maybe the account was already deleted, in this case no further action is required.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
$jsFiles = [
|
||||||
|
'js/signup.js',
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
|
<div class="main">
|
||||||
|
<h2>Sign up</h2>
|
||||||
|
<div class="box">
|
||||||
|
<form id="signupForm" action="/signup" method="post">
|
||||||
|
<input class="big fullWidth" type="email" name="email" placeholder="Email address" autofocus>
|
||||||
|
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password">
|
||||||
|
<input class="big fullWidth marginTop" type="password" name="password_confirm" placeholder="Password confirmation">
|
||||||
|
<p id="signupFormError" class="formError justify marginTop"></p>
|
||||||
|
<div class="right marginTop">
|
||||||
|
<button type="submit">Sign up</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -1,8 +1,23 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>
|
<h1>
|
||||||
<a href="/" title="MapGuesser">
|
<a href="/" title="MapGuesser">
|
||||||
<img class="inline" src="/static/img/icon.svg">
|
<img class="inline" width="1em" height="1em" src="<?= $_ENV['STATIC_ROOT'] ?>/img/icon.svg?rev=<?= REVISION ?>">
|
||||||
MapGuesser
|
<span>MapGuesser</span>
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
<p class="header">
|
||||||
|
<?php if (Container::$request->user()) : ?>
|
||||||
|
<span><a href="/profile" title="Profile">
|
||||||
|
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||||
|
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
|
||||||
|
</svg>
|
||||||
|
<?= Container::$request->user()->getDisplayName() ?><!--
|
||||||
|
--></a></span><!--
|
||||||
|
--><span><a href="/logout" title="Logout">Logout</a></span>
|
||||||
|
<?php else : ?>
|
||||||
|
<span><a href="/signup" title="Login">Sign up</a></span><!--
|
||||||
|
--><span><a href="/login" title="Login">Login</a></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
<script>
|
<script>
|
||||||
(function () {
|
const STATIC_ROOT = '<?= $_ENV['STATIC_ROOT'] ?>';
|
||||||
var anchors = document.getElementsByTagName('a');
|
const REVISION = '<?= REVISION ?>';
|
||||||
for (var i = 0; i < anchors.length; i++) {
|
const ANTI_CSRF_TOKEN = '<?= $_SESSION['anti_csrf_token'] ?>';
|
||||||
var a = anchors[i];
|
|
||||||
if (a.href !== 'javascript:;' && a.target !== '_blank') {
|
|
||||||
a.onclick = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
</script>
|
||||||
|
<script src="<?= $_ENV['STATIC_ROOT'] ?>/js/mapguesser.js?rev=<?= REVISION ?>"></script>
|
||||||
|
<?php if (isset($jsFiles)) : ?>
|
||||||
|
<?php foreach ($jsFiles as $jsFile) : ?>
|
||||||
|
<?php
|
||||||
|
if (!preg_match('/^http(s)?/', $jsFile)) {
|
||||||
|
$jsFile = $_ENV['STATIC_ROOT'] . '/' . $jsFile . '?rev=' . REVISION;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<script src="<?= $jsFile ?>"></script>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -4,19 +4,30 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>MapGuesser</title>
|
<title>MapGuesser</title>
|
||||||
<link href="/static/css/mapguesser.css" rel="stylesheet">
|
<link href="<?= $_ENV['STATIC_ROOT'] ?>/css/mapguesser.css?rev=<?= REVISION ?>" rel="stylesheet">
|
||||||
<?php if (isset($cssFiles)) : ?>
|
<?php if (isset($cssFiles)) : ?>
|
||||||
<?php foreach ($cssFiles as $cssFile) : ?>
|
<?php foreach ($cssFiles as $cssFile) : ?>
|
||||||
|
<?php
|
||||||
|
if (!preg_match('/^http(s)?/', $cssFile)) {
|
||||||
|
$cssFile = $_ENV['STATIC_ROOT'] . '/' . $cssFile . '?rev=' . REVISION;
|
||||||
|
}
|
||||||
|
?>
|
||||||
<link href="<?= $cssFile ?>" rel="stylesheet">
|
<link href="<?= $cssFile ?>" rel="stylesheet">
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;500&display=swap" rel="stylesheet">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/static/img/favicon/192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="<?= $_ENV['STATIC_ROOT'] ?>/img/favicon/192x192.png?rev=<?= REVISION ?>">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="/static/img/favicon/96x96.png">
|
<link rel="icon" type="image/png" sizes="96x96" href="<?= $_ENV['STATIC_ROOT'] ?>/img/favicon/96x96.png?rev=<?= REVISION ?>">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon/32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="<?= $_ENV['STATIC_ROOT'] ?>/img/favicon/32x32.png?rev=<?= REVISION ?>">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon/16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="<?= $_ENV['STATIC_ROOT'] ?>/img/favicon/16x16.png?rev=<?= REVISION ?>">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<img src="/static/img/loading.svg">
|
<img src="<?= $_ENV['STATIC_ROOT'] ?>/img/loading.svg?rev=<?= REVISION ?>" width="64" height="64">
|
||||||
|
</div>
|
||||||
|
<div id="cover"></div>
|
||||||
|
<div id="modal" class="modal">
|
||||||
|
<h2 id="modalTitle"></h2>
|
||||||
|
<p id="modalText" class="justify marginTop"></p>
|
||||||
|
<div id="modalButtons" class="right"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require 'main.php';
|
||||||
|
|
||||||
|
if (!empty($_ENV['DEV'])) {
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
} else {
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
Container::$routeCollection = new MapGuesser\Routing\RouteCollection();
|
||||||
|
|
||||||
|
Container::$routeCollection->get('index', '', [MapGuesser\Controller\HomeController::class, 'getIndex']);
|
||||||
|
Container::$routeCollection->get('login', 'login', [MapGuesser\Controller\LoginController::class, 'getLoginForm']);
|
||||||
|
Container::$routeCollection->post('login-action', 'login', [MapGuesser\Controller\LoginController::class, 'login']);
|
||||||
|
Container::$routeCollection->get('signup', 'signup', [MapGuesser\Controller\SignupController::class, 'getSignupForm']);
|
||||||
|
Container::$routeCollection->post('signup-action', 'signup', [MapGuesser\Controller\SignupController::class, 'signup']);
|
||||||
|
Container::$routeCollection->get('signup.activate', 'signup/activate/{token}', [MapGuesser\Controller\SignupController::class, 'activate']);
|
||||||
|
Container::$routeCollection->get('signup.cancel', 'signup/cancel/{token}', [MapGuesser\Controller\SignupController::class, 'cancel']);
|
||||||
|
Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']);
|
||||||
|
Container::$routeCollection->get('profile', 'profile', [MapGuesser\Controller\UserController::class, 'getProfile']);
|
||||||
|
Container::$routeCollection->post('profile-action', 'profile', [MapGuesser\Controller\UserController::class, 'saveProfile']);
|
||||||
|
Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']);
|
||||||
|
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('game', '{mapId}', [MapGuesser\Controller\GameController::class, 'getGame']);
|
||||||
|
$routeCollection->get('game-json', '{mapId}/json', [MapGuesser\Controller\GameController::class, 'getGameJson']);
|
||||||
|
$routeCollection->get('newPlace-json', '{mapId}/newPlace.json', [MapGuesser\Controller\GameFlowController::class, 'getNewPlace']);
|
||||||
|
$routeCollection->post('guess-json', '{mapId}/guess.json', [MapGuesser\Controller\GameFlowController::class, 'evaluateGuess']);
|
||||||
|
});
|
||||||
|
Container::$routeCollection->group('admin', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('admin.mapEditor', 'mapEditor/{mapId?}', [MapGuesser\Controller\MapAdminController::class, 'getMapEditor']);
|
||||||
|
$routeCollection->get('admin.place', 'place.json/{placeId}', [MapGuesser\Controller\MapAdminController::class, 'getPlace']);
|
||||||
|
$routeCollection->post('admin.saveMap', 'saveMap/{mapId}/json', [MapGuesser\Controller\MapAdminController::class, 'saveMap']);
|
||||||
|
$routeCollection->post('admin.deleteMap', 'deleteMap/{mapId}', [MapGuesser\Controller\MapAdminController::class, 'deleteMap']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Container::$sessionHandler = new MapGuesser\Session\DatabaseSessionHandler();
|
||||||
|
|
||||||
|
session_set_save_handler(Container::$sessionHandler, true);
|
||||||
|
session_start([
|
||||||
|
'gc_maxlifetime' => 604800,
|
||||||
|
'cookie_lifetime' => 604800,
|
||||||
|
'cookie_httponly' => true,
|
||||||
|
'cookie_samesite' => 'Lax'
|
||||||
|
]);
|
||||||
|
|
||||||
|
Container::$request = new MapGuesser\Request\Request($_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'], $_GET, $_POST, $_SESSION);
|
||||||
|
|
||||||
|
if (!Container::$request->session()->has('anti_csrf_token')) {
|
||||||
|
Container::$request->session()->set('anti_csrf_token', hash('sha256', random_bytes(10) . microtime()));
|
||||||
|
}
|
||||||
Reference in new issue
Block a user