Compare commits
	
		
			51 Commits
		
	
	
		
			Release_23
			...
			develop
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9b2ffa8b2c | |||
| 9bafc52626 | |||
| 18ddaa1da4 | |||
| be2105a284 | |||
| 5481cc67a0 | |||
| a547fbb631 | |||
| 0e3f943f1e | |||
| ec42479304 | |||
| dbb7c1c0fc | |||
| 410bba4966 | |||
| 17aee22400 | |||
| aaf220dce2 | |||
| 276a289ca7 | |||
| e684365612 | |||
| 105cc96963 | |||
| bdd62aadf5 | |||
| 390c13608a | |||
| 345cf31bb3 | |||
| c7f5ea0d85 | |||
| fc6141e2b9 | |||
| 5049a01d2a | |||
| 45d0c9fa80 | |||
| 1e4b982430 | |||
| 0a7d248a3e | |||
| 2177dfd893 | |||
| 173b50fa6c | |||
| d7338b84d3 | |||
| 0216861579 | |||
| 7be04f128a | |||
| c4dd60e0de | |||
| 752ea12810 | |||
| 751a86c823 | |||
| 52873fc759 | |||
| 0882a67019 | |||
| 49069f4a52 | |||
| 4bba7599e1 | |||
| 7fb75c9f25 | |||
| 5d367d5b35 | |||
| a2d6376e81 | |||
| f3c3aa69eb | |||
| 467399c81b | |||
| 84e848506f | |||
| e18ed3a034 | |||
| ea8b46ab91 | |||
| a1b0f5e9fb | |||
| b1ed28f4b5 | |||
| 36f4b6b4d0 | |||
| 2c706cc7f3 | |||
| 77c6e6c4e6 | |||
| c25ba2dd28 | |||
| 5b045335a1 | 
							
								
								
									
										49
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							@ -37,7 +37,7 @@ pipeline {
 | 
			
		||||
                sh 'vendor/bin/phpunit --log-junit unit_test_results.xml --testdox tests'
 | 
			
		||||
            }
 | 
			
		||||
            post {
 | 
			
		||||
                success {
 | 
			
		||||
                always {
 | 
			
		||||
                    archiveArtifacts 'unit_test_results.xml'
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -56,29 +56,48 @@ pipeline {
 | 
			
		||||
                sh 'php -d memory_limit=1G vendor/bin/phpstan analyse -c phpstan.neon --error-format=prettyJson > static_code_analysis_results.json'
 | 
			
		||||
            }
 | 
			
		||||
            post {
 | 
			
		||||
                success {
 | 
			
		||||
                always {
 | 
			
		||||
                    archiveArtifacts 'static_code_analysis_results.json'
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stage('Prepare Docker release') {
 | 
			
		||||
            environment {
 | 
			
		||||
                COMPOSER_HOME="${WORKSPACE}/.composer"
 | 
			
		||||
                npm_config_cache="${WORKSPACE}/.npm"
 | 
			
		||||
            }
 | 
			
		||||
            agent {
 | 
			
		||||
                dockerfile {
 | 
			
		||||
                    filename 'docker/Dockerfile'
 | 
			
		||||
                    dir '.'
 | 
			
		||||
                    additionalBuildArgs '--target mapg_base'
 | 
			
		||||
                    reuseNode true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            steps {
 | 
			
		||||
                script {
 | 
			
		||||
                    sh script: 'git clean -ffdx', label: 'Clean repository'
 | 
			
		||||
                    env.VERSION = sh(script: 'git describe --tags --always --match "Release_*" HEAD', returnStdout: true).trim()
 | 
			
		||||
                    sh script: 'docker/scripts/release.sh', label: 'Release script'
 | 
			
		||||
                    sh script: "rm -rf ${env.COMPOSER_HOME} ${env.npm_config_cache}"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        stage('Release Docker image') {
 | 
			
		||||
            steps {
 | 
			
		||||
                script {
 | 
			
		||||
                    sh script: 'git clean -ffdx', label: 'Clean repository'
 | 
			
		||||
 | 
			
		||||
                    def version = sh(script: 'git describe --tags --always --match "Release_*" HEAD', returnStdout: true).trim()
 | 
			
		||||
                    def imageUrl = "git.esoko.eu/esoko/mapguesser:${version}"
 | 
			
		||||
 | 
			
		||||
                    sh script: """docker buildx build \
 | 
			
		||||
                                    -t ${imageUrl} \
 | 
			
		||||
                                    -f docker/Dockerfile \
 | 
			
		||||
                                    --target mapg_release \
 | 
			
		||||
                                    .""",
 | 
			
		||||
                        label: 'Build Docker image'
 | 
			
		||||
 | 
			
		||||
                    withDockerRegistry([credentialsId: 'gitea-system-user', url: 'https://git.esoko.eu/']) {
 | 
			
		||||
                        sh script: "docker push ${imageUrl}", label: 'Push Docker image to registry'
 | 
			
		||||
                        sh script: 'docker buildx create --use --bootstrap --platform=linux/arm64,linux/amd64 --name multi-platform-builder'
 | 
			
		||||
                        sh script: """docker buildx build \
 | 
			
		||||
                                        --platform linux/amd64,linux/arm64 \
 | 
			
		||||
                                        -f docker/Dockerfile \
 | 
			
		||||
                                        --target mapg_release \
 | 
			
		||||
                                        -t git.esoko.eu/esoko/mapguesser:${env.VERSION} \
 | 
			
		||||
                                        --push \
 | 
			
		||||
                                        .""",
 | 
			
		||||
                           label: 'Build Docker image'
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
# MapGuesser
 | 
			
		||||
 | 
			
		||||
[](https://jenkins.e5tv.hu/job/mapguesser/job/develop/)
 | 
			
		||||
[](https://ci.esoko.eu/job/mapguesser/job/develop/)
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
@ -80,7 +80,7 @@ docker compose up -d
 | 
			
		||||
**And you are done!** The application is ready to use. You can create the first administrative user with the following command after attaching to the `app` container:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
./mapg user:add EMAIL PASSWORD admin
 | 
			
		||||
./mapg user:add EMAIL USERNAME PASSWORD admin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Development
 | 
			
		||||
 | 
			
		||||
@ -10,11 +10,10 @@
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "require": {
 | 
			
		||||
    "esoko/soko-web": "0.14",
 | 
			
		||||
    "fzaninotto/faker": "^1.9"
 | 
			
		||||
    "esoko/soko-web": "0.15"
 | 
			
		||||
  },
 | 
			
		||||
  "require-dev": {
 | 
			
		||||
    "phpunit/phpunit": "^9.6",
 | 
			
		||||
    "phpunit/phpunit": "^10.3",
 | 
			
		||||
    "phpstan/phpstan": "^1.10"
 | 
			
		||||
  },
 | 
			
		||||
  "autoload": {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										900
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										900
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1 +0,0 @@
 | 
			
		||||
<?php //empty on purpose
 | 
			
		||||
							
								
								
									
										20
									
								
								database/migrations/data/20230923_1905_username.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								database/migrations/data/20230923_1905_username.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\Repository\UserRepository;
 | 
			
		||||
use MapGuesser\Util\UsernameGenerator;
 | 
			
		||||
use SokoWeb\Database\Query\Select;
 | 
			
		||||
 | 
			
		||||
$select = new Select(Container::$dbConnection);
 | 
			
		||||
$users = Container::$persistentDataManager->selectMultipleFromDb($select, User::class);
 | 
			
		||||
$userRepository = new UserRepository();
 | 
			
		||||
$usernameGenerator = new UsernameGenerator();
 | 
			
		||||
 | 
			
		||||
foreach ($users as $user) {
 | 
			
		||||
    do {
 | 
			
		||||
        $username = $usernameGenerator->generate();
 | 
			
		||||
    } while ($userRepository->getByUsername($username));
 | 
			
		||||
 | 
			
		||||
    $user->setUsername($username);
 | 
			
		||||
    Container::$persistentDataManager->saveToDb($user);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								database/migrations/structure/20230923_1905_username.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								database/migrations/structure/20230923_1905_username.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
ALTER TABLE `users`
 | 
			
		||||
  ADD `username` VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL AFTER `email`,
 | 
			
		||||
  ADD UNIQUE `username` (`username`);
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
FROM ubuntu:focal AS mapg_base
 | 
			
		||||
FROM ubuntu:22.04 AS mapg_base
 | 
			
		||||
 | 
			
		||||
ENV DEBIAN_FRONTEND noninteractive
 | 
			
		||||
 | 
			
		||||
RUN apt update --fix-missing && apt install -y curl git unzip mariadb-client nginx \
 | 
			
		||||
    php-apcu php7.4-cli php7.4-curl php7.4-fpm php7.4-mbstring php7.4-mysql php7.4-zip php7.4-xml
 | 
			
		||||
RUN apt update --fix-missing && apt install -y sudo curl git unzip mariadb-client nginx \
 | 
			
		||||
    php-apcu php8.1-cli php8.1-curl php8.1-fpm php8.1-mbstring php8.1-mysql php8.1-zip php8.1-xml
 | 
			
		||||
 | 
			
		||||
RUN mkdir -p /run/php
 | 
			
		||||
COPY docker/configs/nginx.conf /etc/nginx/sites-available/default
 | 
			
		||||
@ -20,9 +20,9 @@ FROM mapg_base AS mapg_dev
 | 
			
		||||
 | 
			
		||||
RUN apt update --fix-missing && apt install -y php-xdebug
 | 
			
		||||
 | 
			
		||||
RUN echo "xdebug.remote_enable = 1" >> /etc/php/7.4/mods-available/xdebug.ini &&\
 | 
			
		||||
    echo "xdebug.remote_autostart = 1" >> /etc/php/7.4/mods-available/xdebug.ini &&\
 | 
			
		||||
    echo "xdebug.remote_connect_back = 1" >> /etc/php/7.4/mods-available/xdebug.ini
 | 
			
		||||
RUN echo "xdebug.remote_enable = 1" >> /etc/php/8.1/mods-available/xdebug.ini &&\
 | 
			
		||||
    echo "xdebug.remote_autostart = 1" >> /etc/php/8.1/mods-available/xdebug.ini &&\
 | 
			
		||||
    echo "xdebug.remote_connect_back = 1" >> /etc/php/8.1/mods-available/xdebug.ini
 | 
			
		||||
 | 
			
		||||
EXPOSE 80
 | 
			
		||||
EXPOSE 5000
 | 
			
		||||
@ -30,14 +30,14 @@ EXPOSE 8090
 | 
			
		||||
EXPOSE 9229
 | 
			
		||||
ENTRYPOINT docker/scripts/entry-point-dev.sh
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FROM mapg_base AS mapg_release
 | 
			
		||||
 | 
			
		||||
RUN apt update --fix-missing && apt install -y cron
 | 
			
		||||
 | 
			
		||||
WORKDIR /var/www/mapguesser
 | 
			
		||||
COPY ./ /var/www/mapguesser
 | 
			
		||||
RUN docker/scripts/release.sh &&\
 | 
			
		||||
    rm -rf /var/www/mapguesser/.git /var/www/mapguesser/.env
 | 
			
		||||
RUN rm -rf /var/www/mapguesser/.git
 | 
			
		||||
 | 
			
		||||
EXPOSE 80
 | 
			
		||||
EXPOSE 8090
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ server {
 | 
			
		||||
 | 
			
		||||
        location ~ \.php$ {
 | 
			
		||||
                include snippets/fastcgi-php.conf;
 | 
			
		||||
                fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
 | 
			
		||||
                fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
 | 
			
		||||
                fastcgi_param REQUEST_SCHEME $forwarded_scheme;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,6 @@
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
chmod 777 cache
 | 
			
		||||
 | 
			
		||||
echo "Installing Composer packages..."
 | 
			
		||||
if [ -f .env ]; then
 | 
			
		||||
    composer install
 | 
			
		||||
@ -20,11 +18,22 @@ echo "Installing Yarn packages..."
 | 
			
		||||
echo "Migrating DB..."
 | 
			
		||||
./mapg db:migrate
 | 
			
		||||
 | 
			
		||||
echo "Set runner user based on owner of .env..."
 | 
			
		||||
if ! getent group mapg; then
 | 
			
		||||
    USER_GID=$(stat -c "%g" .env)
 | 
			
		||||
    groupadd --gid $USER_GID mapg
 | 
			
		||||
fi
 | 
			
		||||
if ! id -u mapg; then
 | 
			
		||||
    USER_UID=$(stat -c "%u" .env)
 | 
			
		||||
    useradd --uid $USER_UID --gid $USER_GID mapg
 | 
			
		||||
fi
 | 
			
		||||
sed -i -e "s/^user = .*$/user = mapg/g" -e "s/^group = .*$/group = mapg/g" /etc/php/8.1/fpm/pool.d/www.conf
 | 
			
		||||
 | 
			
		||||
set +e
 | 
			
		||||
 | 
			
		||||
/usr/sbin/php-fpm7.4 -F &
 | 
			
		||||
/usr/sbin/php-fpm8.1 -F &
 | 
			
		||||
/usr/sbin/nginx -g 'daemon off;' &
 | 
			
		||||
/usr/bin/node --inspect=0.0.0.0:9229 multi &
 | 
			
		||||
sudo -u mapg -g mapg /usr/bin/node --inspect=0.0.0.0:9229 multi &
 | 
			
		||||
 | 
			
		||||
wait -n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,12 +8,24 @@ echo "Migrating DB..."
 | 
			
		||||
echo "Installing crontab..."
 | 
			
		||||
/usr/bin/crontab docker/scripts/cron
 | 
			
		||||
 | 
			
		||||
echo "Set runner user based on owner of .env..."
 | 
			
		||||
if ! getent group mapg; then
 | 
			
		||||
    USER_GID=$(stat -c "%g" .env)
 | 
			
		||||
    groupadd --gid $USER_GID mapg
 | 
			
		||||
fi
 | 
			
		||||
if ! id -u mapg; then
 | 
			
		||||
    USER_UID=$(stat -c "%u" .env)
 | 
			
		||||
    useradd --uid $USER_UID --gid $USER_GID mapg
 | 
			
		||||
fi
 | 
			
		||||
chown mapg:mapg cache
 | 
			
		||||
sed -i -e "s/^user = .*$/user = mapg/g" -e "s/^group = .*$/group = mapg/g" /etc/php/8.1/fpm/pool.d/www.conf
 | 
			
		||||
 | 
			
		||||
set +e
 | 
			
		||||
 | 
			
		||||
/usr/sbin/cron -f &
 | 
			
		||||
/usr/sbin/php-fpm7.4 -F &
 | 
			
		||||
/usr/sbin/php-fpm8.1 -F &
 | 
			
		||||
/usr/sbin/nginx -g 'daemon off;' &
 | 
			
		||||
/usr/bin/node multi &
 | 
			
		||||
sudo -u mapg -g mapg /usr/bin/node multi &
 | 
			
		||||
 | 
			
		||||
wait -n
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,6 @@
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
chmod 777 cache
 | 
			
		||||
 | 
			
		||||
echo "Installing Composer packages..."
 | 
			
		||||
composer create-project --no-dev
 | 
			
		||||
 | 
			
		||||
@ -28,3 +26,5 @@ find public/static/img -type f -iname '*.svg' -exec svgo {} -o {} \;
 | 
			
		||||
 | 
			
		||||
echo "Linking view files..."
 | 
			
		||||
./mapg view:link
 | 
			
		||||
 | 
			
		||||
rm .env
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								multi/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										38
									
								
								multi/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1,17 +1,43 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "mapguesser-multi",
 | 
			
		||||
    "lockfileVersion": 3,
 | 
			
		||||
    "requires": true,
 | 
			
		||||
    "lockfileVersion": 1,
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "dotenv": {
 | 
			
		||||
    "packages": {
 | 
			
		||||
        "": {
 | 
			
		||||
            "name": "mapguesser-multi",
 | 
			
		||||
            "license": "GNU AGPL 3.0",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "dotenv": "^8.2.0",
 | 
			
		||||
                "ws": "^7.4.4"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/dotenv": {
 | 
			
		||||
            "version": "8.2.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
 | 
			
		||||
            "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
 | 
			
		||||
            "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==",
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">=8"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ws": {
 | 
			
		||||
        "node_modules/ws": {
 | 
			
		||||
            "version": "7.4.4",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
 | 
			
		||||
            "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw=="
 | 
			
		||||
            "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==",
 | 
			
		||||
            "engines": {
 | 
			
		||||
                "node": ">=8.3.0"
 | 
			
		||||
            },
 | 
			
		||||
            "peerDependencies": {
 | 
			
		||||
                "bufferutil": "^4.0.1",
 | 
			
		||||
                "utf-8-validate": "^5.0.2"
 | 
			
		||||
            },
 | 
			
		||||
            "peerDependenciesMeta": {
 | 
			
		||||
                "bufferutil": {
 | 
			
		||||
                    "optional": true
 | 
			
		||||
                },
 | 
			
		||||
                "utf-8-validate": {
 | 
			
		||||
                    "optional": true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -376,7 +376,7 @@ header>p>span {
 | 
			
		||||
    padding-left: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header>p>span>a:link, header>p>span>a:visited {
 | 
			
		||||
header>p>span>a:link, header>p>span>a:visited, footer>p>a:link, footer>p>a:visited {
 | 
			
		||||
    color: inherit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -231,17 +231,6 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 });
 | 
			
		||||
 | 
			
		||||
        prepare: function () {
 | 
			
		||||
            var data = new FormData();
 | 
			
		||||
            var userNames;
 | 
			
		||||
 | 
			
		||||
            if (roomId) {
 | 
			
		||||
                var userNames = localStorage.userNames ? JSON.parse(localStorage.userNames) : {};
 | 
			
		||||
                if (!userNames.hasOwnProperty(roomId)) {
 | 
			
		||||
                    userNames[roomId] = prompt('Your name: ');
 | 
			
		||||
                    localStorage.userNames = JSON.stringify(userNames);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                data.append('userName', userNames[roomId]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            document.getElementById('loading').style.visibility = 'visible';
 | 
			
		||||
            var url = Game.getGameIdentifier() + '/prepare.json';
 | 
			
		||||
@ -618,7 +607,7 @@ const GameType = Object.freeze({ 'SINGLE': 0, 'MULTI': 1, 'CHALLENGE': 2 });
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case 'anonymous_user':
 | 
			
		||||
                    MapGuesser.showModalWithContent('Error', 'You have to login to join a challenge!');
 | 
			
		||||
                    MapGuesser.showModalWithContent('Error', 'You have to login to join this game!');
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
 | 
			
		||||
@ -89,6 +89,9 @@ var MapGuesser = {
 | 
			
		||||
 | 
			
		||||
                    formError.style.display = 'block';
 | 
			
		||||
                    formError.innerHTML = this.response.error.errorText;
 | 
			
		||||
                    if (typeof grecaptcha !== 'undefined') {
 | 
			
		||||
                        grecaptcha.reset();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
@ -183,12 +186,23 @@ var MapGuesser = {
 | 
			
		||||
        document.getElementById('cover').style.visibility = 'hidden';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observeInput: function (input, buttonToToggle) {
 | 
			
		||||
        if (input.defaultValue !== input.value) {
 | 
			
		||||
            buttonToToggle.disabled = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            buttonToToggle.disabled = true;
 | 
			
		||||
    observeInput: function (form, observedInputs) {
 | 
			
		||||
        var anyChanged = false;
 | 
			
		||||
 | 
			
		||||
        for (var i = 0; i < observedInputs.length; i++) {
 | 
			
		||||
            var input = form.elements[observedInputs[i]];
 | 
			
		||||
            if (input.type === 'checkbox') {
 | 
			
		||||
                if (input.defaultChecked !== input.checked) {
 | 
			
		||||
                    anyChanged = true;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (input.defaultValue !== input.value) {
 | 
			
		||||
                    anyChanged = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        form.elements['submit_button'].disabled = !anyChanged;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    observeInputsInForm: function (form, observedInputs) {
 | 
			
		||||
@ -199,19 +213,19 @@ var MapGuesser = {
 | 
			
		||||
                case 'INPUT':
 | 
			
		||||
                case 'TEXTAREA':
 | 
			
		||||
                    input.oninput = function () {
 | 
			
		||||
                        MapGuesser.observeInput(this, form.elements.submit);
 | 
			
		||||
                        MapGuesser.observeInput(form, observedInputs);
 | 
			
		||||
                    };
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'SELECT':
 | 
			
		||||
                    input.onchange = function () {
 | 
			
		||||
                        MapGuesser.observeInput(this, form.elements.submit);
 | 
			
		||||
                        MapGuesser.observeInput(form, observedInputs);
 | 
			
		||||
                    };
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        form.onreset = function () {
 | 
			
		||||
            form.elements.submit.disabled = true;
 | 
			
		||||
            form.elements['submit_button'].disabled = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -131,11 +131,13 @@
 | 
			
		||||
        }, formData);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    document.getElementById('multiButton').onclick = function () {
 | 
			
		||||
        MapGuesser.showModal('multi');
 | 
			
		||||
        document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
 | 
			
		||||
        document.getElementById('multiForm').elements.roomId.select();
 | 
			
		||||
        document.getElementById('playMode').style.visibility = 'hidden';
 | 
			
		||||
    if (document.getElementById('multiButton')) {
 | 
			
		||||
        document.getElementById('multiButton').onclick = function () {
 | 
			
		||||
            MapGuesser.showModal('multi');
 | 
			
		||||
            document.getElementById('createNewRoomButton').href = '/multiGame/new/' + this.dataset.mapId;
 | 
			
		||||
            document.getElementById('multiForm').elements.roomId.select();
 | 
			
		||||
            document.getElementById('playMode').style.visibility = 'hidden';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (document.getElementById('challengeButton')) {
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ class AddUserCommand extends Command
 | 
			
		||||
        $this->setName('user:add')
 | 
			
		||||
            ->setDescription('Adding of user.')
 | 
			
		||||
            ->addArgument('email', InputArgument::REQUIRED, 'Email of user')
 | 
			
		||||
            ->addArgument('username', InputArgument::REQUIRED, 'Username of user')
 | 
			
		||||
            ->addArgument('password', InputArgument::REQUIRED, 'Password of user')
 | 
			
		||||
            ->addArgument('type', InputArgument::OPTIONAL, 'Type of user');;
 | 
			
		||||
    }
 | 
			
		||||
@ -22,6 +23,7 @@ class AddUserCommand extends Command
 | 
			
		||||
    {
 | 
			
		||||
        $user = new User();
 | 
			
		||||
        $user->setEmail($input->getArgument('email'));
 | 
			
		||||
        $user->setUsername($input->getArgument('username'));
 | 
			
		||||
        $user->setPlainPassword($input->getArgument('password'));
 | 
			
		||||
        $user->setActive(true);
 | 
			
		||||
        $user->setCreatedDate(new DateTime());
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
<?php namespace MapGuesser\Controller;
 | 
			
		||||
 | 
			
		||||
use DateTime;
 | 
			
		||||
use Faker\Factory;
 | 
			
		||||
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
@ -12,6 +11,7 @@ use MapGuesser\PersistentData\Model\Challenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\MultiRoom;
 | 
			
		||||
use MapGuesser\PersistentData\Model\PlaceInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\UserInChallenge;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\Repository\ChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\MapRepository;
 | 
			
		||||
use MapGuesser\Repository\MultiRoomRepository;
 | 
			
		||||
@ -190,13 +190,17 @@ class GameController implements IAuthenticationRequired
 | 
			
		||||
 | 
			
		||||
    public function prepareMultiGame(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        $roomId = \Container::$request->query('roomId');
 | 
			
		||||
        $userName = \Container::$request->post('userName');
 | 
			
		||||
        if (empty($userName)) {
 | 
			
		||||
            $faker = Factory::create();
 | 
			
		||||
            $userName = $faker->userName;
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User|null $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
        if ($user === null)
 | 
			
		||||
        {
 | 
			
		||||
            return new JsonContent(['error' => 'anonymous_user']);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $roomId = \Container::$request->query('roomId');
 | 
			
		||||
 | 
			
		||||
        $room = $this->multiRoomRepository->getByRoomId($roomId);
 | 
			
		||||
 | 
			
		||||
        if (!isset($room)) {
 | 
			
		||||
@ -225,7 +229,7 @@ class GameController implements IAuthenticationRequired
 | 
			
		||||
        $this->multiConnector->sendMessage('join_room', [
 | 
			
		||||
            'roomId' => $roomId,
 | 
			
		||||
            'token' => $token,
 | 
			
		||||
            'userName' => $userName
 | 
			
		||||
            'userName' => $user->getDisplayName()
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent([
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ use MapGuesser\Repository\UserConfirmationRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPasswordResetterRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPlayedPlaceRepository;
 | 
			
		||||
use MapGuesser\Repository\UserRepository;
 | 
			
		||||
use MapGuesser\Util\UsernameGenerator;
 | 
			
		||||
use SokoWeb\Response\HtmlContent;
 | 
			
		||||
use SokoWeb\Response\JsonContent;
 | 
			
		||||
use SokoWeb\Response\Redirect;
 | 
			
		||||
@ -80,17 +81,20 @@ class LoginController
 | 
			
		||||
 | 
			
		||||
        if (\Container::$request->session()->has('tmp_user_data')) {
 | 
			
		||||
            $tmpUserData = \Container::$request->session()->get('tmp_user_data');
 | 
			
		||||
 | 
			
		||||
            $data = ['email' => $tmpUserData['email']];
 | 
			
		||||
        } else {
 | 
			
		||||
            $data = [];
 | 
			
		||||
            $tmpUserData = [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('login/signup', $data);
 | 
			
		||||
        return new HtmlContent('login/signup', $tmpUserData);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSignupSuccess(): IContent
 | 
			
		||||
    public function getSignupSuccess()
 | 
			
		||||
    {
 | 
			
		||||
        if (\Container::$request->user() !== null) {
 | 
			
		||||
            $this->deleteRedirectUrl();
 | 
			
		||||
            return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('login/signup_success');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -153,7 +157,14 @@ class LoginController
 | 
			
		||||
            return new JsonContent(['success' => true]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmail(\Container::$request->post('email'));
 | 
			
		||||
        if (
 | 
			
		||||
            filter_var(\Container::$request->post('email'), FILTER_VALIDATE_EMAIL) === false &&
 | 
			
		||||
            preg_match('/^[a-zA-Z0-9_\-\.]+$/', \Container::$request->post('email')) !== 1
 | 
			
		||||
        ) {
 | 
			
		||||
            return new JsonContent(['error' => ['errorText' => 'This is not a valid email address or username.']]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmailOrUsername(\Container::$request->post('email'));
 | 
			
		||||
 | 
			
		||||
        if ($user === null) {
 | 
			
		||||
            if (strlen(\Container::$request->post('password')) < 6) {
 | 
			
		||||
@ -167,10 +178,14 @@ class LoginController
 | 
			
		||||
            $tmpUser = new User();
 | 
			
		||||
            $tmpUser->setPlainPassword(\Container::$request->post('password'));
 | 
			
		||||
 | 
			
		||||
            \Container::$request->session()->set('tmp_user_data', [
 | 
			
		||||
                'email' => \Container::$request->post('email'),
 | 
			
		||||
                'password_hashed' => $tmpUser->getPassword()
 | 
			
		||||
            ]);
 | 
			
		||||
            $tmpUserData = ['password_hashed' => $tmpUser->getPassword()];
 | 
			
		||||
            if (filter_var(\Container::$request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
 | 
			
		||||
                $tmpUserData['username'] = \Container::$request->post('email');
 | 
			
		||||
            } else {
 | 
			
		||||
                $tmpUserData['email'] = \Container::$request->post('email');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            \Container::$request->session()->set('tmp_user_data', $tmpUserData);
 | 
			
		||||
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'redirect' => [
 | 
			
		||||
@ -184,7 +199,7 @@ class LoginController
 | 
			
		||||
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => [
 | 
			
		||||
                    'errorText' => 'User found with the given email address, but the account is not activated. ' .
 | 
			
		||||
                    'errorText' => 'User found with the given email address / username, but the account is not activated. ' .
 | 
			
		||||
                        'Please check your email and click on the activation link!'
 | 
			
		||||
                ]
 | 
			
		||||
            ]);
 | 
			
		||||
@ -265,131 +280,141 @@ class LoginController
 | 
			
		||||
            return new JsonContent(['redirect' => ['target' => $this->redirectUrl]]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmail(\Container::$request->post('email'));
 | 
			
		||||
        $newUser = new User();
 | 
			
		||||
 | 
			
		||||
        if ($user !== null) {
 | 
			
		||||
            if ($user->getActive()) {
 | 
			
		||||
                if (!$user->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
                    return new JsonContent([
 | 
			
		||||
                        'error' => [
 | 
			
		||||
                            'errorText' => 'There is a user already registered with the given email address, ' .
 | 
			
		||||
                                'but the given password is wrong. You can <a href="/password/requestReset?email=' .
 | 
			
		||||
                                urlencode($user->getEmail()) . '" title="Request password reset">request password reset</a>!'
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
        $googleUserData = \Container::$request->session()->get('google_user_data');
 | 
			
		||||
        if ($googleUserData !== null) {
 | 
			
		||||
            $user = $this->userRepository->getByEmail($googleUserData['email']);
 | 
			
		||||
 | 
			
		||||
                \Container::$request->setUser($user);
 | 
			
		||||
 | 
			
		||||
                $this->deleteRedirectUrl();
 | 
			
		||||
                $data = ['redirect' => ['target' => $this->redirectUrl]];
 | 
			
		||||
            } else {
 | 
			
		||||
                $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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
 | 
			
		||||
            if (!\Container::$request->post('g-recaptcha-response')) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $captchaValidator = new CaptchaValidator();
 | 
			
		||||
            $captchaResponse = $captchaValidator->validate(\Container::$request->post('g-recaptcha-response'));
 | 
			
		||||
            if (!$captchaResponse['success']) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (filter_var(\Container::$request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
 | 
			
		||||
            return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (\Container::$request->session()->has('tmp_user_data')) {
 | 
			
		||||
            $tmpUserData = \Container::$request->session()->get('tmp_user_data');
 | 
			
		||||
 | 
			
		||||
            $tmpUser = new User();
 | 
			
		||||
            $tmpUser->setPassword($tmpUserData['password_hashed']);
 | 
			
		||||
 | 
			
		||||
            if (!$tmpUser->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (strlen(\Container::$request->post('password')) < 6) {
 | 
			
		||||
            if ($user !== null) {
 | 
			
		||||
                return new JsonContent([
 | 
			
		||||
                    'error' => [
 | 
			
		||||
                        'errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!'
 | 
			
		||||
                        'errorText' => 'There is a user already registered with the email address of this Google account, ' .
 | 
			
		||||
                            'but Google account is not linked to the user. Please <a href="/login?email=' .
 | 
			
		||||
                            urlencode($googleUserData['email']) . '" title="Login">login</a> first to link your Google account!'
 | 
			
		||||
                    ]
 | 
			
		||||
                ]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (\Container::$request->post('password') !== \Container::$request->post('password_confirm')) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
 | 
			
		||||
            $newUser->setActive(true);
 | 
			
		||||
            $newUser->setEmail($googleUserData['email']);
 | 
			
		||||
            $newUser->setGoogleSub($googleUserData['sub']);
 | 
			
		||||
        } else {
 | 
			
		||||
            $user = $this->userRepository->getByEmailOrUsername(\Container::$request->post('email'));
 | 
			
		||||
 | 
			
		||||
            if ($user !== null) {
 | 
			
		||||
                if ($user->getActive()) {
 | 
			
		||||
                    if (!$user->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
                        return new JsonContent([
 | 
			
		||||
                            'error' => [
 | 
			
		||||
                                'errorText' => 'There is a user already registered with the given email address / username, ' .
 | 
			
		||||
                                    'but the given password is wrong. You can <a href="/password/requestReset?email=' .
 | 
			
		||||
                                    urlencode($user->getEmail()) . '" title="Request password reset">request password reset</a>!'
 | 
			
		||||
                            ]
 | 
			
		||||
                        ]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    \Container::$request->setUser($user);
 | 
			
		||||
 | 
			
		||||
                    $this->deleteRedirectUrl();
 | 
			
		||||
                    $data = ['redirect' => ['target' => $this->redirectUrl]];
 | 
			
		||||
                } else {
 | 
			
		||||
                    $data = [
 | 
			
		||||
                        'error' => [
 | 
			
		||||
                            'errorText' => 'There is a user already registered with the given email address / username. ' .
 | 
			
		||||
                                'Please check your email and click on the activation link!'
 | 
			
		||||
                        ]
 | 
			
		||||
                    ];
 | 
			
		||||
                }
 | 
			
		||||
                return new JsonContent($data);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
 | 
			
		||||
                if (!\Container::$request->post('g-recaptcha-response')) {
 | 
			
		||||
                    return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $captchaValidator = new CaptchaValidator();
 | 
			
		||||
                $captchaResponse = $captchaValidator->validate(\Container::$request->post('g-recaptcha-response'));
 | 
			
		||||
                if (!$captchaResponse['success']) {
 | 
			
		||||
                    return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (filter_var(\Container::$request->post('email'), FILTER_VALIDATE_EMAIL) === false) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (\Container::$request->session()->has('tmp_user_data')) {
 | 
			
		||||
                $tmpUserData = \Container::$request->session()->get('tmp_user_data');
 | 
			
		||||
 | 
			
		||||
                $tmpUser = new User();
 | 
			
		||||
                $tmpUser->setPassword($tmpUserData['password_hashed']);
 | 
			
		||||
 | 
			
		||||
                if (!$tmpUser->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
                    return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (strlen(\Container::$request->post('password')) < 6) {
 | 
			
		||||
                    return new JsonContent([
 | 
			
		||||
                        'error' => [
 | 
			
		||||
                            'errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!'
 | 
			
		||||
                        ]
 | 
			
		||||
                    ]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (\Container::$request->post('password') !== \Container::$request->post('password_confirm')) {
 | 
			
		||||
                    return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $newUser->setActive(false);
 | 
			
		||||
            $newUser->setEmail(\Container::$request->post('email'));
 | 
			
		||||
            $newUser->setPlainPassword(\Container::$request->post('password'));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = new User();
 | 
			
		||||
        $user->setEmail(\Container::$request->post('email'));
 | 
			
		||||
        $user->setPlainPassword(\Container::$request->post('password'));
 | 
			
		||||
        $user->setCreatedDate(new DateTime());
 | 
			
		||||
        if (strlen(\Container::$request->post('username')) > 0) {
 | 
			
		||||
            $username = \Container::$request->post('username');
 | 
			
		||||
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($user);
 | 
			
		||||
            if (preg_match('/^[a-zA-Z0-9_\-\.]+$/', $username) !== 1) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'Username can contain only english letters, digits, - (hyphen), . (dot), _ (underscore).']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        $token = bin2hex(random_bytes(16));
 | 
			
		||||
            if ($this->userRepository->getByUsername($username) !== null) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given username is already taken.']]);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            $usernameGenerator = new UsernameGenerator();
 | 
			
		||||
            do {
 | 
			
		||||
                $username = $usernameGenerator->generate();
 | 
			
		||||
            } while ($this->userRepository->getByUsername($username));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $confirmation = new UserConfirmation();
 | 
			
		||||
        $confirmation->setUser($user);
 | 
			
		||||
        $confirmation->setToken($token);
 | 
			
		||||
        $confirmation->setLastSentDate(new DateTime());
 | 
			
		||||
        $newUser->setUsername($username);
 | 
			
		||||
        $newUser->setCreatedDate(new DateTime());
 | 
			
		||||
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($confirmation);
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($newUser);
 | 
			
		||||
 | 
			
		||||
        $this->sendConfirmationEmail($user->getEmail(), $token, $user->getCreatedDate());
 | 
			
		||||
        if ($googleUserData !== null) {
 | 
			
		||||
            $this->sendWelcomeEmail($newUser->getEmail());
 | 
			
		||||
 | 
			
		||||
            \Container::$request->setUser($newUser);
 | 
			
		||||
        } else {
 | 
			
		||||
            $token = bin2hex(random_bytes(16));
 | 
			
		||||
 | 
			
		||||
            $confirmation = new UserConfirmation();
 | 
			
		||||
            $confirmation->setUser($newUser);
 | 
			
		||||
            $confirmation->setToken($token);
 | 
			
		||||
            $confirmation->setLastSentDate(new DateTime());
 | 
			
		||||
 | 
			
		||||
            \Container::$persistentDataManager->saveToDb($confirmation);
 | 
			
		||||
 | 
			
		||||
            $this->sendConfirmationEmail($newUser->getEmail(), $token, $newUser->getCreatedDate());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        \Container::$request->session()->delete('tmp_user_data');
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function signupWithGoogle(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        if (\Container::$request->user() !== null) {
 | 
			
		||||
            $this->deleteRedirectUrl();
 | 
			
		||||
            return new JsonContent(['success' => true]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $userData = \Container::$request->session()->get('google_user_data');
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmail($userData['email']);
 | 
			
		||||
 | 
			
		||||
        if ($user === null) {
 | 
			
		||||
            $sendWelcomeEmail = true;
 | 
			
		||||
 | 
			
		||||
            $user = new User();
 | 
			
		||||
            $user->setEmail($userData['email']);
 | 
			
		||||
            $user->setCreatedDate(new DateTime());
 | 
			
		||||
        } else {
 | 
			
		||||
            $sendWelcomeEmail = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user->setActive(true);
 | 
			
		||||
        $user->setGoogleSub($userData['sub']);
 | 
			
		||||
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($user);
 | 
			
		||||
 | 
			
		||||
        if ($sendWelcomeEmail) {
 | 
			
		||||
            $this->sendWelcomeEmail($user->getEmail());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        \Container::$request->session()->delete('google_user_data');
 | 
			
		||||
        \Container::$request->setUser($user);
 | 
			
		||||
 | 
			
		||||
        $this->deleteRedirectUrl();
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -482,12 +507,19 @@ class LoginController
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmail(\Container::$request->post('email'));
 | 
			
		||||
        if (
 | 
			
		||||
            filter_var(\Container::$request->post('email'), FILTER_VALIDATE_EMAIL) === false &&
 | 
			
		||||
            preg_match('/^[a-zA-Z0-9_\-\.]+$/', \Container::$request->post('email')) !== 1
 | 
			
		||||
        ) {
 | 
			
		||||
            return new JsonContent(['error' => ['errorText' => 'This is not a valid email address or username.']]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user = $this->userRepository->getByEmailOrUsername(\Container::$request->post('email'));
 | 
			
		||||
 | 
			
		||||
        if ($user === null) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => [
 | 
			
		||||
                    'errorText' => 'No user found with the given email address. You can <a href="/signup" title="Sign up">sign up</a>!'
 | 
			
		||||
                    'errorText' => 'No user found with the given email address / username. You can <a href="/signup" title="Sign up">sign up</a>!'
 | 
			
		||||
                ]
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
@ -497,7 +529,7 @@ class LoginController
 | 
			
		||||
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => [
 | 
			
		||||
                    'errorText' => 'User found with the given email address, but the account is not activated. ' .
 | 
			
		||||
                    'errorText' => 'User found with the given email address / username, but the account is not activated. ' .
 | 
			
		||||
                        'Please check your email and click on the activation link!'
 | 
			
		||||
                ]
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ use SokoWeb\Interfaces\Response\IRedirect;
 | 
			
		||||
use SokoWeb\OAuth\GoogleOAuth;
 | 
			
		||||
use MapGuesser\PersistentData\Model\User;
 | 
			
		||||
use MapGuesser\Repository\GuessRepository;
 | 
			
		||||
use MapGuesser\Repository\UserRepository;
 | 
			
		||||
use MapGuesser\Repository\UserConfirmationRepository;
 | 
			
		||||
use MapGuesser\Repository\UserInChallengeRepository;
 | 
			
		||||
use MapGuesser\Repository\UserPasswordResetterRepository;
 | 
			
		||||
@ -19,6 +20,8 @@ use SokoWeb\Util\JwtParser;
 | 
			
		||||
 | 
			
		||||
class UserController implements IAuthenticationRequired
 | 
			
		||||
{
 | 
			
		||||
    private UserRepository $userRepository;
 | 
			
		||||
 | 
			
		||||
    private UserConfirmationRepository $userConfirmationRepository;
 | 
			
		||||
 | 
			
		||||
    private UserPasswordResetterRepository $userPasswordResetterRepository;
 | 
			
		||||
@ -31,6 +34,7 @@ class UserController implements IAuthenticationRequired
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        $this->userRepository = new UserRepository();
 | 
			
		||||
        $this->userConfirmationRepository = new UserConfirmationRepository();
 | 
			
		||||
        $this->userPasswordResetterRepository = new UserPasswordResetterRepository();
 | 
			
		||||
        $this->userPlayedPlaceRepository = new UserPlayedPlaceRepository();
 | 
			
		||||
@ -53,6 +57,130 @@ class UserController implements IAuthenticationRequired
 | 
			
		||||
        return new HtmlContent('account/account', ['user' => $user->toArray()]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGoogleConnectRedirect(): IRedirect
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
 | 
			
		||||
        $state = bin2hex(random_bytes(16));
 | 
			
		||||
        $nonce = bin2hex(random_bytes(16));
 | 
			
		||||
 | 
			
		||||
        \Container::$request->session()->set('oauth_state', $state);
 | 
			
		||||
        \Container::$request->session()->set('oauth_nonce', $nonce);
 | 
			
		||||
 | 
			
		||||
        $oAuth = new GoogleOAuth(new Request());
 | 
			
		||||
 | 
			
		||||
        $url = $oAuth->getDialogUrl(
 | 
			
		||||
            $state,
 | 
			
		||||
            \Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleConnect-confirm')->generateLink(),
 | 
			
		||||
            $nonce,
 | 
			
		||||
            $user->getEmail()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return new Redirect($url, IRedirect::TEMPORARY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGoogleConnectConfirm(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        $defaultError = 'Authentication with Google failed. Please <a href="' . \Container::$routeCollection->getRoute('account.googleConnect')->generateLink() . '" title="Connect with Google">try again</a>!';
 | 
			
		||||
 | 
			
		||||
        if (\Container::$request->query('state') !== \Container::$request->session()->get('oauth_state')) {
 | 
			
		||||
            return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $oAuth = new GoogleOAuth(new Request());
 | 
			
		||||
        $tokenData = $oAuth->getToken(
 | 
			
		||||
            \Container::$request->query('code'),
 | 
			
		||||
            \Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleConnect-confirm')->generateLink()
 | 
			
		||||
        );
 | 
			
		||||
        if (!isset($tokenData['id_token'])) {
 | 
			
		||||
            return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $jwtParser = new JwtParser($tokenData['id_token']);
 | 
			
		||||
        $idToken = $jwtParser->getPayload();
 | 
			
		||||
        if ($idToken['nonce'] !== \Container::$request->session()->get('oauth_nonce')) {
 | 
			
		||||
            return new HtmlContent('account/google_connect', ['success' => false, 'error' => $defaultError]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $anotherUser = $this->userRepository->getByGoogleSub($idToken['sub']);
 | 
			
		||||
        if ($anotherUser !== null) {
 | 
			
		||||
            return new HtmlContent('account/google_connect', [
 | 
			
		||||
                'success' => false,
 | 
			
		||||
                'error' => 'This Google account is linked to another account.'
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        \Container::$request->session()->set('google_user_data', $idToken);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('account/google_connect', [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
            'googleAccount' => $idToken['email'],
 | 
			
		||||
            'userEmail' => $user->getEmail()
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function connectGoogle(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
        if (!$user->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => [
 | 
			
		||||
                    'errorText' => 'The given password is wrong.'
 | 
			
		||||
                ]
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $googleUserData = \Container::$request->session()->get('google_user_data');
 | 
			
		||||
        $user->setGoogleSub($googleUserData['sub']);
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($user);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGoogleDisconnectConfirm(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
 | 
			
		||||
        return new HtmlContent('account/google_disconnect', [
 | 
			
		||||
            'success' => true,
 | 
			
		||||
            'userEmail' => $user->getEmail()
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function disconnectGoogle(): IContent
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
         * @var User $user
 | 
			
		||||
         */
 | 
			
		||||
        $user = \Container::$request->user();
 | 
			
		||||
        if (!$user->checkPassword(\Container::$request->post('password'))) {
 | 
			
		||||
            return new JsonContent([
 | 
			
		||||
                'error' => [
 | 
			
		||||
                    'errorText' => 'The given password is wrong.'
 | 
			
		||||
                ]
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $user->setGoogleSub(null);
 | 
			
		||||
        \Container::$persistentDataManager->saveToDb($user);
 | 
			
		||||
 | 
			
		||||
        return new JsonContent(['success' => true]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGoogleAuthenticateRedirect(): IRedirect
 | 
			
		||||
    {
 | 
			
		||||
        /**
 | 
			
		||||
@ -148,6 +276,36 @@ class UserController implements IAuthenticationRequired
 | 
			
		||||
            return new JsonContent(['error' => ['errorText' => $error]]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $newEmail = \Container::$request->post('email');
 | 
			
		||||
        if ($newEmail !== $user->getEmail()) {
 | 
			
		||||
            if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given email address is not valid.']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->userRepository->getByEmail($newEmail) !== null) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given email address belongs to another account.']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $user->setEmail($newEmail);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $newUsername = \Container::$request->post('username');
 | 
			
		||||
        if ($newUsername !== $user->getUsername()) {
 | 
			
		||||
            if (strlen($newUsername) == 0) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'Username cannot be empty.']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (preg_match('/^[a-zA-Z0-9_\-\.]+$/', $newUsername) !== 1) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'Username can contain only english letters, digits, - (hyphen), . (dot), _ (underscore).']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($this->userRepository->getByUsername($newUsername) !== null) {
 | 
			
		||||
                return new JsonContent(['error' => ['errorText' => 'The given username is already taken.']]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $user->setUsername($newUsername);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (strlen(\Container::$request->post('password_new')) > 0) {
 | 
			
		||||
            if (strlen(\Container::$request->post('password_new')) < 6) {
 | 
			
		||||
                return new JsonContent([
 | 
			
		||||
 | 
			
		||||
@ -8,12 +8,14 @@ class User extends Model implements IUser
 | 
			
		||||
{
 | 
			
		||||
    protected static string $table = 'users';
 | 
			
		||||
 | 
			
		||||
    protected static array $fields = ['email', 'password', 'type', 'active', 'google_sub', 'created'];
 | 
			
		||||
    protected static array $fields = ['email', 'username', 'password', 'type', 'active', 'google_sub', 'created'];
 | 
			
		||||
 | 
			
		||||
    private static array $types = ['user', 'admin'];
 | 
			
		||||
 | 
			
		||||
    private string $email = '';
 | 
			
		||||
 | 
			
		||||
    private string $username = '';
 | 
			
		||||
 | 
			
		||||
    private ?string $password = null;
 | 
			
		||||
 | 
			
		||||
    private string $type = 'user';
 | 
			
		||||
@ -29,6 +31,11 @@ class User extends Model implements IUser
 | 
			
		||||
        $this->email = $email;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setUsername(string $username): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->username = $username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setPassword(?string $hashedPassword): void
 | 
			
		||||
    {
 | 
			
		||||
        $this->password = $hashedPassword;
 | 
			
		||||
@ -71,6 +78,11 @@ class User extends Model implements IUser
 | 
			
		||||
        return $this->email;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getUsername(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getPassword(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->password;
 | 
			
		||||
@ -120,7 +132,7 @@ class User extends Model implements IUser
 | 
			
		||||
 | 
			
		||||
    public function getDisplayName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->email;
 | 
			
		||||
        return $this->username;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function checkPassword(string $password): bool
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,23 @@ class UserRepository implements IUserRepository
 | 
			
		||||
        return \Container::$persistentDataManager->selectFromDb($select, User::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getByUsername(string $username): ?User
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
        $select->where('username', '=', $username);
 | 
			
		||||
 | 
			
		||||
        return \Container::$persistentDataManager->selectFromDb($select, User::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getByEmailOrUsername(string $emailOrUsername): ?User
 | 
			
		||||
    {
 | 
			
		||||
        if (filter_var($emailOrUsername, FILTER_VALIDATE_EMAIL)) {
 | 
			
		||||
            return $this->getByEmail($emailOrUsername);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $this->getByUsername($emailOrUsername);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getByGoogleSub(string $sub): ?User
 | 
			
		||||
    {
 | 
			
		||||
        $select = new Select(\Container::$dbConnection);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										247
									
								
								src/Util/UsernameGenerator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/Util/UsernameGenerator.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,247 @@
 | 
			
		||||
<?php namespace MapGuesser\Util;
 | 
			
		||||
 | 
			
		||||
class UsernameGenerator
 | 
			
		||||
{
 | 
			
		||||
    const ADJECTIVES = [
 | 
			
		||||
        'abundant',
 | 
			
		||||
        'agile',
 | 
			
		||||
        'alluring',
 | 
			
		||||
        'ample',
 | 
			
		||||
        'adorable',
 | 
			
		||||
        'angry',
 | 
			
		||||
        'anxious',
 | 
			
		||||
        'astonishing',
 | 
			
		||||
        'beautiful',
 | 
			
		||||
        'big',
 | 
			
		||||
        'bitter',
 | 
			
		||||
        'blissful',
 | 
			
		||||
        'blue',
 | 
			
		||||
        'brave',
 | 
			
		||||
        'bright',
 | 
			
		||||
        'brilliant',
 | 
			
		||||
        'busy',
 | 
			
		||||
        'calm',
 | 
			
		||||
        'captivating',
 | 
			
		||||
        'careful',
 | 
			
		||||
        'charming',
 | 
			
		||||
        'cheerful',
 | 
			
		||||
        'clumsy',
 | 
			
		||||
        'colorful',
 | 
			
		||||
        'confused',
 | 
			
		||||
        'cooperative',
 | 
			
		||||
        'courageous',
 | 
			
		||||
        'cozy',
 | 
			
		||||
        'crispy',
 | 
			
		||||
        'curious',
 | 
			
		||||
        'dazzling',
 | 
			
		||||
        'delightful',
 | 
			
		||||
        'determined',
 | 
			
		||||
        'eager',
 | 
			
		||||
        'elegant',
 | 
			
		||||
        'enchanting',
 | 
			
		||||
        'enthusiastic',
 | 
			
		||||
        'exciting',
 | 
			
		||||
        'exquisite',
 | 
			
		||||
        'faithful',
 | 
			
		||||
        'fancy',
 | 
			
		||||
        'fearless',
 | 
			
		||||
        'fierce',
 | 
			
		||||
        'fluffy',
 | 
			
		||||
        'fresh',
 | 
			
		||||
        'friendly',
 | 
			
		||||
        'frigid',
 | 
			
		||||
        'funny',
 | 
			
		||||
        'gentle',
 | 
			
		||||
        'glorious',
 | 
			
		||||
        'graceful',
 | 
			
		||||
        'grateful',
 | 
			
		||||
        'happy',
 | 
			
		||||
        'harmonious',
 | 
			
		||||
        'healthy',
 | 
			
		||||
        'helpful',
 | 
			
		||||
        'honest',
 | 
			
		||||
        'hopeful',
 | 
			
		||||
        'hot',
 | 
			
		||||
        'humble',
 | 
			
		||||
        'hungry',
 | 
			
		||||
        'impressive',
 | 
			
		||||
        'infamous',
 | 
			
		||||
        'innocent',
 | 
			
		||||
        'intense',
 | 
			
		||||
        'jolly',
 | 
			
		||||
        'joyful',
 | 
			
		||||
        'kind',
 | 
			
		||||
        'lively',
 | 
			
		||||
        'lonely',
 | 
			
		||||
        'lovely',
 | 
			
		||||
        'lucky',
 | 
			
		||||
        'mysterious',
 | 
			
		||||
        'naughty',
 | 
			
		||||
        'nervous',
 | 
			
		||||
        'nutritious',
 | 
			
		||||
        'obedient',
 | 
			
		||||
        'peaceful',
 | 
			
		||||
        'playful',
 | 
			
		||||
        'polite',
 | 
			
		||||
        'powerful',
 | 
			
		||||
        'precious',
 | 
			
		||||
        'proud',
 | 
			
		||||
        'radiant',
 | 
			
		||||
        'reckless',
 | 
			
		||||
        'reliable',
 | 
			
		||||
        'rich',
 | 
			
		||||
        'romantic',
 | 
			
		||||
        'rough',
 | 
			
		||||
        'sad',
 | 
			
		||||
        'scary',
 | 
			
		||||
        'sensitive',
 | 
			
		||||
        'shiny',
 | 
			
		||||
        'silky',
 | 
			
		||||
        'sincere',
 | 
			
		||||
        'sleepy',
 | 
			
		||||
        'smart',
 | 
			
		||||
        'sneaky',
 | 
			
		||||
        'soft',
 | 
			
		||||
        'sparkling',
 | 
			
		||||
        'splendid',
 | 
			
		||||
        'strong',
 | 
			
		||||
        'stubborn',
 | 
			
		||||
        'sweet',
 | 
			
		||||
        'tender',
 | 
			
		||||
        'thoughtful',
 | 
			
		||||
        'thrilling',
 | 
			
		||||
        'timid',
 | 
			
		||||
        'tranquil',
 | 
			
		||||
        'trustworthy',
 | 
			
		||||
        'unique',
 | 
			
		||||
        'vibrant',
 | 
			
		||||
        'victorious',
 | 
			
		||||
        'warm',
 | 
			
		||||
        'wise',
 | 
			
		||||
        'witty',
 | 
			
		||||
        'wonderful',
 | 
			
		||||
        'worried',
 | 
			
		||||
        'zealous'
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    const NOUNS = [
 | 
			
		||||
        'airplane',
 | 
			
		||||
        'ant',
 | 
			
		||||
        'apple',
 | 
			
		||||
        'aquarium',
 | 
			
		||||
        'backpack',
 | 
			
		||||
        'banana',
 | 
			
		||||
        'bear',
 | 
			
		||||
        'bee',
 | 
			
		||||
        'camera',
 | 
			
		||||
        'car',
 | 
			
		||||
        'cat',
 | 
			
		||||
        'chocolate',
 | 
			
		||||
        'desk',
 | 
			
		||||
        'diamond',
 | 
			
		||||
        'dog',
 | 
			
		||||
        'dolphin',
 | 
			
		||||
        'duck',
 | 
			
		||||
        'egg',
 | 
			
		||||
        'eiffeltower',
 | 
			
		||||
        'elephant',
 | 
			
		||||
        'fire',
 | 
			
		||||
        'flower',
 | 
			
		||||
        'forest',
 | 
			
		||||
        'fork',
 | 
			
		||||
        'fox',
 | 
			
		||||
        'galaxy',
 | 
			
		||||
        'giraffe',
 | 
			
		||||
        'globe',
 | 
			
		||||
        'guitar',
 | 
			
		||||
        'hammer',
 | 
			
		||||
        'hamster',
 | 
			
		||||
        'hat',
 | 
			
		||||
        'house',
 | 
			
		||||
        'icecream',
 | 
			
		||||
        'iguana',
 | 
			
		||||
        'island',
 | 
			
		||||
        'jacket',
 | 
			
		||||
        'jaguar',
 | 
			
		||||
        'jellyfish',
 | 
			
		||||
        'jigsaw',
 | 
			
		||||
        'kangaroo',
 | 
			
		||||
        'key',
 | 
			
		||||
        'kite',
 | 
			
		||||
        'koala',
 | 
			
		||||
        'lamp',
 | 
			
		||||
        'lighthouse',
 | 
			
		||||
        'lightning',
 | 
			
		||||
        'lion',
 | 
			
		||||
        'llama',
 | 
			
		||||
        'moon',
 | 
			
		||||
        'mountain',
 | 
			
		||||
        'mouse',
 | 
			
		||||
        'necklace',
 | 
			
		||||
        'nest',
 | 
			
		||||
        'newt',
 | 
			
		||||
        'notebook',
 | 
			
		||||
        'ocean',
 | 
			
		||||
        'octopus',
 | 
			
		||||
        'orchid',
 | 
			
		||||
        'owl',
 | 
			
		||||
        'panda',
 | 
			
		||||
        'pencil',
 | 
			
		||||
        'penguin',
 | 
			
		||||
        'piano',
 | 
			
		||||
        'queen',
 | 
			
		||||
        'quilt',
 | 
			
		||||
        'quokka',
 | 
			
		||||
        'rabbit',
 | 
			
		||||
        'rainbow',
 | 
			
		||||
        'robot',
 | 
			
		||||
        'ship',
 | 
			
		||||
        'snake',
 | 
			
		||||
        'statue',
 | 
			
		||||
        'sun',
 | 
			
		||||
        'sunflower',
 | 
			
		||||
        'table',
 | 
			
		||||
        'telescope',
 | 
			
		||||
        'tiger',
 | 
			
		||||
        'tree',
 | 
			
		||||
        'turtle',
 | 
			
		||||
        'uakari',
 | 
			
		||||
        'umbrella',
 | 
			
		||||
        'unicorn',
 | 
			
		||||
        'universe',
 | 
			
		||||
        'vase',
 | 
			
		||||
        'violin',
 | 
			
		||||
        'volcano',
 | 
			
		||||
        'vulture',
 | 
			
		||||
        'wallaby',
 | 
			
		||||
        'waterfall',
 | 
			
		||||
        'whale',
 | 
			
		||||
        'xray',
 | 
			
		||||
        'xylophone',
 | 
			
		||||
        'yacht',
 | 
			
		||||
        'yak',
 | 
			
		||||
        'yarn',
 | 
			
		||||
        'yeti',
 | 
			
		||||
        'zebra',
 | 
			
		||||
        'zeppelin',
 | 
			
		||||
        'zucchini',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    function generate(): string
 | 
			
		||||
    {
 | 
			
		||||
        $numberOfAdjectives = count(self::ADJECTIVES);
 | 
			
		||||
        $numberOfNouns = count(self::NOUNS);
 | 
			
		||||
 | 
			
		||||
        $firstAdjective = self::ADJECTIVES[mt_rand(0, $numberOfAdjectives - 1)];
 | 
			
		||||
        do {
 | 
			
		||||
            $secondAdjective = self::ADJECTIVES[mt_rand(0, $numberOfAdjectives - 1)];
 | 
			
		||||
        } while ($firstAdjective === $secondAdjective);
 | 
			
		||||
        $noun = self::NOUNS[mt_rand(0, $numberOfNouns - 1)];
 | 
			
		||||
 | 
			
		||||
        $firstAdjective = ucfirst($firstAdjective);
 | 
			
		||||
        $secondAdjective = ucfirst($secondAdjective);
 | 
			
		||||
        $noun = ucfirst($noun);
 | 
			
		||||
 | 
			
		||||
        return $firstAdjective . $secondAdjective . $noun;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								tests/Util/UsernameGeneratorTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/Util/UsernameGeneratorTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
<?php namespace MapGuesser\Tests\Util;
 | 
			
		||||
 | 
			
		||||
use MapGuesser\Util\UsernameGenerator;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
final class UsernameGeneratorTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testCanGenerateRandomUsernameFromComponents(): void
 | 
			
		||||
    {
 | 
			
		||||
        $generator = new UsernameGenerator();
 | 
			
		||||
        $parts = $this->getUsernameParts($generator->generate());
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(3, count($parts));
 | 
			
		||||
        $this->assertContains($parts[0], UsernameGenerator::ADJECTIVES);
 | 
			
		||||
        $this->assertContains($parts[1], UsernameGenerator::ADJECTIVES);
 | 
			
		||||
        $this->assertContains($parts[2], UsernameGenerator::NOUNS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function getUsernameParts(string $username): array
 | 
			
		||||
    {
 | 
			
		||||
        return explode('-', strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $username)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -5,11 +5,11 @@
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2>Account</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <form id="accountForm" action="/account" method="post" data-observe-inputs="password_new,password_new_confirm">
 | 
			
		||||
        <form id="accountForm" action="/account" method="post" data-reload-on-success="true" data-observe-inputs="email,username,password_new,password_new_confirm">
 | 
			
		||||
            <?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
 | 
			
		||||
                <p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
 | 
			
		||||
                <div class="inputWithButton">
 | 
			
		||||
                    <input type="password" class="text name="password" placeholder="Current password" autocomplete="current-password" required minlength="6" autofocus><!--
 | 
			
		||||
                    <input type="password" class="text" name="password" placeholder="Current password" autocomplete="current-password" required minlength="6" autofocus><!--
 | 
			
		||||
                 --><button id="authenticateWithGoogleButton" class="yellow" type="button">Google</button>
 | 
			
		||||
                </div>
 | 
			
		||||
            <?php elseif ($user['password'] !== null): ?>
 | 
			
		||||
@ -23,16 +23,23 @@
 | 
			
		||||
                </div>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
            <hr>
 | 
			
		||||
            <?php /* TODO: disabled for the time being, email modification should be implemented */ ?>
 | 
			
		||||
            <input type="email" class="text big fullWidth" name="email" placeholder="Email address" autocomplete="username" value="<?= $user['email'] ?>" disabled>
 | 
			
		||||
            <input type="email" class="text big fullWidth" name="email" placeholder="Email address" autocomplete="username" value="<?= $user['email'] ?>">
 | 
			
		||||
            <input type="username" class="text big fullWidth marginTop" name="username" placeholder="Username" value="<?= $user['username'] ?>">
 | 
			
		||||
            <input type="password" class="text big fullWidth marginTop" name="password_new" placeholder="New password" autocomplete="new-password" minlength="6">
 | 
			
		||||
            <input type="password" class="text big fullWidth marginTop" name="password_new_confirm" placeholder="New password confirmation" autocomplete="new-password" minlength="6">
 | 
			
		||||
            <p id="accountFormError" class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right marginTop">
 | 
			
		||||
                <button type="submit" name="submit" disabled>Save</button>
 | 
			
		||||
                <button type="submit" name="submit_button" disabled>Save</button>
 | 
			
		||||
            </div>
 | 
			
		||||
            <hr>
 | 
			
		||||
            <div class="center">
 | 
			
		||||
            <div class="center" style="font-size: 0;">
 | 
			
		||||
                <?php if ($user['google_sub'] === null): ?>
 | 
			
		||||
                    <a class="button yellow marginRight" href="<?= Container::$routeCollection->getRoute('account.googleConnect')->generateLink() ?>" title="Connect with Google">Connect with Google</a>
 | 
			
		||||
                <?php else: ?>
 | 
			
		||||
                    <?php if ($user['password'] !== null): ?>
 | 
			
		||||
                        <a class="button yellow marginRight" href="<?= Container::$routeCollection->getRoute('account.googleDisconnect')->generateLink() ?>" title="Disconnect from Google">Disconnect from Google</a>
 | 
			
		||||
                    <?php endif; ?>
 | 
			
		||||
                <?php endif; ?>
 | 
			
		||||
                <a class="button red" href="/account/delete" title="Delete account">Delete account</a>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
            <p id="deleteAccountFormError" class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right marginTop">
 | 
			
		||||
                <button class="red marginRight" type="submit" name="submit">Delete account</button><!--
 | 
			
		||||
                <button class="red marginRight" type="submit" name="submit_button">Delete account</button><!--
 | 
			
		||||
             --><a class="button gray marginTop" href="/account" title="Back to account">Cancel</a>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								views/account/google_connect.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								views/account/google_connect.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2>Connect with Google</h2>
 | 
			
		||||
    <div class="box compactBox">
 | 
			
		||||
        <?php if (!$success): ?>
 | 
			
		||||
            <p class="error justify"><?= $error ?></p>
 | 
			
		||||
        <?php else: ?>
 | 
			
		||||
            <form id="connectGoogleForm" action="<?= Container::$routeCollection->getRoute('account.googleConnect-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('account')->generateLink() ?>">
 | 
			
		||||
                <p class="justify marginBottom">Your account will be connected with the following Google account: <b><?= $googleAccount ?></b></p>
 | 
			
		||||
                <input type="email" style="display: none;" name="email" autocomplete="username" value="<?= $userEmail ?>">
 | 
			
		||||
                <p class="formLabel marginTop">Password</p>
 | 
			
		||||
                <input type="password" class="text big fullWidth" name="password" autocomplete="current-password" required minlength="6" autofocus>
 | 
			
		||||
                <p class="formError justify marginTop"></p>
 | 
			
		||||
                <div class="right marginTop">
 | 
			
		||||
                    <button class="marginRight" type="submit" name="submit"><i class="fa-solid fa-link"></i> Connect</button><!--
 | 
			
		||||
                 --><a class="button gray" href="<?= Container::$routeCollection->getRoute('account')->generateLink() ?>" title="Back to account">Cancel</a>
 | 
			
		||||
                </div>
 | 
			
		||||
            </form>
 | 
			
		||||
        <?php endif; ?>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
							
								
								
									
										18
									
								
								views/account/google_disconnect.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								views/account/google_disconnect.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
@extends(templates/layout_normal)
 | 
			
		||||
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2>Disconnect from Google</h2>
 | 
			
		||||
    <div class="box compactBox">
 | 
			
		||||
        <form id="connectGoogleForm" action="<?= Container::$routeCollection->getRoute('account.googleDisconnect-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('account')->generateLink() ?>">
 | 
			
		||||
            <p class="justify marginBottom">Your account will be disconnected from the currently set Google account.</p>
 | 
			
		||||
            <input type="email" style="display: none;" name="email" autocomplete="username" value="<?= $userEmail ?>">
 | 
			
		||||
            <p class="formLabel marginTop">Password</p>
 | 
			
		||||
            <input type="password" class="text big fullWidth" name="password" autocomplete="current-password" required minlength="6" autofocus>
 | 
			
		||||
            <p class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right marginTop">
 | 
			
		||||
                <button class="red marginRight" type="submit" name="submit"><i class="fa-solid fa-link-slash"></i> Disconnect</button><!--
 | 
			
		||||
             --><a class="button gray" href="<?= Container::$routeCollection->getRoute('account')->generateLink() ?>" title="Back to account">Cancel</a>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
    </div>
 | 
			
		||||
@endsection
 | 
			
		||||
@ -5,21 +5,13 @@
 | 
			
		||||
@section(main)
 | 
			
		||||
    <h2>Sign up</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <form id="googleSignupForm" action="/signup/google" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
 | 
			
		||||
            <?php if ($found): ?>
 | 
			
		||||
                <p class="justify">Please confirm that you link your account to your Google account.</p>
 | 
			
		||||
            <?php else: ?>
 | 
			
		||||
                <p class="justify">Please confirm your sign up request. Your account will be linked to your Google account.</p>
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
        <form id="googleSignupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
 | 
			
		||||
            <p class="justify">Please confirm your sign up request. Your account will be linked to your Google account.</p>
 | 
			
		||||
            <input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
 | 
			
		||||
            <input type="username" class="text big fullWidth marginTop" name="username" placeholder="Username">
 | 
			
		||||
            <p id="googleSignupFormError" class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right">
 | 
			
		||||
                <button class="marginTop marginRight" type="submit">
 | 
			
		||||
                    <?php if ($found): ?>
 | 
			
		||||
                        Link
 | 
			
		||||
                    <?php else: ?>
 | 
			
		||||
                        Sign up
 | 
			
		||||
                    <?php endif; ?>
 | 
			
		||||
                </button><!--
 | 
			
		||||
                <button class="marginTop marginRight" type="submit">Sign up</button><!--
 | 
			
		||||
             --><button id="cancelGoogleSignupButton" class="gray marginTop" type="button">Cancel</button>
 | 
			
		||||
            </div>
 | 
			
		||||
        </form>
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
    <h2>Login</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <form id="loginForm" action="/login" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
 | 
			
		||||
            <input type="email" class="text big fullWidth" name="email" placeholder="Email address" autocomplete="username" required autofocus>
 | 
			
		||||
            <input type="text" class="text big fullWidth" name="email" placeholder="Email address / username" autocomplete="username" required autofocus>
 | 
			
		||||
            <input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" autocomplete="current-password" required minlength="6">
 | 
			
		||||
            <p id="loginFormError" class="formError justify marginTop"></p>
 | 
			
		||||
            <div class="right marginTop">
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
    <h2>Request password reset</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <form id="passwordResetForm" action="/password/requestReset" method="post" data-redirect-on-success="/password/requestReset/success">
 | 
			
		||||
            <input type="email" class="text big fullWidth" name="email" placeholder="Email address" autocomplete="username" value="<?= isset($email) ? $email : '' ?>" required autofocus>
 | 
			
		||||
            <input type="text" class="text big fullWidth" name="email" placeholder="Email address / username" autocomplete="username" value="<?= isset($email) ? $email : '' ?>" required autofocus>
 | 
			
		||||
            <?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
 | 
			
		||||
                <div class="marginTop">
 | 
			
		||||
                    <div class="g-recaptcha" data-sitekey="<?= $_ENV['RECAPTCHA_SITEKEY'] ?>"></div>
 | 
			
		||||
 | 
			
		||||
@ -7,12 +7,22 @@
 | 
			
		||||
    <h2>Sign up</h2>
 | 
			
		||||
    <div class="box">
 | 
			
		||||
        <form id="signupForm" action="/signup" method="post" data-redirect-on-success="/signup/success">
 | 
			
		||||
            <?php if (isset($email)): ?>
 | 
			
		||||
                <p class="justify">No user found with the given email address. Sign up with one click!</p>
 | 
			
		||||
                <input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" autocomplete="username" value="<?= $email ?>" required>
 | 
			
		||||
                <input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password confirmation" autocomplete="new-password" required minlength="6" autofocus>
 | 
			
		||||
            <?php if (isset($email) || isset($username)): ?>
 | 
			
		||||
                <p class="justify">No user found with the given email address / username. Sign up with one click!</p>
 | 
			
		||||
                <?php if (isset($email)): ?>
 | 
			
		||||
                    <input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" autocomplete="username" value="<?= $email ?>" required>
 | 
			
		||||
                <?php else: ?>
 | 
			
		||||
                    <input type="email" class="text big fullWidth marginTop" name="email" placeholder="Email address" autocomplete="username" required autofocus>
 | 
			
		||||
                <?php endif; ?>
 | 
			
		||||
                <?php if (isset($username)): ?>
 | 
			
		||||
                    <input type="username" class="text big fullWidth marginTop" name="username" placeholder="Username" value="<?= $username ?>">
 | 
			
		||||
                <?php else: ?>
 | 
			
		||||
                    <input type="username" class="text big fullWidth marginTop" name="username" placeholder="Username" autofocus>
 | 
			
		||||
                <?php endif; ?>
 | 
			
		||||
                <input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password confirmation" autocomplete="new-password" required minlength="6">
 | 
			
		||||
            <?php else: ?>
 | 
			
		||||
                <input type="email" class="text big fullWidth" name="email" placeholder="Email address" autocomplete="username" required autofocus>
 | 
			
		||||
                <input type="username" class="text big fullWidth marginTop" name="username" placeholder="Username">
 | 
			
		||||
                <input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" autocomplete="new-password" required minlength="6">
 | 
			
		||||
                <input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" autocomplete="new-password" minlength="6">
 | 
			
		||||
            <?php endif; ?>
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,9 @@ TODO: condition!
 | 
			
		||||
<div id="playMode" class="modal">
 | 
			
		||||
    <h2>Play map</h2>
 | 
			
		||||
    <a id="singleButton" class="button fullWidth marginTop" href="" title="Single player">Single player</a>
 | 
			
		||||
    <p class="bold center marginTop marginBottom">OR</p>
 | 
			
		||||
    <button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
 | 
			
		||||
    <?php if ($isLoggedIn): ?>
 | 
			
		||||
        <p class="bold center marginTop marginBottom">OR</p>
 | 
			
		||||
        <button id="multiButton" class="fullWidth green" data-map-id="">Multiplayer (beta)</button>
 | 
			
		||||
        <p class="bold center marginTop marginBottom">OR</p>
 | 
			
		||||
        <button id="challengeButton" class="fullWidth yellow" data-map-id="" data-timer="">Challenge (gamma)</button>
 | 
			
		||||
    <?php endif; ?>
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,6 @@
 | 
			
		||||
</main>
 | 
			
		||||
<footer>
 | 
			
		||||
    <p><span class="bold"><?= $_ENV['APP_NAME'] ?></span> <?= str_replace('Release_', '', VERSION) ?></p><!--
 | 
			
		||||
 --><p>© The MapGuesser Contributors <?= (new DateTime(REVISION_DATE))->format('Y') ?></p>
 | 
			
		||||
 --><p>© The <a href="https://git.esoko.eu/esoko/mapguesser" target="_blank">MapGuesser</a> Contributors <?= (new DateTime(REVISION_DATE))->format('Y') ?></p>
 | 
			
		||||
</footer>
 | 
			
		||||
@endsection
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								web.php
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								web.php
									
									
									
									
									
								
							@ -16,9 +16,8 @@ use MapGuesser\Repository\UserRepository;
 | 
			
		||||
 | 
			
		||||
require 'main.php';
 | 
			
		||||
 | 
			
		||||
error_reporting(E_ALL);
 | 
			
		||||
if (!empty($_ENV['DEV'])) {
 | 
			
		||||
    error_reporting(E_ALL);
 | 
			
		||||
 | 
			
		||||
    ini_set('display_errors', '1');
 | 
			
		||||
} else {
 | 
			
		||||
    ini_set('display_errors', '0');
 | 
			
		||||
@ -38,7 +37,6 @@ Container::$routeCollection->group('signup', function (RouteCollection $routeCol
 | 
			
		||||
    $routeCollection->get('signup', '', [LoginController::class, 'getSignupForm']);
 | 
			
		||||
    $routeCollection->post('signup-action', '', [LoginController::class, 'signup']);
 | 
			
		||||
    $routeCollection->get('signup-google', 'google', [LoginController::class, 'getSignupWithGoogleForm']);
 | 
			
		||||
    $routeCollection->post('signup-google-action', 'google', [LoginController::class, 'signupWithGoogle']);
 | 
			
		||||
    $routeCollection->post('signup.reset', 'reset', [LoginController::class, 'resetSignup']);
 | 
			
		||||
    $routeCollection->post('signup-google.reset', 'google/reset', [LoginController::class, 'resetGoogleSignup']);
 | 
			
		||||
    $routeCollection->get('signup.success', 'success', [LoginController::class, 'getSignupSuccess']);
 | 
			
		||||
@ -58,6 +56,11 @@ Container::$routeCollection->group('account', function (RouteCollection $routeCo
 | 
			
		||||
    $routeCollection->post('account-action', '', [UserController::class, 'saveAccount']);
 | 
			
		||||
    $routeCollection->get('account.delete', 'delete', [UserController::class, 'getDeleteAccount']);
 | 
			
		||||
    $routeCollection->post('account.delete-action', 'delete', [UserController::class, 'deleteAccount']);
 | 
			
		||||
    $routeCollection->get('account.googleConnect', 'googleConnect', [UserController::class, 'getGoogleConnectRedirect']);
 | 
			
		||||
    $routeCollection->get('account.googleConnect-confirm', 'googleConnect/code', [UserController::class, 'getGoogleConnectConfirm']);
 | 
			
		||||
    $routeCollection->post('account.googleConnect-action', 'googleConnect', [UserController::class, 'connectGoogle']);
 | 
			
		||||
    $routeCollection->get('account.googleDisconnect', 'googleDisconnect', [UserController::class, 'getGoogleDisconnectConfirm']);
 | 
			
		||||
    $routeCollection->post('account.googleDisconnect-action', 'googleDisconnect', [UserController::class, 'disconnectGoogle']);
 | 
			
		||||
    $routeCollection->get('account.googleAuthenticate', 'googleAuthenticate', [UserController::class, 'getGoogleAuthenticateRedirect']);
 | 
			
		||||
    $routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [UserController::class, 'authenticateWithGoogle']);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user