Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4769b62dd | ||
|
|
bee991e300 | ||
|
|
ddc56426c2 | ||
|
|
c06dd1e1d2 | ||
|
|
fd657b4244 | ||
|
|
532f099ec9 | ||
|
|
ec969167e4 | ||
|
|
e4a51c139f | ||
|
|
27115e44b6 | ||
|
|
45904a98c2 | ||
|
|
b1ae7391e7 | ||
|
|
8987b563dd | ||
|
|
70d8807f38 | ||
|
|
910fdddf34 | ||
|
|
87c4c06aa6 | ||
|
|
ad24f8ac28 | ||
|
|
e2067627c4 | ||
|
|
a8419535e1 | ||
|
|
725d14400b |
No files matched your search
@@ -1,5 +1,63 @@
|
|||||||
# MapGuesser
|
# MapGuesser
|
||||||
|
|
||||||
This is the MapGuesser Application project.
|
This is the MapGuesser Application project. This is a game about guessing where you are based on a street view panorama - inspired by existing applications.
|
||||||
|
|
||||||
License: GNU AGPL 3.0
|
## Installation
|
||||||
|
|
||||||
|
### Clone the Git repository
|
||||||
|
|
||||||
|
The first step is obviously cloning the repository to your machine:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://bitbucket.org/esoko/mapguesser.git
|
||||||
|
```
|
||||||
|
|
||||||
|
All the commands listed here should be executed from the repository root.
|
||||||
|
|
||||||
|
### (Optional) Setup Docker stack
|
||||||
|
|
||||||
|
The easiest way to build up a fully working application with web server and database is to use Docker Compose with the included `docker-compose.yml`.
|
||||||
|
|
||||||
|
All you have to do is executing the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
Attach shell to the container of `mapguesser_app`. All of the following commands should be executed there.
|
||||||
|
|
||||||
|
**If you don't use the Docker stack you need to install your environment manually. Check `docker-compose.yml` and `docker/Dockerfile` to see the system requirements.**
|
||||||
|
|
||||||
|
### Initialize project
|
||||||
|
|
||||||
|
This command installes all of the Composer requirements and creates a copy of the example `.env` file.
|
||||||
|
|
||||||
|
```
|
||||||
|
composer create-project
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set environment variables
|
||||||
|
|
||||||
|
The `.env` file contains several environment variables that are needed by the application to work properly. These should be configured for your environment.
|
||||||
|
|
||||||
|
**You should set here the API keys that enable playing the game. Without these API keys the application cannot work well. To get Google API keys visit this page: https://console.developers.google.com/**
|
||||||
|
|
||||||
|
One very important variable is `DEV`. This indicates that the application operates in development (staging) and not in produciton mode.
|
||||||
|
|
||||||
|
If you install the application in the Docker stack for development (staging) environment, only the variables for external dependencies (API keys, map attribution, etc.) should be adapted. All other variables (for DB connection, static root, mailing, etc.) are fine with the default value.
|
||||||
|
|
||||||
|
### Finalize installation
|
||||||
|
|
||||||
|
After you set the environment variables in the `.env` file, execute the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
scripts/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**And you are done!** The application is ready to use and develop.
|
||||||
|
|
||||||
|
If you installed it in the Docker stack, you can reach it on http://localhost. The mails that are sent by the application can be found on http://localhost:8080/. If needed, the database server can be directly reached on localhost:3306.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*License: **GNU AGPL 3.0**. Full license text can be found in file `LICENSE`.*
|
||||||
@@ -11,6 +11,8 @@ services:
|
|||||||
- 'mail'
|
- 'mail'
|
||||||
mariadb:
|
mariadb:
|
||||||
image: mariadb:10.3
|
image: mariadb:10.3
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
volumes:
|
volumes:
|
||||||
- mysql:/var/lib/mysql
|
- mysql:/var/lib/mysql
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
(function () {
|
|
||||||
var form = document.getElementById('loginForm');
|
|
||||||
|
|
||||||
form.onsubmit = function (e) {
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var formData = new FormData(form);
|
|
||||||
|
|
||||||
MapGuesser.httpRequest('POST', form.action, function () {
|
|
||||||
if (this.response.error) {
|
|
||||||
if (this.response.error === 'user_not_found') {
|
|
||||||
window.location.replace('/signup');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorText;
|
|
||||||
switch (this.response.error) {
|
|
||||||
case 'password_too_short':
|
|
||||||
errorText = 'The given password is too short. Please choose a password that is at least 6 characters long!'
|
|
||||||
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;
|
|
||||||
case 'password_not_match':
|
|
||||||
errorText = 'The given password is wrong.'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
|
||||||
|
|
||||||
var loginFormError = document.getElementById('loginFormError');
|
|
||||||
loginFormError.style.display = 'block';
|
|
||||||
loginFormError.innerHTML = errorText;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.replace('/');
|
|
||||||
}, formData);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
(function () {
|
||||||
|
document.getElementById('cancelGoogleSignupButton').onclick = function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', '/signup/google/reset', function () {
|
||||||
|
window.location.replace('/signup');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
(function () {
|
||||||
|
var resetSignupButton = document.getElementById('resetSignupButton');
|
||||||
|
if (resetSignupButton) {
|
||||||
|
resetSignupButton.onclick = function () {
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', '/signup/reset', function () {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -15,7 +15,7 @@ var MapGuesser = {
|
|||||||
document.head.appendChild(script);
|
document.head.appendChild(script);
|
||||||
|
|
||||||
window.dataLayer = window.dataLayer || [];
|
window.dataLayer = window.dataLayer || [];
|
||||||
function gtag(){dataLayer.push(arguments);}
|
function gtag() { dataLayer.push(arguments); }
|
||||||
gtag('js', new Date());
|
gtag('js', new Date());
|
||||||
gtag('config', GOOGLE_ANALITICS_ID);
|
gtag('config', GOOGLE_ANALITICS_ID);
|
||||||
},
|
},
|
||||||
@@ -66,6 +66,52 @@ var MapGuesser = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setOnsubmitForForm: function (form) {
|
||||||
|
form.onsubmit = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
|
var formData = new FormData(form);
|
||||||
|
var formError = form.getElementsByClassName('formError')[0];
|
||||||
|
var pageLeaveOnSuccess = form.dataset.redirectOnSuccess || form.dataset.reloadOnSuccess;
|
||||||
|
|
||||||
|
MapGuesser.httpRequest('POST', form.action, function () {
|
||||||
|
if (!pageLeaveOnSuccess) {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.response.error) {
|
||||||
|
if (pageLeaveOnSuccess) {
|
||||||
|
document.getElementById('loading').style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
|
||||||
|
formError.style.display = 'block';
|
||||||
|
formError.innerHTML = this.response.error.errorText;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.response.redirect) {
|
||||||
|
window.location.replace(this.response.redirect.target);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pageLeaveOnSuccess) {
|
||||||
|
formError.style.display = 'none';
|
||||||
|
form.reset();
|
||||||
|
} else {
|
||||||
|
if (form.dataset.redirectOnSuccess) {
|
||||||
|
window.location.replace(form.dataset.redirectOnSuccess);
|
||||||
|
} else if (form.dataset.reloadOnSuccess) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, formData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
showModal: function (id) {
|
showModal: function (id) {
|
||||||
document.getElementById(id).style.visibility = 'visible';
|
document.getElementById(id).style.visibility = 'visible';
|
||||||
document.getElementById('cover').style.visibility = 'visible';
|
document.getElementById('cover').style.visibility = 'visible';
|
||||||
@@ -136,11 +182,35 @@ var MapGuesser = {
|
|||||||
document.getElementById('cover').style.visibility = 'hidden';
|
document.getElementById('cover').style.visibility = 'hidden';
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleDisableOnChange: function (input, button) {
|
observeInput: function (input, buttonToToggle) {
|
||||||
if (input.defaultValue !== input.value) {
|
if (input.defaultValue !== input.value) {
|
||||||
button.disabled = false;
|
buttonToToggle.disabled = false;
|
||||||
} else {
|
} else {
|
||||||
button.disabled = true;
|
buttonToToggle.disabled = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
observeInputsInForm: function (form, observedInputs) {
|
||||||
|
for (var i = 0; i < observedInputs.length; i++) {
|
||||||
|
var input = form.elements[observedInputs[i]];
|
||||||
|
|
||||||
|
switch (input.tagName) {
|
||||||
|
case 'INPUT':
|
||||||
|
case 'TEXTAREA':
|
||||||
|
input.oninput = function () {
|
||||||
|
MapGuesser.observeInput(this, form.elements.submit);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'SELECT':
|
||||||
|
input.onchange = function () {
|
||||||
|
MapGuesser.observeInput(this, form.elements.submit);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form.onreset = function () {
|
||||||
|
form.elements.submit.disabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -156,6 +226,21 @@ var MapGuesser = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var forms = document.getElementsByTagName('form');
|
||||||
|
for (var i = 0; i < forms.length; i++) {
|
||||||
|
var form = forms[i];
|
||||||
|
|
||||||
|
if (form.dataset.noSubmit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapGuesser.setOnsubmitForForm(form);
|
||||||
|
|
||||||
|
if (form.dataset.observeInputs) {
|
||||||
|
MapGuesser.observeInputsInForm(form, form.dataset.observeInputs.split(','));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('cover').onclick = function () {
|
document.getElementById('cover').onclick = function () {
|
||||||
MapGuesser.hideModal();
|
MapGuesser.hideModal();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
(function () {
|
||||||
|
var Maps = {
|
||||||
|
descriptionDivs: null,
|
||||||
|
|
||||||
|
addStaticMaps: function () {
|
||||||
|
var imgContainers = document.getElementById('mapContainer').getElementsByClassName('imgContainer');
|
||||||
|
for (var i = 0; i < imgContainers.length; i++) {
|
||||||
|
var imgContainer = imgContainers[i];
|
||||||
|
|
||||||
|
var imgSrc = 'https://maps.googleapis.com/maps/api/staticmap?size=350x175&' +
|
||||||
|
'scale=' + (window.devicePixelRatio >= 2 ? 2 : 1) + '&' +
|
||||||
|
'visible=' + imgContainer.dataset.boundSouthLat + ',' + imgContainer.dataset.boundWestLng + '|' +
|
||||||
|
imgContainer.dataset.boundNorthLat + ',' + imgContainer.dataset.boundEastLng +
|
||||||
|
'&key=' + GOOGLE_MAPS_JS_API_KEY;
|
||||||
|
|
||||||
|
imgContainer.style.backgroundImage = 'url("' + imgSrc + '")';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initializeDescriptionDivs: function () {
|
||||||
|
Maps.descriptionDivs = document.getElementById('mapContainer').getElementsByClassName('description');
|
||||||
|
|
||||||
|
for (var i = 0; i < Maps.descriptionDivs.length; i++) {
|
||||||
|
var description = Maps.descriptionDivs[i];
|
||||||
|
var boundingClientRect = description.getBoundingClientRect();
|
||||||
|
|
||||||
|
description.defaultHeight = boundingClientRect.height;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
calculateDescriptionDivHeights: function () {
|
||||||
|
var currentY;
|
||||||
|
var rows = [];
|
||||||
|
for (var i = 0; i < Maps.descriptionDivs.length; i++) {
|
||||||
|
var description = Maps.descriptionDivs[i];
|
||||||
|
var boundingClientRect = description.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (currentY !== boundingClientRect.y) {
|
||||||
|
rows.push([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
rows[rows.length - 1].push(description);
|
||||||
|
|
||||||
|
currentY = boundingClientRect.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < rows.length; i++) {
|
||||||
|
var row = rows[i];
|
||||||
|
|
||||||
|
var maxHeight = 0;
|
||||||
|
for (var j = 0; j < row.length; j++) {
|
||||||
|
var description = row[j];
|
||||||
|
|
||||||
|
if (description.defaultHeight > maxHeight) {
|
||||||
|
maxHeight = description.defaultHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j = 0; j < row.length; j++) {
|
||||||
|
var description = row[j];
|
||||||
|
|
||||||
|
description.style.height = maxHeight + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Maps.addStaticMaps();
|
||||||
|
|
||||||
|
Maps.initializeDescriptionDivs();
|
||||||
|
Maps.calculateDescriptionDivHeights();
|
||||||
|
|
||||||
|
window.onresize = function () {
|
||||||
|
Maps.calculateDescriptionDivHeights();
|
||||||
|
};
|
||||||
|
})();
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
(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 i = 0; i < buttons.length; i++) {
|
||||||
|
var button = buttons[i];
|
||||||
|
|
||||||
|
button.onclick = function () {
|
||||||
|
Maps.deleteMap(this.dataset.mapId, this.dataset.mapName);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
(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 'password_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;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('profileFormError').style.display = 'none';
|
|
||||||
form.reset();
|
|
||||||
form.elements.save.disabled = true;
|
|
||||||
}, formData);
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
(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 () {
|
|
||||||
if (this.response.error) {
|
|
||||||
if (this.response.error === 'user_found') {
|
|
||||||
window.location.replace('/');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorText;
|
|
||||||
switch (this.response.error) {
|
|
||||||
case 'email_not_valid':
|
|
||||||
errorText = 'The given email address is not valid.'
|
|
||||||
break;
|
|
||||||
case 'password_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_user_not_active':
|
|
||||||
errorText = 'There is a user already registered with the given email address. Please check your email and click on the activation link!';
|
|
||||||
break;
|
|
||||||
case 'user_found_password_not_match':
|
|
||||||
errorText = 'There is a user already registered with the given email address, but the given password is wrong.'
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('loading').style.visibility = 'hidden';
|
|
||||||
|
|
||||||
var signupFormError = document.getElementById('signupFormError');
|
|
||||||
signupFormError.style.display = 'block';
|
|
||||||
signupFormError.innerHTML = errorText;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.replace('/signup/success');
|
|
||||||
}, formData);
|
|
||||||
};
|
|
||||||
|
|
||||||
var resetSignupButton = document.getElementById('resetSignupButton');
|
|
||||||
if (resetSignupButton) {
|
|
||||||
resetSignupButton.onclick = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
|
||||||
|
|
||||||
MapGuesser.httpRequest('POST', '/signup/reset', function () {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php namespace MapGuesser\Controller;
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
|
use MapGuesser\Http\Request;
|
||||||
use MapGuesser\Interfaces\Request\IRequest;
|
use MapGuesser\Interfaces\Request\IRequest;
|
||||||
use MapGuesser\Interfaces\Response\IContent;
|
use MapGuesser\Interfaces\Response\IContent;
|
||||||
use MapGuesser\Interfaces\Response\IRedirect;
|
use MapGuesser\Interfaces\Response\IRedirect;
|
||||||
@@ -49,7 +50,7 @@ class LoginController
|
|||||||
|
|
||||||
$this->request->session()->set('oauth_state', $state);
|
$this->request->session()->set('oauth_state', $state);
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth();
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
$url = $oAuth->getDialogUrl($state, $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
|
$url = $oAuth->getDialogUrl($state, $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
|
||||||
|
|
||||||
return new Redirect($url, IRedirect::TEMPORARY);
|
return new Redirect($url, IRedirect::TEMPORARY);
|
||||||
@@ -107,7 +108,7 @@ class LoginController
|
|||||||
|
|
||||||
if ($user === null) {
|
if ($user === null) {
|
||||||
if (strlen($this->request->post('password')) < 6) {
|
if (strlen($this->request->post('password')) < 6) {
|
||||||
$data = ['error' => 'password_too_short'];
|
$data = ['error' => ['errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,17 +117,17 @@ class LoginController
|
|||||||
|
|
||||||
$this->request->session()->set('tmp_user_data', ['email' => $this->request->post('email'), 'password_hashed' => $tmpUser->getPassword()]);
|
$this->request->session()->set('tmp_user_data', ['email' => $this->request->post('email'), 'password_hashed' => $tmpUser->getPassword()]);
|
||||||
|
|
||||||
$data = ['error' => 'user_not_found'];
|
$data = ['redirect' => ['target' => '/' . \Container::$routeCollection->getRoute('signup')->generateLink()]];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$user->getActive()) {
|
if (!$user->getActive()) {
|
||||||
$data = ['error' => 'user_not_active'];
|
$data = ['error' => ['errorText' => 'User found with the given email address, but the account is not activated. Please check your email and click on the activation link!']];
|
||||||
return new JsonContent($data);
|
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' => ['errorText' => 'The given password is wrong.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ class LoginController
|
|||||||
return new HtmlContent('login/google_login', $data);
|
return new HtmlContent('login/google_login', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth();
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
$tokenData = $oAuth->getToken($this->request->query('code'), $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
|
$tokenData = $oAuth->getToken($this->request->query('code'), $this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink());
|
||||||
|
|
||||||
if (!isset($tokenData['id_token'])) {
|
if (!isset($tokenData['id_token'])) {
|
||||||
@@ -186,7 +187,7 @@ class LoginController
|
|||||||
public function signup(): IContent
|
public function signup(): IContent
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if ($this->request->user() !== null) {
|
||||||
$data = ['error' => 'logged_in'];
|
$data = ['redirect' => ['target' => '/' . \Container::$routeCollection->getRoute('home')->generateLink()]];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,21 +196,21 @@ class LoginController
|
|||||||
if ($user !== null) {
|
if ($user !== null) {
|
||||||
if ($user->getActive()) {
|
if ($user->getActive()) {
|
||||||
if (!$user->checkPassword($this->request->post('password'))) {
|
if (!$user->checkPassword($this->request->post('password'))) {
|
||||||
$data = ['error' => 'user_found_password_not_match'];
|
$data = ['error' => ['errorText' => 'There is a user already registered with the given email address, but the given password is wrong.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->request->setUser($user);
|
$this->request->setUser($user);
|
||||||
|
|
||||||
$data = ['error' => 'user_found'];
|
$data = ['redirect' => ['target' => '/' . \Container::$routeCollection->getRoute('index')->generateLink()]];
|
||||||
} else {
|
} else {
|
||||||
$data = ['error' => 'user_found_user_not_active'];
|
$data = ['error' => ['errorText' => 'There is a user already registered with the given email address. Please check your email and click on the activation link!']];
|
||||||
}
|
}
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
|
if (filter_var($this->request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
|
||||||
$data = ['error' => 'email_not_valid'];
|
$data = ['error' => ['errorText' => 'The given email address is not valid.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,17 +221,17 @@ class LoginController
|
|||||||
$tmpUser->setPassword($tmpUserData['password_hashed']);
|
$tmpUser->setPassword($tmpUserData['password_hashed']);
|
||||||
|
|
||||||
if (!$tmpUser->checkPassword($this->request->post('password'))) {
|
if (!$tmpUser->checkPassword($this->request->post('password'))) {
|
||||||
$data = ['error' => 'passwords_not_match'];
|
$data = ['error' => ['errorText' => 'The given passwords do not match.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (strlen($this->request->post('password')) < 6) {
|
if (strlen($this->request->post('password')) < 6) {
|
||||||
$data = ['error' => 'password_too_short'];
|
$data = ['error' => ['errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->post('password') !== $this->request->post('password_confirm')) {
|
if ($this->request->post('password') !== $this->request->post('password_confirm')) {
|
||||||
$data = ['error' => 'passwords_not_match'];
|
$data = ['error' => ['errorText' => 'The given passwords do not match.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
<?php namespace MapGuesser\Controller;
|
<?php namespace MapGuesser\Controller;
|
||||||
|
|
||||||
|
use MapGuesser\Database\Query\Select;
|
||||||
use MapGuesser\Interfaces\Authorization\ISecured;
|
use MapGuesser\Interfaces\Authorization\ISecured;
|
||||||
|
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\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
use MapGuesser\PersistentData\Model\User;
|
use MapGuesser\PersistentData\Model\User;
|
||||||
|
use MapGuesser\PersistentData\Model\UserConfirmation;
|
||||||
|
use MapGuesser\Repository\UserConfirmationRepository;
|
||||||
use MapGuesser\Response\HtmlContent;
|
use MapGuesser\Response\HtmlContent;
|
||||||
use MapGuesser\Response\JsonContent;
|
use MapGuesser\Response\JsonContent;
|
||||||
|
|
||||||
@@ -14,10 +18,13 @@ class UserController implements ISecured
|
|||||||
|
|
||||||
private PersistentDataManager $pdm;
|
private PersistentDataManager $pdm;
|
||||||
|
|
||||||
|
private UserConfirmationRepository $userConfirmationRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct(IRequest $request)
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->pdm = new PersistentDataManager();
|
$this->pdm = new PersistentDataManager();
|
||||||
|
$this->userConfirmationRepository = new UserConfirmationRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@@ -27,7 +34,7 @@ class UserController implements ISecured
|
|||||||
return $user !== null;
|
return $user !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getProfile(): IContent
|
public function getAccount(): IContent
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
@@ -35,10 +42,21 @@ class UserController implements ISecured
|
|||||||
$user = $this->request->user();
|
$user = $this->request->user();
|
||||||
|
|
||||||
$data = ['user' => $user->toArray()];
|
$data = ['user' => $user->toArray()];
|
||||||
return new HtmlContent('profile', $data);
|
return new HtmlContent('account/account', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveProfile(): IContent
|
public function getDeleteAccount(): IContent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = $this->request->user();
|
||||||
|
|
||||||
|
$data = ['user' => $user->toArray()];
|
||||||
|
return new HtmlContent('account/delete', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveAccount(): IContent
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
@@ -46,18 +64,18 @@ class UserController implements ISecured
|
|||||||
$user = $this->request->user();
|
$user = $this->request->user();
|
||||||
|
|
||||||
if (!$user->checkPassword($this->request->post('password'))) {
|
if (!$user->checkPassword($this->request->post('password'))) {
|
||||||
$data = ['error' => 'password_not_match'];
|
$data = ['error' => ['errorText' => 'The given current password is wrong.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($this->request->post('password_new')) > 0) {
|
if (strlen($this->request->post('password_new')) > 0) {
|
||||||
if (strlen($this->request->post('password_new')) < 6) {
|
if (strlen($this->request->post('password_new')) < 6) {
|
||||||
$data = ['error' => 'password_too_short'];
|
$data = ['error' => ['errorText' => 'The given new password is too short. Please choose a password that is at least 6 characters long!']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) {
|
if ($this->request->post('password_new') !== $this->request->post('password_new_confirm')) {
|
||||||
$data = ['error' => 'passwords_not_match'];
|
$data = ['error' => ['errorText' => 'The given new passwords do not match.']];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,4 +87,30 @@ class UserController implements ISecured
|
|||||||
$data = ['success' => true];
|
$data = ['success' => true];
|
||||||
return new JsonContent($data);
|
return new JsonContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function deleteAccount(): IContent
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = $this->request->user();
|
||||||
|
|
||||||
|
if (!$user->checkPassword($this->request->post('password'))) {
|
||||||
|
$data = ['error' => ['errorText' => 'The given current password is wrong.']];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$dbConnection->startTransaction();
|
||||||
|
|
||||||
|
foreach ($this->userConfirmationRepository->getByUser($user) as $userConfirmation) {
|
||||||
|
$this->pdm->deleteFromDb($userConfirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pdm->deleteFromDb($user);
|
||||||
|
|
||||||
|
\Container::$dbConnection->commit();
|
||||||
|
|
||||||
|
$data = ['success' => true];
|
||||||
|
return new JsonContent($data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+16
-7
@@ -1,11 +1,10 @@
|
|||||||
<?php namespace MapGuesser\Http;
|
<?php namespace MapGuesser\Http;
|
||||||
|
|
||||||
class Request
|
use MapGuesser\Interfaces\Http\IRequest;
|
||||||
|
use MapGuesser\Interfaces\Http\IResponse;
|
||||||
|
|
||||||
|
class Request implements IRequest
|
||||||
{
|
{
|
||||||
const HTTP_GET = 0;
|
|
||||||
|
|
||||||
const HTTP_POST = 1;
|
|
||||||
|
|
||||||
private string $url;
|
private string $url;
|
||||||
|
|
||||||
private int $method;
|
private int $method;
|
||||||
@@ -14,12 +13,22 @@ class Request
|
|||||||
|
|
||||||
private array $headers = [];
|
private array $headers = [];
|
||||||
|
|
||||||
public function __construct(string $url, int $method = self::HTTP_GET)
|
public function __construct(string $url = '', int $method = self::HTTP_GET)
|
||||||
{
|
{
|
||||||
$this->url = $url;
|
$this->url = $url;
|
||||||
$this->method = $method;
|
$this->method = $method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUrl(string $url): void
|
||||||
|
{
|
||||||
|
$this->url = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMethod(int $method): void
|
||||||
|
{
|
||||||
|
$this->method = $method;
|
||||||
|
}
|
||||||
|
|
||||||
public function setQuery($query)
|
public function setQuery($query)
|
||||||
{
|
{
|
||||||
if (is_string($query)) {
|
if (is_string($query)) {
|
||||||
@@ -34,7 +43,7 @@ class Request
|
|||||||
$this->headers = array_merge($this->headers, $headers);
|
$this->headers = array_merge($this->headers, $headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function send(): Response
|
public function send(): IResponse
|
||||||
{
|
{
|
||||||
$ch = curl_init();
|
$ch = curl_init();
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<?php namespace MapGuesser\Http;
|
<?php namespace MapGuesser\Http;
|
||||||
|
|
||||||
class Response
|
use MapGuesser\Interfaces\Http\IResponse;
|
||||||
|
|
||||||
|
class Response implements IResponse
|
||||||
{
|
{
|
||||||
private string $body;
|
private string $body;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php namespace MapGuesser\Interfaces\Http;
|
||||||
|
|
||||||
|
interface IRequest
|
||||||
|
{
|
||||||
|
const HTTP_GET = 0;
|
||||||
|
|
||||||
|
const HTTP_POST = 1;
|
||||||
|
|
||||||
|
public function setUrl(string $url): void;
|
||||||
|
|
||||||
|
public function setMethod(int $method): void;
|
||||||
|
|
||||||
|
public function setQuery($query);
|
||||||
|
|
||||||
|
public function setHeaders(array $headers);
|
||||||
|
|
||||||
|
public function send(): IResponse;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php namespace MapGuesser\Interfaces\Http;
|
||||||
|
|
||||||
|
interface IResponse
|
||||||
|
{
|
||||||
|
public function getBody();
|
||||||
|
|
||||||
|
public function getHeaders();
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?php namespace MapGuesser\OAuth;
|
<?php namespace MapGuesser\OAuth;
|
||||||
|
|
||||||
use MapGuesser\Http\Request;
|
use MapGuesser\Interfaces\Http\IRequest;
|
||||||
|
|
||||||
class GoogleOAuth
|
class GoogleOAuth
|
||||||
{
|
{
|
||||||
@@ -8,6 +8,13 @@ class GoogleOAuth
|
|||||||
|
|
||||||
private static $tokenUrlBase = 'https://oauth2.googleapis.com/token';
|
private static $tokenUrlBase = 'https://oauth2.googleapis.com/token';
|
||||||
|
|
||||||
|
private IRequest $request;
|
||||||
|
|
||||||
|
public function __construct(IRequest $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
public function getDialogUrl(string $state, string $redirectUrl): string
|
public function getDialogUrl(string $state, string $redirectUrl): string
|
||||||
{
|
{
|
||||||
$oauthParams = [
|
$oauthParams = [
|
||||||
@@ -32,9 +39,10 @@ class GoogleOAuth
|
|||||||
'grant_type' => 'authorization_code',
|
'grant_type' => 'authorization_code',
|
||||||
];
|
];
|
||||||
|
|
||||||
$request = new Request(self::$tokenUrlBase, Request::HTTP_POST);
|
$this->request->setUrl(self::$tokenUrlBase);
|
||||||
$request->setQuery($tokenParams);
|
$this->request->setMethod(IRequest::HTTP_POST);
|
||||||
$response = $request->send();
|
$this->request->setQuery($tokenParams);
|
||||||
|
$response = $this->request->send();
|
||||||
|
|
||||||
return json_decode($response->getBody(), true);
|
return json_decode($response->getBody(), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php namespace MapGuesser\PersistentData;
|
<?php namespace MapGuesser\PersistentData;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
use MapGuesser\Database\Query\Modify;
|
use MapGuesser\Database\Query\Modify;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
use MapGuesser\Interfaces\Database\IResultSet;
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
@@ -9,30 +10,8 @@ class PersistentDataManager
|
|||||||
{
|
{
|
||||||
public function selectFromDb(Select $select, string $type, bool $withRelations = false): ?Model
|
public function selectFromDb(Select $select, string $type, bool $withRelations = false): ?Model
|
||||||
{
|
{
|
||||||
$table = call_user_func([$type, 'getTable']);
|
$select = $this->createSelect($select, $type, $withRelations);
|
||||||
$fields = call_user_func([$type, 'getFields']);
|
|
||||||
|
|
||||||
$select->from($table);
|
|
||||||
|
|
||||||
//TODO: only with some relations?
|
|
||||||
if ($withRelations) {
|
|
||||||
$relations = call_user_func([$type, 'getRelations']);
|
|
||||||
|
|
||||||
$columns = [];
|
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
$columns[] = [$table, $field];
|
|
||||||
}
|
|
||||||
|
|
||||||
$columns = array_merge($columns, $this->getRelationColumns($relations));
|
|
||||||
|
|
||||||
$this->leftJoinRelations($select, $table, $relations);
|
|
||||||
$select->columns($columns);
|
|
||||||
} else {
|
|
||||||
$select->columns($fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: return with array?
|
|
||||||
$data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
$data = $select->execute()->fetch(IResultSet::FETCH_ASSOC);
|
||||||
|
|
||||||
if ($data === null) {
|
if ($data === null) {
|
||||||
@@ -45,6 +24,18 @@ class PersistentDataManager
|
|||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function selectMultipleFromDb(Select $select, string $type, bool $withRelations = false): Generator
|
||||||
|
{
|
||||||
|
$select = $this->createSelect($select, $type, $withRelations);
|
||||||
|
|
||||||
|
while ($data = $select->execute()->fetch(IResultSet::FETCH_ASSOC)) {
|
||||||
|
$model = new $type();
|
||||||
|
$this->fillWithData($data, $model);
|
||||||
|
|
||||||
|
yield $model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function selectFromDbById($id, string $type, bool $withRelations = false): ?Model
|
public function selectFromDbById($id, string $type, bool $withRelations = false): ?Model
|
||||||
{
|
{
|
||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
@@ -136,6 +127,34 @@ class PersistentDataManager
|
|||||||
$model->resetSnapshot();
|
$model->resetSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function createSelect(Select $select, string $type, bool $withRelations = false): Select
|
||||||
|
{
|
||||||
|
$table = call_user_func([$type, 'getTable']);
|
||||||
|
$fields = call_user_func([$type, 'getFields']);
|
||||||
|
|
||||||
|
$select->from($table);
|
||||||
|
|
||||||
|
//TODO: only with some relations?
|
||||||
|
if ($withRelations) {
|
||||||
|
$relations = call_user_func([$type, 'getRelations']);
|
||||||
|
|
||||||
|
$columns = [];
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$columns[] = [$table, $field];
|
||||||
|
}
|
||||||
|
|
||||||
|
$columns = array_merge($columns, $this->getRelationColumns($relations));
|
||||||
|
|
||||||
|
$this->leftJoinRelations($select, $table, $relations);
|
||||||
|
$select->columns($columns);
|
||||||
|
} else {
|
||||||
|
$select->columns($fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $select;
|
||||||
|
}
|
||||||
|
|
||||||
private function getRelationColumns(array $relations): array
|
private function getRelationColumns(array $relations): array
|
||||||
{
|
{
|
||||||
$columns = [];
|
$columns = [];
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<?php namespace MapGuesser\Repository;
|
<?php namespace MapGuesser\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
use MapGuesser\Database\Query\Select;
|
use MapGuesser\Database\Query\Select;
|
||||||
|
use MapGuesser\Interfaces\Database\IResultSet;
|
||||||
|
use MapGuesser\PersistentData\Model\User;
|
||||||
use MapGuesser\PersistentData\Model\UserConfirmation;
|
use MapGuesser\PersistentData\Model\UserConfirmation;
|
||||||
use MapGuesser\PersistentData\PersistentDataManager;
|
use MapGuesser\PersistentData\PersistentDataManager;
|
||||||
|
|
||||||
@@ -25,4 +28,12 @@ class UserConfirmationRepository
|
|||||||
|
|
||||||
return $this->pdm->selectFromDb($select, UserConfirmation::class);
|
return $this->pdm->selectFromDb($select, UserConfirmation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByUser(User $user): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
yield from $this->pdm->selectMultipleFromDb($select, UserConfirmation::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MapGuesser\Tests\OAuth;
|
||||||
|
|
||||||
|
use MapGuesser\Interfaces\Http\IRequest;
|
||||||
|
use MapGuesser\Interfaces\Http\IResponse;
|
||||||
|
use MapGuesser\OAuth\GoogleOAuth;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class GoogleOAuthTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testCanCreateDialogUrl(): void
|
||||||
|
{
|
||||||
|
$_ENV['GOOGLE_OAUTH_CLIENT_ID'] = 'xyz';
|
||||||
|
$state = 'random_state_string';
|
||||||
|
$redirectUrl = 'http://example.com/oauth';
|
||||||
|
|
||||||
|
$requestMock = $this->getMockBuilder(IRequest::class)
|
||||||
|
->setMethods(['setUrl', 'setMethod', 'setQuery', 'setHeaders', 'send'])
|
||||||
|
->getMock();
|
||||||
|
$googleOAuth = new GoogleOAuth($requestMock);
|
||||||
|
|
||||||
|
$dialogUrl = $googleOAuth->getDialogUrl($state, $redirectUrl);
|
||||||
|
$dialogUrlParsed = explode('?', $dialogUrl);
|
||||||
|
|
||||||
|
$this->assertEquals('https://accounts.google.com/o/oauth2/v2/auth', $dialogUrlParsed[0]);
|
||||||
|
|
||||||
|
parse_str($dialogUrlParsed[1], $dialogUrlQueryParams);
|
||||||
|
|
||||||
|
$expectedQueryParams = [
|
||||||
|
'response_type' => 'code',
|
||||||
|
'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
|
||||||
|
'scope' => 'openid email',
|
||||||
|
'redirect_uri' => $redirectUrl,
|
||||||
|
'state' => $state,
|
||||||
|
'nonce' => hash('sha256', random_bytes(10) . microtime()),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertEquals($expectedQueryParams['response_type'], $dialogUrlQueryParams['response_type']);
|
||||||
|
$this->assertEquals($expectedQueryParams['client_id'], $dialogUrlQueryParams['client_id']);
|
||||||
|
$this->assertEquals($expectedQueryParams['scope'], $dialogUrlQueryParams['scope']);
|
||||||
|
$this->assertEquals($expectedQueryParams['redirect_uri'], $dialogUrlQueryParams['redirect_uri']);
|
||||||
|
$this->assertEquals($expectedQueryParams['state'], $dialogUrlQueryParams['state']);
|
||||||
|
$this->assertMatchesRegularExpression('/^[a-f0-9]{64}$/', $dialogUrlQueryParams['nonce']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanRequestToken(): void
|
||||||
|
{
|
||||||
|
$_ENV['GOOGLE_OAUTH_CLIENT_ID'] = 'abc';
|
||||||
|
$_ENV['GOOGLE_OAUTH_CLIENT_SECRET'] = 'xxx';
|
||||||
|
$code = 'code_from_google';
|
||||||
|
$redirectUrl = 'http://example.com/oauth';
|
||||||
|
|
||||||
|
$requestMock = $this->getMockBuilder(IRequest::class)
|
||||||
|
->setMethods(['setUrl', 'setMethod', 'setQuery', 'setHeaders', 'send'])
|
||||||
|
->getMock();
|
||||||
|
$responseMock = $this->getMockBuilder(IResponse::class)
|
||||||
|
->setMethods(['getBody', 'getHeaders'])
|
||||||
|
->getMock();
|
||||||
|
$googleOAuth = new GoogleOAuth($requestMock);
|
||||||
|
|
||||||
|
$expectedQueryParams = [
|
||||||
|
'code' => $code,
|
||||||
|
'client_id' => $_ENV['GOOGLE_OAUTH_CLIENT_ID'],
|
||||||
|
'client_secret' => $_ENV['GOOGLE_OAUTH_CLIENT_SECRET'],
|
||||||
|
'redirect_uri' => $redirectUrl,
|
||||||
|
'grant_type' => 'authorization_code',
|
||||||
|
];
|
||||||
|
|
||||||
|
$requestMock->expects($this->once())
|
||||||
|
->method('setUrl')
|
||||||
|
->with($this->equalTo('https://oauth2.googleapis.com/token'));
|
||||||
|
$requestMock->expects($this->once())
|
||||||
|
->method('setMethod')
|
||||||
|
->with($this->equalTo(IRequest::HTTP_POST));
|
||||||
|
$requestMock->expects($this->once())
|
||||||
|
->method('setQuery')
|
||||||
|
->with($this->equalTo($expectedQueryParams));
|
||||||
|
$requestMock->expects($this->once())
|
||||||
|
->method('send')
|
||||||
|
->will($this->returnValue($responseMock));
|
||||||
|
$responseMock->expects($this->once())
|
||||||
|
->method('getBody')
|
||||||
|
->will($this->returnValue('{"test":"json"}'));
|
||||||
|
|
||||||
|
$token = $googleOAuth->getToken($code, $redirectUrl);
|
||||||
|
|
||||||
|
$this->assertEquals(['test' => 'json'], $token);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php namespace MapGuesser\Tests\Util;
|
||||||
|
|
||||||
|
use MapGuesser\PersistentData\Model\Model;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class DummyModel extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'test_table';
|
||||||
|
|
||||||
|
protected static array $fields = ['name', 'valid'];
|
||||||
|
|
||||||
|
protected static array $relations = ['other_model' => OtherModel::class];
|
||||||
|
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
private bool $valid;
|
||||||
|
|
||||||
|
public function setName(string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValid(bool $valid): void
|
||||||
|
{
|
||||||
|
$this->valid = $valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValid(): bool
|
||||||
|
{
|
||||||
|
return $this->valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class ModelTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testCanReturnTable(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals('test_table', DummyModel::getTable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanReturnFields(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(['id', 'name', 'valid'], DummyModel::getFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanReturnRelations(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(['other_model' => OtherModel::class], DummyModel::getRelations());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanBeConvertedToArray(): void
|
||||||
|
{
|
||||||
|
$model = new DummyModel();
|
||||||
|
$model->setId(123);
|
||||||
|
$model->setName('John');
|
||||||
|
$model->setValid(true);
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'id' => 123,
|
||||||
|
'name' => 'John',
|
||||||
|
'valid' => true
|
||||||
|
], $model->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanSaveAndResetSnapshot(): void
|
||||||
|
{
|
||||||
|
$model = new DummyModel();
|
||||||
|
$model->setId(123);
|
||||||
|
$model->setName('John');
|
||||||
|
$model->setValid(true);
|
||||||
|
|
||||||
|
$model->saveSnapshot();
|
||||||
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'id' => 123,
|
||||||
|
'name' => 'John',
|
||||||
|
'valid' => true
|
||||||
|
], $model->getSnapshot());
|
||||||
|
|
||||||
|
$model->resetSnapshot();
|
||||||
|
|
||||||
|
$this->assertEquals([], $model->getSnapshot());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php namespace MapGuesser\Tests\Util;
|
||||||
|
|
||||||
|
use MapGuesser\Util\JwtParser;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class JwtParserTest extends TestCase
|
||||||
|
{
|
||||||
|
private JwtParser $jwtParser;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->jwtParser = new JwtParser(
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSettingTokenIsTheSameAsCreatingWithToken(): void
|
||||||
|
{
|
||||||
|
$jwtParser2 = new JwtParser();
|
||||||
|
$jwtParser2->setToken(
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($this->jwtParser, $jwtParser2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanParseTokenHeader(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'alg' => 'HS256',
|
||||||
|
'typ' => 'JWT'
|
||||||
|
], $this->jwtParser->getHeader());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanParseTokenPayload(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals([
|
||||||
|
'sub' => '1234567890',
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'iat' => 1516239022
|
||||||
|
], $this->jwtParser->getPayload());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanParseTokenSignature(): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
'49f94ac7044948c78a285d904f87f0a4c7897f7e8f3a4eb2255fda750b2cc397',
|
||||||
|
bin2hex($this->jwtParser->getSignature())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,21 @@
|
|||||||
<?php
|
|
||||||
$jsFiles = [
|
|
||||||
'js/profile.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'; ?>
|
||||||
<h2>Profile</h2>
|
<h2>Account</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="profileForm" action="/profile" method="post">
|
<form id="accountForm" action="/account" method="post" data-observe-inputs="password_new,password_new_confirm">
|
||||||
<input class="big fullWidth" type="password" name="password" placeholder="Current password" autofocus>
|
<input class="big fullWidth" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
|
||||||
<hr>
|
<hr>
|
||||||
<?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
|
<?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" type="email" name="email" placeholder="Email address" value="<?= $user['email'] ?>" disabled>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_new" placeholder="New password" minlength="6">
|
<input class="big fullWidth marginTop" type="password" name="password_new" placeholder="New password" minlength="6">
|
||||||
<input class="big fullWidth marginTop" type="password" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
|
<input class="big fullWidth marginTop" type="password" name="password_new_confirm" placeholder="New password confirmation" minlength="6">
|
||||||
<p id="profileFormError" class="formError justify marginTop"></p>
|
<p id="accountFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit" name="save" disabled>Save</button>
|
<button type="submit" name="submit" disabled>Save</button>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="center">
|
||||||
|
<a class="button red" href="/account/delete" title="Delete account">Delete account</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php require ROOT . '/views/templates/main_header.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/header.php'; ?>
|
||||||
|
<h2>Delete account</h2>
|
||||||
|
<div class="box">
|
||||||
|
<form id="deleteAccountForm" action="/account/delete" method="post" data-redirect-on-success="/">
|
||||||
|
<p class="justify">Are you sure you want to delete your account? This cannot be undone!</p>
|
||||||
|
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Current password" required minlength="6" autofocus>
|
||||||
|
<p id="deleteAccountFormError" class="formError justify marginTop"></p>
|
||||||
|
<div class="right marginTop">
|
||||||
|
<button class="red" type="submit" name="submit">Delete account</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php require ROOT . '/views/templates/footer.php'; ?>
|
||||||
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -50,7 +50,7 @@ $jsFiles = [
|
|||||||
</header>
|
</header>
|
||||||
<div id="metadata" class="modal">
|
<div id="metadata" class="modal">
|
||||||
<h2>Edit map data</h2>
|
<h2>Edit map data</h2>
|
||||||
<form id="metadataForm" class="marginTop">
|
<form id="metadataForm" class="marginTop" data-no-submit="true">
|
||||||
<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 class="right">
|
<div class="right">
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
$jsFiles = [
|
||||||
|
'js/login/google_signup.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'; ?>
|
||||||
<h2>Sign up</h2>
|
<h2>Sign up</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="googleSignupForm" action="/signup/google" method="post">
|
<form id="googleSignupForm" action="/signup/google" method="post" data-redirect-on-success="/">
|
||||||
<?php if ($found): ?>
|
<?php if ($found): ?>
|
||||||
<p class="justify">Please confirm that you link your account to your Google account.</p>
|
<p class="justify">Please confirm that you link your account to your Google account.</p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@@ -22,27 +27,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<?php require ROOT . '/views/templates/footer.php'; ?>
|
<?php require ROOT . '/views/templates/footer.php'; ?>
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
var form = document.getElementById('googleSignupForm');
|
|
||||||
|
|
||||||
form.onsubmit = function (e) {
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
MapGuesser.httpRequest('POST', form.action, function () {
|
|
||||||
window.location.replace('/');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById('cancelGoogleSignupButton').onclick = function () {
|
|
||||||
document.getElementById('loading').style.visibility = 'visible';
|
|
||||||
|
|
||||||
MapGuesser.httpRequest('POST', '/signup/google/reset', function () {
|
|
||||||
window.location.replace('/signup');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
<?php require ROOT . '/views/templates/main_footer.php'; ?>
|
||||||
@@ -1,13 +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'; ?>
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="loginForm" action="/login" method="post">
|
<form id="loginForm" action="/login" method="post" data-redirect-on-success="/">
|
||||||
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
|
<input class="big fullWidth" type="email" name="email" placeholder="Email address" required autofocus>
|
||||||
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
|
<input class="big fullWidth marginTop" type="password" name="password" placeholder="Password" required minlength="6">
|
||||||
<p id="loginFormError" class="formError justify marginTop"></p>
|
<p id="loginFormError" class="formError justify marginTop"></p>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
$jsFiles = [
|
$jsFiles = [
|
||||||
'js/signup.js',
|
'js/login/signup.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'; ?>
|
||||||
<h2>Sign up</h2>
|
<h2>Sign up</h2>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<form id="signupForm" action="/signup" method="post">
|
<form id="signupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
|
||||||
<?php if (isset($email)): ?>
|
<?php if (isset($email)): ?>
|
||||||
<p class="justify">No user found with the given email address. Sign up with one click!</p>
|
<p class="justify">No user found with the given email address. Sign up with one click!</p>
|
||||||
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" required>
|
<input class="big fullWidth marginTop" type="email" name="email" placeholder="Email address" value="<?= $email ?>" required>
|
||||||
|
|||||||
+6
-118
@@ -2,6 +2,12 @@
|
|||||||
$cssFiles = [
|
$cssFiles = [
|
||||||
'css/maps.css'
|
'css/maps.css'
|
||||||
];
|
];
|
||||||
|
$jsFiles = [
|
||||||
|
'js/maps.js',
|
||||||
|
];
|
||||||
|
if ($isAdmin) {
|
||||||
|
$jsFiles[] = 'js/maps_admin.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'; ?>
|
||||||
@@ -61,122 +67,4 @@ $cssFiles = [
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php require ROOT . '/views/templates/footer.php'; ?>
|
<?php require ROOT . '/views/templates/footer.php'; ?>
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
const GOOGLE_MAPS_JS_API_KEY = '<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>';
|
|
||||||
|
|
||||||
var Maps = {
|
|
||||||
descriptionDivs: null,
|
|
||||||
|
|
||||||
addStaticMaps: function () {
|
|
||||||
var imgContainers = document.getElementById('mapContainer').getElementsByClassName('imgContainer');
|
|
||||||
for (var i = 0; i < imgContainers.length; i++) {
|
|
||||||
var imgContainer = imgContainers[i];
|
|
||||||
|
|
||||||
var imgSrc = 'https://maps.googleapis.com/maps/api/staticmap?size=350x175&' +
|
|
||||||
'scale=' + (window.devicePixelRatio >= 2 ? 2 : 1) + '&' +
|
|
||||||
'visible=' + imgContainer.dataset.boundSouthLat + ',' + imgContainer.dataset.boundWestLng + '|' +
|
|
||||||
imgContainer.dataset.boundNorthLat + ',' + imgContainer.dataset.boundEastLng +
|
|
||||||
'&key=' + GOOGLE_MAPS_JS_API_KEY;
|
|
||||||
|
|
||||||
imgContainer.style.backgroundImage = 'url("' + imgSrc + '")';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initializeDescriptionDivs: function () {
|
|
||||||
Maps.descriptionDivs = document.getElementById('mapContainer').getElementsByClassName('description');
|
|
||||||
|
|
||||||
for (var i = 0; i < Maps.descriptionDivs.length; i++) {
|
|
||||||
var description = Maps.descriptionDivs[i];
|
|
||||||
var boundingClientRect = description.getBoundingClientRect();
|
|
||||||
|
|
||||||
description.defaultHeight = boundingClientRect.height;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
calculateDescriptionDivHeights: function () {
|
|
||||||
var currentY;
|
|
||||||
var rows = [];
|
|
||||||
for (var i = 0; i < Maps.descriptionDivs.length; i++) {
|
|
||||||
var description = Maps.descriptionDivs[i];
|
|
||||||
var boundingClientRect = description.getBoundingClientRect();
|
|
||||||
|
|
||||||
if (currentY !== boundingClientRect.y) {
|
|
||||||
rows.push([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
rows[rows.length - 1].push(description);
|
|
||||||
|
|
||||||
currentY = boundingClientRect.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < rows.length; i++) {
|
|
||||||
var row = rows[i];
|
|
||||||
|
|
||||||
var maxHeight = 0;
|
|
||||||
for (var j = 0; j < row.length; j++) {
|
|
||||||
var description = row[j];
|
|
||||||
|
|
||||||
if (description.defaultHeight > maxHeight) {
|
|
||||||
maxHeight = description.defaultHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < row.length; j++) {
|
|
||||||
var description = row[j];
|
|
||||||
|
|
||||||
description.style.height = maxHeight + 'px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Maps.addStaticMaps();
|
|
||||||
|
|
||||||
Maps.initializeDescriptionDivs();
|
|
||||||
Maps.calculateDescriptionDivHeights();
|
|
||||||
|
|
||||||
window.onresize = function () {
|
|
||||||
Maps.calculateDescriptionDivHeights();
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<?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 i = 0; i < buttons.length; i++) {
|
|
||||||
var button = buttons[i];
|
|
||||||
|
|
||||||
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'; ?>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
<?php if (Container::$request->user()) : ?>
|
<?php if (Container::$request->user()) : ?>
|
||||||
<span><a href="/profile" title="Profile">
|
<span><a href="/account" title="Account">
|
||||||
<?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" 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"/>
|
<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"/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
const STATIC_ROOT = '<?= $_ENV['STATIC_ROOT'] ?>';
|
const STATIC_ROOT = '<?= $_ENV['STATIC_ROOT'] ?>';
|
||||||
const REVISION = '<?= REVISION ?>';
|
const REVISION = '<?= REVISION ?>';
|
||||||
var ANTI_CSRF_TOKEN = '<?= \Container::$request->session()->get('anti_csrf_token') ?>';
|
var ANTI_CSRF_TOKEN = '<?= \Container::$request->session()->get('anti_csrf_token') ?>';
|
||||||
|
const GOOGLE_MAPS_JS_API_KEY = '<?= $_ENV['GOOGLE_MAPS_JS_API_KEY'] ?>';
|
||||||
<?php if (!empty($_ENV['GOOGLE_ANALITICS_ID'])): ?>
|
<?php if (!empty($_ENV['GOOGLE_ANALITICS_ID'])): ?>
|
||||||
const GOOGLE_ANALITICS_ID = '<?= $_ENV['GOOGLE_ANALITICS_ID'] ?>';
|
const GOOGLE_ANALITICS_ID = '<?= $_ENV['GOOGLE_ANALITICS_ID'] ?>';
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ Container::$routeCollection->group('signup', function (MapGuesser\Routing\RouteC
|
|||||||
$routeCollection->get('signup.cancel', 'cancel/{token}', [MapGuesser\Controller\LoginController::class, 'cancel']);
|
$routeCollection->get('signup.cancel', 'cancel/{token}', [MapGuesser\Controller\LoginController::class, 'cancel']);
|
||||||
});
|
});
|
||||||
Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']);
|
Container::$routeCollection->get('logout', 'logout', [MapGuesser\Controller\LoginController::class, 'logout']);
|
||||||
Container::$routeCollection->group('profile', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('account', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('profile', '', [MapGuesser\Controller\UserController::class, 'getProfile']);
|
$routeCollection->get('account', '', [MapGuesser\Controller\UserController::class, 'getAccount']);
|
||||||
$routeCollection->post('profile-action', '', [MapGuesser\Controller\UserController::class, 'saveProfile']);
|
$routeCollection->post('account-action', '', [MapGuesser\Controller\UserController::class, 'saveAccount']);
|
||||||
|
$routeCollection->get('account.delete', 'delete', [MapGuesser\Controller\UserController::class, 'getDeleteAccount']);
|
||||||
|
$routeCollection->post('account.delete-action', 'delete', [MapGuesser\Controller\UserController::class, 'deleteAccount']);
|
||||||
});
|
});
|
||||||
//Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']);
|
//Container::$routeCollection->get('maps', 'maps', [MapGuesser\Controller\MapsController::class, 'getMaps']);
|
||||||
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('game', function (MapGuesser\Routing\RouteCollection $routeCollection) {
|
||||||
|
|||||||
Reference in new issue
Block a user