chore: init
This commit is contained in:
commit
ebcd871b24
107 changed files with 8701 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# phpMyAdmin
|
||||||
|
phpmyadmin/
|
||||||
|
|
||||||
|
# configuration
|
||||||
|
/config.php
|
||||||
|
|
||||||
|
# Composer
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# uploaded images
|
||||||
|
/img/mods/
|
||||||
|
/img/mods_pending/
|
19
composer.json
Normal file
19
composer.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "svrjs/svrjs-mod-directory",
|
||||||
|
"description": "The directory for SVR.JS mods",
|
||||||
|
"type": "project",
|
||||||
|
"require": {
|
||||||
|
"phpmailer/phpmailer": "^6.9",
|
||||||
|
"ext-mysqli": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-zlib": "*"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Dorian Niemiec",
|
||||||
|
"email": "dorian.niemiec@svrjs.org"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
105
composer.lock
generated
Normal file
105
composer.lock
generated
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "dded9f0f6e5d5d8ae98547cbe0680e5f",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "phpmailer/phpmailer",
|
||||||
|
"version": "v6.9.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||||
|
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||||
|
"reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"ext-filter": "*",
|
||||||
|
"ext-hash": "*",
|
||||||
|
"php": ">=5.5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||||
|
"doctrine/annotations": "^1.2.6 || ^1.13.3",
|
||||||
|
"php-parallel-lint/php-console-highlighter": "^1.0.0",
|
||||||
|
"php-parallel-lint/php-parallel-lint": "^1.3.2",
|
||||||
|
"phpcompatibility/php-compatibility": "^9.3.5",
|
||||||
|
"roave/security-advisories": "dev-latest",
|
||||||
|
"squizlabs/php_codesniffer": "^3.7.2",
|
||||||
|
"yoast/phpunit-polyfills": "^1.0.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication",
|
||||||
|
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
|
||||||
|
"ext-openssl": "Needed for secure SMTP sending and DKIM signing",
|
||||||
|
"greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication",
|
||||||
|
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
|
||||||
|
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
|
||||||
|
"psr/log": "For optional PSR-3 debug logging",
|
||||||
|
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)",
|
||||||
|
"thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"PHPMailer\\PHPMailer\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL-2.1-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Marcus Bointon",
|
||||||
|
"email": "phpmailer@synchromedia.co.uk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jim Jagielski",
|
||||||
|
"email": "jimjag@gmail.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Andy Prevost",
|
||||||
|
"email": "codeworxtech@users.sourceforge.net"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Brent R. Matzelle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
|
||||||
|
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/Synchro",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2024-11-24T18:04:13+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": {
|
||||||
|
"ext-mysqli": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-zlib": "*"
|
||||||
|
},
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.6.0"
|
||||||
|
}
|
44
config.example.php
Normal file
44
config.example.php
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
// Database connection parameters
|
||||||
|
define('MYSQL_HOST', "localhost");
|
||||||
|
define('MYSQL_USERNAME', "svrjs_mods");
|
||||||
|
define('MYSQL_PASSWORD', "svrjs_mods");
|
||||||
|
define('MYSQL_DATABASE', "svrjs_mods");
|
||||||
|
define('MYSQL_PORT', null);
|
||||||
|
|
||||||
|
// Whenever the URL is rewritten using web server's URL rewriting engine
|
||||||
|
define('URL_REWRITTEN', false);
|
||||||
|
|
||||||
|
// Elements per page
|
||||||
|
define('PAGE_MODS', 12);
|
||||||
|
define('PAGE_REVIEWS', 10);
|
||||||
|
|
||||||
|
// Email settings
|
||||||
|
define('EMAIL_SMTP', false);
|
||||||
|
define('EMAIL_SMTP_HOST', '');
|
||||||
|
define('EMAIL_SMTP_AUTHENTICATED', false);
|
||||||
|
define('EMAIL_SMTP_USERNAME', '');
|
||||||
|
define('EMAIL_SMTP_PASSWORD', '');
|
||||||
|
define('EMAIL_SMTP_ENCRYPTION', 0); // 0 for no encryption (port 25), 1 for STARTTLS (port 587), 2 for TLS/SSL (port 465)
|
||||||
|
define('EMAIL_SMTP_PORT', 25);
|
||||||
|
define('EMAIL_FROM', '');
|
||||||
|
define('EMAIL_FROM_NAME', '');
|
||||||
|
|
||||||
|
// Image specifications
|
||||||
|
define('IMAGE_EXTENSIONS_ALLOWED', ["png", "jpg", "jpeg"]);
|
||||||
|
define('IMAGE_MAX_SIZE', 4194304);
|
||||||
|
|
||||||
|
// Page compression (PHP-level) setting
|
||||||
|
define('COMPRESSION_ENABLED', true);
|
||||||
|
|
||||||
|
// hCaptcha settings
|
||||||
|
define('HCAPTCHA_ENABLED', false);
|
||||||
|
define('HCAPTCHA_SECRET_KEY', '');
|
||||||
|
define('HCAPTCHA_SITE_KEY', '');
|
||||||
|
|
||||||
|
// StopForumSpam settings
|
||||||
|
define('STOPFORUMSPAM_ENABLED', true);
|
||||||
|
define('STOPFORUMSPAM_CHECK_USERNAME', true);
|
||||||
|
define('STOPFORUMSPAM_CHECK_EMAIL', true);
|
||||||
|
define('STOPFORUMSPAM_CHECK_IP', true);
|
||||||
|
define('STOPFORUMSPAM_THRESHOLD', 5);
|
375
css/cookieconsent.min.css
vendored
Normal file
375
css/cookieconsent.min.css
vendored
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
.cc-window {
|
||||||
|
opacity: 1;
|
||||||
|
-webkit-transition: opacity 1s ease;
|
||||||
|
transition: opacity 1s ease
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-invisible {
|
||||||
|
opacity: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate.cc-revoke {
|
||||||
|
-webkit-transition: transform 1s ease;
|
||||||
|
-webkit-transition: -webkit-transform 1s ease;
|
||||||
|
transition: -webkit-transform 1s ease;
|
||||||
|
transition: transform 1s ease;
|
||||||
|
transition: transform 1s ease, -webkit-transform 1s ease
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate.cc-revoke.cc-top {
|
||||||
|
-webkit-transform: translateY(-2em);
|
||||||
|
transform: translateY(-2em)
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate.cc-revoke.cc-bottom {
|
||||||
|
-webkit-transform: translateY(2em);
|
||||||
|
transform: translateY(2em)
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate.cc-revoke.cc-active.cc-top {
|
||||||
|
-webkit-transform: translateY(0);
|
||||||
|
transform: translateY(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-animate.cc-revoke.cc-active.cc-bottom {
|
||||||
|
-webkit-transform: translateY(0);
|
||||||
|
transform: translateY(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke:hover {
|
||||||
|
-webkit-transform: translateY(0);
|
||||||
|
transform: translateY(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-grower {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-transition: max-height 1s;
|
||||||
|
transition: max-height 1s
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke,
|
||||||
|
.cc-window {
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-ms-flex-wrap: nowrap;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
z-index: 9999
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-static {
|
||||||
|
position: static
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-floating {
|
||||||
|
padding: 2em;
|
||||||
|
max-width: 24em;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner {
|
||||||
|
padding: 1em 1.8em;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-box-orient: horizontal;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke {
|
||||||
|
padding: .5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke:hover {
|
||||||
|
text-decoration: underline
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-header {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-btn,
|
||||||
|
.cc-close,
|
||||||
|
.cc-link,
|
||||||
|
.cc-revoke {
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-link {
|
||||||
|
opacity: .8;
|
||||||
|
display: inline-block;
|
||||||
|
padding: .2em;
|
||||||
|
text-decoration: underline
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-link:hover {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-link:active,
|
||||||
|
.cc-link:visited {
|
||||||
|
color: initial
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-btn {
|
||||||
|
display: block;
|
||||||
|
padding: .4em .8em;
|
||||||
|
font-size: .9em;
|
||||||
|
font-weight: 700;
|
||||||
|
border-width: 2px;
|
||||||
|
border-style: solid;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-highlight .cc-btn:first-child {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-highlight .cc-btn:first-child:focus,
|
||||||
|
.cc-highlight .cc-btn:first-child:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
text-decoration: underline
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-close {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: .5em;
|
||||||
|
right: .5em;
|
||||||
|
font-size: 1.6em;
|
||||||
|
opacity: .9;
|
||||||
|
line-height: .75
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-close:focus,
|
||||||
|
.cc-close:hover {
|
||||||
|
opacity: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke.cc-top {
|
||||||
|
top: 0;
|
||||||
|
left: 3em;
|
||||||
|
border-bottom-left-radius: .5em;
|
||||||
|
border-bottom-right-radius: .5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke.cc-bottom {
|
||||||
|
bottom: 0;
|
||||||
|
left: 3em;
|
||||||
|
border-top-left-radius: .5em;
|
||||||
|
border-top-right-radius: .5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke.cc-left {
|
||||||
|
left: 3em;
|
||||||
|
right: unset
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-revoke.cc-right {
|
||||||
|
right: 3em;
|
||||||
|
left: unset
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-top {
|
||||||
|
top: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-left {
|
||||||
|
left: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-right {
|
||||||
|
right: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-bottom {
|
||||||
|
bottom: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating>.cc-link {
|
||||||
|
margin-bottom: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating .cc-message {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-floating .cc-compliance {
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-ms-flex: 1 0 auto;
|
||||||
|
flex: 1 0 auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner {
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-banner.cc-top {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-banner.cc-bottom {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-banner .cc-message {
|
||||||
|
display: block;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-ms-flex: 1 1 auto;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-right: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-compliance {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-ms-flex-line-pack: justify;
|
||||||
|
align-content: space-between
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating .cc-compliance>.cc-btn {
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-ms-flex: 1;
|
||||||
|
flex: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-btn+.cc-btn {
|
||||||
|
margin-left: .5em
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
|
||||||
|
.cc-revoke,
|
||||||
|
.cc-window {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:900px) {
|
||||||
|
.cc-btn {
|
||||||
|
white-space: normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:414px) and (orientation:portrait),
|
||||||
|
screen and (max-width:736px) and (orientation:landscape) {
|
||||||
|
.cc-window.cc-top {
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-bottom {
|
||||||
|
bottom: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner,
|
||||||
|
.cc-window.cc-floating,
|
||||||
|
.cc-window.cc-left,
|
||||||
|
.cc-window.cc-right {
|
||||||
|
left: 0;
|
||||||
|
right: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner {
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner .cc-compliance {
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
-ms-flex: 1 1 auto;
|
||||||
|
flex: 1 1 auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-floating {
|
||||||
|
max-width: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window .cc-message {
|
||||||
|
margin-bottom: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner {
|
||||||
|
-webkit-box-align: unset;
|
||||||
|
-ms-flex-align: unset;
|
||||||
|
align-items: unset
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-window.cc-banner .cc-message {
|
||||||
|
margin-right: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating.cc-theme-classic {
|
||||||
|
padding: 1.2em;
|
||||||
|
border-radius: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating.cc-type-info.cc-theme-classic .cc-compliance {
|
||||||
|
text-align: center;
|
||||||
|
display: inline;
|
||||||
|
-webkit-box-flex: 0;
|
||||||
|
-ms-flex: none;
|
||||||
|
flex: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-theme-classic .cc-btn {
|
||||||
|
border-radius: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-theme-classic .cc-btn:last-child {
|
||||||
|
min-width: 140px
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating.cc-type-info.cc-theme-classic .cc-btn {
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-theme-edgeless.cc-window {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating.cc-theme-edgeless .cc-message {
|
||||||
|
margin: 2em;
|
||||||
|
margin-bottom: 1.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-banner.cc-theme-edgeless .cc-btn {
|
||||||
|
margin: 0;
|
||||||
|
padding: .8em 1.8em;
|
||||||
|
height: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-banner.cc-theme-edgeless .cc-message {
|
||||||
|
margin-left: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-floating.cc-theme-edgeless .cc-btn+.cc-btn {
|
||||||
|
margin-left: 0
|
||||||
|
}
|
359
css/font.css
Normal file
359
css/font.css
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiAyp8kv8JHgFVrJJLmE0tCMPI.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm21lVF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiGyp8kv8JHgFVrJJLufntAKPY.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiGyp8kv8JHgFVrJJLucHtA.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmr19VF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLmy15VF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm111VF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiDyp8kv8JHgFVrJJLm81xVF9eO.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiGyp8kv8JHgFVrLPTucHtA.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLFj_Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLDD4Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Poppins';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
font-display: swap;
|
||||||
|
src: url(../fonts/pxiByp8kv8JHgFVrLBT5Z1xlFQ.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
800
css/main.css
Normal file
800
css/main.css
Normal file
|
@ -0,0 +1,800 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Poppins, sans-serif;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1440px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1px solid #e4e4e7;
|
||||||
|
padding: 0.5em;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header::after {
|
||||||
|
display: table;
|
||||||
|
content: '';
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-logo {
|
||||||
|
display: inline-block;
|
||||||
|
background-image: url('../img/logo.png');
|
||||||
|
background-size: 162px 40px;
|
||||||
|
width: 162px;
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 1em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav {
|
||||||
|
background-color: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
float: right;
|
||||||
|
min-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li form {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a,
|
||||||
|
header nav ul li form input[type="submit"],
|
||||||
|
header nav ul li form button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5em 0.75em;
|
||||||
|
margin: 0 0.1em;
|
||||||
|
background-color: inherit;
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a.nav-active,
|
||||||
|
header nav ul li form input.nav-active[type="submit"],
|
||||||
|
header nav ul li form button.nav-active {
|
||||||
|
background-color: #e2e2e4;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:hover,
|
||||||
|
header nav ul li form input[type="submit"]:hover,
|
||||||
|
header nav ul li form button:hover {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:focus,
|
||||||
|
header nav ul li form input[type="submit"]:focus,
|
||||||
|
header nav ul li form button:focus {
|
||||||
|
outline: revert;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-hamburger {
|
||||||
|
display: none;
|
||||||
|
float: right;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-hamburger .header-hamburger-bar {
|
||||||
|
display: block;
|
||||||
|
background-color: #000000;
|
||||||
|
width: 30px;
|
||||||
|
height: 5px;
|
||||||
|
margin: 6.25px 5px;
|
||||||
|
border-radius: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened {
|
||||||
|
display: block;
|
||||||
|
border-bottom: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened nav {
|
||||||
|
display: block;
|
||||||
|
float: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 50px 0.5em 0.5em 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-js.header-nav-opened nav {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened nav ul {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened nav ul li {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened nav ul li form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened nav ul li a,
|
||||||
|
header.header-nav-opened nav ul li form input[type="submit"],
|
||||||
|
header.header-nav-opened nav ul li form button {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened .header-logo {
|
||||||
|
position: fixed;
|
||||||
|
display: block;
|
||||||
|
top: 0.5em;
|
||||||
|
left: 0.5em;
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-nav-opened .header-hamburger {
|
||||||
|
position: fixed;
|
||||||
|
display: block;
|
||||||
|
top: 0.5em;
|
||||||
|
right: 0.5em;
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero {
|
||||||
|
padding: 3em 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-bottom: 1px solid #e4e4e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero h1 {
|
||||||
|
font-size: 4em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero p {
|
||||||
|
font-size: 1.25em;
|
||||||
|
color: #71717a;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.search-form {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: 1em auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.search-form input[name="q"] {
|
||||||
|
width: 128px;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.search-form input[type="submit"],
|
||||||
|
form.search-form button {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero form.search-form {
|
||||||
|
margin: 3em auto;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.content {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #f6f6f7;
|
||||||
|
color: #000000;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
min-height: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline: 2px solid #2cac5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]+label,
|
||||||
|
input[type="radio"]+label {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"],
|
||||||
|
button,
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #16a34a;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
button:hover,
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #2cac5b;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.category-headline {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category h3 {
|
||||||
|
margin: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category p {
|
||||||
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-top: 1px solid #e4e4e7;
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container {
|
||||||
|
width: 33.3333%;
|
||||||
|
padding: 0.5em;
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card {
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card img {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 10px 10px 0 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card .mods-mod-card-contents {
|
||||||
|
padding: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card .mods-mod-card-contents h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card .mods-mod-card-contents p {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
margin-bottom: 0.75em;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card .mods-mod-card-contents .mods-mod-card-bottom {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card .mods-mod-card-contents .mods-mod-card-bottom p.mods-mod-card-publisher {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background-color: #16a34a;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 9999px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
margin: 0 0.25em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
display: block;
|
||||||
|
margin: 1em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination a,
|
||||||
|
.pagination span {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.mod-cover {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
max-width: 1024px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container .btn {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-links {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-badges {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-header h1 {
|
||||||
|
flex-grow: 1;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-header .mod-badges {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block {
|
||||||
|
display: block;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block-checkbox {
|
||||||
|
display: block;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
color: #ff0000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-captcha-container div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-weak {
|
||||||
|
color: #ff0000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-medium {
|
||||||
|
color: #ff7f00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-strong {
|
||||||
|
color: #007f00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-rating {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-stars {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-star.rating-star-higlighted {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review {
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review .review-header {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review p {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select label {
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select input:not(:checked)~label:hover,
|
||||||
|
.rating-select input:not(:checked)~label:hover~label {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select input:checked~label {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1024px) {
|
||||||
|
.mods .mods-mod-container {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
header.header-js .header-hamburger {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.header-js nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero {
|
||||||
|
padding-top: 2em;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero h1 {
|
||||||
|
font-size: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category h3,
|
||||||
|
.category p {
|
||||||
|
align-self: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container .btn {
|
||||||
|
margin: 0.25em 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-links {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-badges {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-header {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-header h1 {
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block input,
|
||||||
|
form.form .form-block label,
|
||||||
|
form.form .form-block textarea,
|
||||||
|
form.form .form-block select,
|
||||||
|
form.form .form-block button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-captcha-container {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-header {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (prefers-color-scheme: dark) {
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
background-color: #0c0a09;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #0c0a09;
|
||||||
|
border-bottom-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-logo {
|
||||||
|
background-image: url('../img/logo-dark.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav {
|
||||||
|
background-color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a,
|
||||||
|
header nav ul li form input[type="submit"],
|
||||||
|
header nav ul li form button {
|
||||||
|
background-color: inherit;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a.nav-active,
|
||||||
|
header nav ul li form input.nav-active[type="submit"],
|
||||||
|
header nav ul li form button.nav-active {
|
||||||
|
background-color: #2a2829;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:hover,
|
||||||
|
header nav ul li form input[type="submit"]:hover,
|
||||||
|
header nav ul li form button:hover {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-hamburger .header-hamburger-bar {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero {
|
||||||
|
background-color: #090907;
|
||||||
|
border-bottom-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
section.hero p {
|
||||||
|
color: #a1a1aa;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
background-color: #221f1e;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline: 2px solid #2cac5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"],
|
||||||
|
button,
|
||||||
|
.btn {
|
||||||
|
background-color: #22c55e;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
button:hover,
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #21b356;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: #090907;
|
||||||
|
border-top-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mods .mods-mod-container .mods-mod-card {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background-color: #22c55e;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
color: #ea4648;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-weak {
|
||||||
|
color: #ea4648;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-medium {
|
||||||
|
color: #ff7f00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.password-strong {
|
||||||
|
color: #00ff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-star.rating-star-higlighted {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select input:not(:checked)~label:hover,
|
||||||
|
.rating-select input:not(:checked)~label:hover~label {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-select input:checked~label {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
}
|
386
css/moderation.css
Normal file
386
css/moderation.css
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Poppins, sans-serif;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1px solid #e4e4e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .header-title {
|
||||||
|
padding: 0.25em;
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav {
|
||||||
|
padding: 0.5em;
|
||||||
|
padding-top: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li form {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a,
|
||||||
|
header nav ul li form input[type="submit"],
|
||||||
|
header nav ul li form button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.5em 0.75em;
|
||||||
|
margin: 0 0.1em;
|
||||||
|
background-color: inherit;
|
||||||
|
color: #000000;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a.nav-active {
|
||||||
|
background-color: #e2e2e4;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:hover {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:focus {
|
||||||
|
outline: revert;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #f6f6f7;
|
||||||
|
color: #000000;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
min-height: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline: 2px solid #2cac5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
width: 1.25em;
|
||||||
|
height: 1.25em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]+label,
|
||||||
|
input[type="radio"]+label {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"],
|
||||||
|
button,
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #16a34a;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0.5em 1em;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
button:hover,
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #2cac5b;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block {
|
||||||
|
display: block;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block-checkbox {
|
||||||
|
display: block;
|
||||||
|
margin: 0.25em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
color: #ff0000;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background-color: #16a34a;
|
||||||
|
color: #ffffff;
|
||||||
|
border-radius: 9999px;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0.5em;
|
||||||
|
margin: 0 0.25em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod {
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 1em 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod img {
|
||||||
|
width: 320px;
|
||||||
|
align-self: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod .mod-info {
|
||||||
|
padding: 0.5em;
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-stars {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-star.rating-star-higlighted {
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review {
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 0.25em 0.5em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review .review-header {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review p {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
padding: 0.5em;
|
||||||
|
background-color: #f9f9fa;
|
||||||
|
border: 1px solid #e4e4e7;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category h3 {
|
||||||
|
margin: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category p {
|
||||||
|
margin: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
header .header-title {
|
||||||
|
padding: 0.375em;
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.form .form-block input,
|
||||||
|
form.form .form-block label,
|
||||||
|
form.form .form-block textarea,
|
||||||
|
form.form .form-block select,
|
||||||
|
form.form .form-block button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod .mod-info {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-header {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category h3,
|
||||||
|
.category p {
|
||||||
|
align-self: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (prefers-color-scheme: dark) {
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
background-color: #0c0a09;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #0c0a09;
|
||||||
|
border-bottom-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a {
|
||||||
|
background-color: inherit;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a.nav-active {
|
||||||
|
background-color: #2a2829;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li a:hover {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
background-color: #221f1e;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline: 2px solid #2cac5b;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"],
|
||||||
|
button,
|
||||||
|
.btn {
|
||||||
|
background-color: #22c55e;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
button:hover,
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #21b356;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background-color: #292524;
|
||||||
|
color: #f2f2f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
color: #ea4648;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background-color: #22c55e;
|
||||||
|
color: #0c0a09;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rating-star.rating-star-higlighted {
|
||||||
|
color: #22c55e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
background-color: #191817;
|
||||||
|
border-color: #27272a;
|
||||||
|
}
|
||||||
|
}
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
fonts/pxiAyp8kv8JHgFVrJJLmE0tCMPI.woff2
Normal file
BIN
fonts/pxiAyp8kv8JHgFVrJJLmE0tCMPI.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.woff2
Normal file
BIN
fonts/pxiAyp8kv8JHgFVrJJLmE0tMMPKzSQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLBT5Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLBT5Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLBT5Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLDD4Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLDD4Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLDD4Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLFj_Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLFj_Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLFj_Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLGT9Z1JlFc-K.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2
Normal file
BIN
fonts/pxiByp8kv8JHgFVrLGT9Z1xlFQ.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm111VF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm111VF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm111VGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm21lVF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm21lVF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm21lVGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm81xVF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm81xVF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLm81xVGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmg1hVF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmg1hVGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmr19VF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmr19VF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmr19VGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmv1pVF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmv1pVGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmy15VF9eO.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmy15VF9eO.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.woff2
Normal file
BIN
fonts/pxiDyp8kv8JHgFVrJJLmy15VGdeOcEg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiEyp8kv8JHgFVrJJfecg.woff2
Normal file
BIN
fonts/pxiEyp8kv8JHgFVrJJfecg.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiEyp8kv8JHgFVrJJnecmNE.woff2
Normal file
BIN
fonts/pxiEyp8kv8JHgFVrJJnecmNE.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiGyp8kv8JHgFVrJJLucHtA.woff2
Normal file
BIN
fonts/pxiGyp8kv8JHgFVrJJLucHtA.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiGyp8kv8JHgFVrJJLufntAKPY.woff2
Normal file
BIN
fonts/pxiGyp8kv8JHgFVrJJLufntAKPY.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiGyp8kv8JHgFVrLPTucHtA.woff2
Normal file
BIN
fonts/pxiGyp8kv8JHgFVrLPTucHtA.woff2
Normal file
Binary file not shown.
BIN
fonts/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2
Normal file
BIN
fonts/pxiGyp8kv8JHgFVrLPTufntAKPY.woff2
Normal file
Binary file not shown.
BIN
img/cover.png
Normal file
BIN
img/cover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
BIN
img/logo-dark.png
Normal file
BIN
img/logo-dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
img/logo.png
Normal file
BIN
img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
BIN
img/mod-missing.png
Normal file
BIN
img/mod-missing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
3
includes/final.php
Normal file
3
includes/final.php
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$connection->close();
|
20
includes/footer.php
Normal file
20
includes/footer.php
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<?php if (!defined('SVRJS_MOD_DIRECTORY')) die; ?>
|
||||||
|
<footer>
|
||||||
|
<div class="wrapper">
|
||||||
|
<p>Copyright © <?php $copyrightYear = intval(date("Y"));
|
||||||
|
echo $copyrightYear > 2024 ? "2024-$copyrightYear" : "2024" ?> <a href="https://svrjs.org">SVR.JS</a>. All rights reserved.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'tos') ?>">Terms of Service</a> | <a href="https://svrjs.org/privacy">Privacy Policy</a> | <a href="https://svrjs.org/contact">Contact us</a></p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT); ?>js/cookieconsent.min.js"></script>
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT); ?>js/cookieconsentinit.js"></script>
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT); ?>js/hamburger.js"></script>
|
||||||
|
<?php if (isset($passwordStrengthMeter) && $passwordStrengthMeter) { ?>
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT); ?>js/passwordStrength.js"></script>
|
||||||
|
<?php } ?>
|
||||||
|
<?php if (isset($hCaptchaForm) && $hCaptchaForm) { ?>
|
||||||
|
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
|
||||||
|
<?php } ?>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
79
includes/header.php
Normal file
79
includes/header.php
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$username = null;
|
||||||
|
$isModerator = false;
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$usernameStatement = $connection->prepare('SELECT username, is_moderator FROM users WHERE id = ?;');
|
||||||
|
if ($usernameStatement) {
|
||||||
|
$usernameStatement->bind_param('i', $_SESSION['user']);
|
||||||
|
$usernameStatement->execute();
|
||||||
|
$usernameResult = $usernameStatement->get_result();
|
||||||
|
if ($usernameResult) {
|
||||||
|
$usernameRow = $usernameResult->fetch_assoc();
|
||||||
|
if ($usernameRow) {
|
||||||
|
$username = $usernameRow['username'];
|
||||||
|
$isModerator = boolval($usernameRow['is_moderator']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$usernameStatement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en-US">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!--[if lt IE 9]><script src="<?php echo htmlspecialchars(APP_ROOT . 'js/html5shiv.js'); ?>"></script><![endif]-->
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT . 'js/analytics.js'); ?>"></script>
|
||||||
|
<link rel="stylesheet" href="<?php echo htmlspecialchars(APP_ROOT); ?>css/font.css">
|
||||||
|
<link rel="stylesheet" href="<?php echo htmlspecialchars(APP_ROOT); ?>css/main.css">
|
||||||
|
<link rel="stylesheet" href="<?php echo htmlspecialchars(APP_ROOT); ?>css/cookieconsent.min.css">
|
||||||
|
<title><?php echo htmlentities(isset($pageTitle) && $pageTitle ? "$pageTitle - SVR.JS Mods" : "SVR.JS Mods"); ?></title>
|
||||||
|
<meta name="description" content="<?php echo htmlspecialchars((isset($pageDescription) && $pageDescription) ? $pageDescription : "Expand the functionality of SVR.JS with mods! Visit the SVR.JS Mods directory to explore and install a variety of mods for a better web server experience.") ?>">
|
||||||
|
<meta name="og:title" content=" <?php echo htmlspecialchars((isset($pageTitle) && $pageTitle) ? "$pageTitle - SVR.JS Mods" : "SVR.JS Mods") ?>">
|
||||||
|
<meta name="og:type" content="website">
|
||||||
|
<meta name="og:description" content="<?php echo htmlspecialchars((isset($pageDescription) && $pageDescription) ? $pageDescription : "Expand the functionality of SVR.JS with mods! Visit the SVR.JS Mods directory to explore and install a variety of mods for a better web server experience.") ?>">
|
||||||
|
<meta name="og:url" content="<?php echo htmlspecialchars((isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . $_SERVER['REQUEST_URI']); ?>">
|
||||||
|
<meta name="og:image" content="<?php echo htmlspecialchars((isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . APP_ROOT . 'img/' . (isset($pageImage) && $pageImage ? $pageImage : 'cover.png')); ?>">
|
||||||
|
<meta name="og:image:width" content="800">
|
||||||
|
<meta name="og:image:height" content="600">
|
||||||
|
<meta name="og:image:alt" content="<?php echo htmlspecialchars((isset($pageTitle) && $pageTitle) ? "$pageTitle - SVR.JS Mods" : "SVR.JS Mods") ?>">
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta name="twitter:title" content=" <?php echo htmlspecialchars((isset($pageTitle) && $pageTitle) ? "$pageTitle - SVR.JS Mods" : "SVR.JS Mods") ?>">
|
||||||
|
<meta name="twitter:description" content="<?php echo htmlspecialchars((isset($pageDescription) && $pageDescription) ? $pageDescription : "Expand the functionality of SVR.JS with mods! Visit the SVR.JS Mods directory to explore and install a variety of mods for a better web server experience.") ?>">
|
||||||
|
<meta name="twitter:image" content="<?php echo htmlspecialchars((isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . APP_ROOT . 'img/' . (isset($pageImage) && $pageImage ? $pageImage : 'cover.png')); ?>">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header id="header">
|
||||||
|
<div class="wrapper">
|
||||||
|
<a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="header-logo"></a>
|
||||||
|
<nav id="header-nav">
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" <?php if (count($segments) == 0 || (count($segments) == 1 && $segments[0] == '')) echo 'class="nav-active"'; ?>>Home</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search') ?>" <?php if (count($segments) == 1 && $segments[0] == 'search') echo 'class="nav-active"'; ?>>Search</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'submit') ?>" <?php if (count($segments) == 1 && $segments[0] == 'submit') echo 'class="nav-active"'; ?>>Submit</a></li>
|
||||||
|
<?php if (isset($_SESSION['user'])) { ?>
|
||||||
|
<?php if ($isModerator) { ?>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'index.php') ?>">Moderation</a></li>
|
||||||
|
<?php } ?>
|
||||||
|
<li><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($username)) ?>" <?php if (count($segments) == 2 && ($segments[0] == 'user' || $segments[0] == 'user-mods' || $segments[0] == 'user-reviews') && strtolower($segments[1]) == strtolower($username)) echo 'class="nav-active"'; ?>>Profile</a></li>
|
||||||
|
<li>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'logout') ?>" method="post"><input type="submit" value="Logout"><input type="hidden" name="_csrf" value="<?php echo $_SESSION['csrf']; ?>"><input type="hidden" name="redirect" value="<?php echo $_SERVER['REQUEST_URI']; ?>"></form>
|
||||||
|
</li>
|
||||||
|
<?php } else { ?>
|
||||||
|
<li><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI'])) ?>" <?php if (count($segments) == 1 && $segments[0] == 'login') echo 'class="nav-active"'; ?>>Login</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'register') ?>" <?php if (count($segments) == 1 && $segments[0] == 'register') echo 'class="nav-active"'; ?>>Register</a></li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<span id="header-hamburger" class="header-hamburger" tabindex="0">
|
||||||
|
<span class="header-hamburger-bar"></span>
|
||||||
|
<span class="header-hamburger-bar"></span>
|
||||||
|
<span class="header-hamburger-bar"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
113
includes/init.php
Normal file
113
includes/init.php
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$requiredExtensions = ['mysqli'];
|
||||||
|
|
||||||
|
if (HCAPTCHA_ENABLED || STOPFORUMSPAM_ENABLED) array_push($requiredExtensions, 'json', 'curl');
|
||||||
|
if (COMPRESSION_ENABLED) array_push($requiredExtensions, 'zlib');
|
||||||
|
|
||||||
|
foreach ($requiredExtensions as $requiredExtension) {
|
||||||
|
if (!extension_loaded($requiredExtension)) {
|
||||||
|
die("The required PHP extension isn't loaded - " . htmlspecialchars($requiredExtension));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'utils.php';
|
||||||
|
|
||||||
|
$mysqlDriver = new mysqli_driver();
|
||||||
|
$mysqlDriver->report_mode = MYSQLI_REPORT_OFF;
|
||||||
|
|
||||||
|
$connection = new mysqli(
|
||||||
|
MYSQL_HOST,
|
||||||
|
MYSQL_USERNAME,
|
||||||
|
MYSQL_PASSWORD,
|
||||||
|
MYSQL_DATABASE,
|
||||||
|
MYSQL_PORT
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($connection->connect_error) die("Error connecting to a database.");
|
||||||
|
|
||||||
|
// Custom session handler functions
|
||||||
|
class MySQLSessionHandler
|
||||||
|
{
|
||||||
|
private $mysqli;
|
||||||
|
|
||||||
|
public function __construct($mysqli)
|
||||||
|
{
|
||||||
|
$this->mysqli = $mysqli;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function open($savePath, $sessionName)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function read($id)
|
||||||
|
{
|
||||||
|
$data = null;
|
||||||
|
$stmt = $this->mysqli->prepare("SELECT data FROM sessions WHERE id = ?");
|
||||||
|
if (!$stmt) return '';
|
||||||
|
$stmt->bind_param('s', $id);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->bind_result($data);
|
||||||
|
$stmt->fetch();
|
||||||
|
$stmt->close();
|
||||||
|
return isset($data) && $data ? $data : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write($id, $data)
|
||||||
|
{
|
||||||
|
$stmt = $this->mysqli->prepare("REPLACE INTO sessions (id, data) VALUES (?, ?)");
|
||||||
|
if (!$stmt) return false;
|
||||||
|
$stmt->bind_param('ss', $id, $data);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
$stmt = $this->mysqli->prepare("DELETE FROM sessions WHERE id = ?");
|
||||||
|
if (!$stmt) return false;
|
||||||
|
$stmt->bind_param('s', $id);
|
||||||
|
return $stmt->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gc($maxlifetime)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create_sid()
|
||||||
|
{
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$sid = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$sid = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$sid = $sid . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate_sid($key)
|
||||||
|
{
|
||||||
|
$stmt = $this->mysqli->prepare("SELECT data FROM sessions WHERE id = ?");
|
||||||
|
if (!$stmt) return false;
|
||||||
|
$stmt->bind_param('s', $key);
|
||||||
|
$stmt->execute();
|
||||||
|
$result = $stmt->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$stmt->close();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$valid = boolval($result->fetch_assoc());
|
||||||
|
$stmt->close();
|
||||||
|
return $valid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
includes/moderation_final.php
Normal file
25
includes/moderation_final.php
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY_MODERATION')) die;
|
||||||
|
|
||||||
|
if (session_id()) session_write_close();
|
||||||
|
|
||||||
|
if (ob_get_status()) {
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
if ($content && COMPRESSION_ENABLED && isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
|
||||||
|
$acceptEncoding = array_map(function ($encoding) {
|
||||||
|
return trim($encoding);
|
||||||
|
}, explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']));
|
||||||
|
|
||||||
|
if (in_array('gzip', $acceptEncoding)) {
|
||||||
|
$content = gzencode($content, 9);
|
||||||
|
header('Content-Encoding: gzip');
|
||||||
|
} elseif (in_array('deflate', $acceptEncoding)) {
|
||||||
|
$content = gzdeflate($content, 9);
|
||||||
|
header('Content-Encoding: deflate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $content;
|
||||||
|
}
|
8
includes/moderation_footer.php
Normal file
8
includes/moderation_footer.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY_MODERATION')) die;
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
37
includes/moderation_header.php
Normal file
37
includes/moderation_header.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY_MODERATION')) die;
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en-US">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!--[if lt IE 9]><script src="<?php echo htmlspecialchars(APP_ROOT . 'js/html5shiv.js'); ?>"></script><![endif]-->
|
||||||
|
<script src="<?php echo htmlspecialchars(APP_ROOT . 'js/analytics.js'); ?>"></script>
|
||||||
|
<link rel="stylesheet" href="<?php echo htmlspecialchars(APP_ROOT); ?>css/font.css">
|
||||||
|
<link rel="stylesheet" href="<?php echo htmlspecialchars(APP_ROOT); ?>css/moderation.css">
|
||||||
|
<title><?php echo htmlentities(isset($pageTitle) && $pageTitle ? "$pageTitle - Moderation - SVR.JS Mods" : "Moderation - SVR.JS Mods"); ?></title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="header-title">
|
||||||
|
<span>Moderation</span>
|
||||||
|
</div>
|
||||||
|
<?php if (!isset($moderationNotAllowed) || !$moderationNotAllowed) { ?>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_ROOT); ?>">Return</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'index.php'); ?>" <?php if (APP_MODERATION_FILENAME == "index.php") echo 'class="nav-active"'; ?>>Home</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'pending.php'); ?>" <?php if (APP_MODERATION_FILENAME == "pending.php") echo 'class="nav-active"'; ?>>Pending mods</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'mods.php'); ?>" <?php if (APP_MODERATION_FILENAME == "mods.php") echo 'class="nav-active"'; ?>>Mods</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'users.php'); ?>" <?php if (APP_MODERATION_FILENAME == "users.php" || APP_MODERATION_FILENAME == "user.php") echo 'class="nav-active"'; ?>>Users</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'reviews.php'); ?>" <?php if (APP_MODERATION_FILENAME == "reviews.php") echo 'class="nav-active"'; ?>>Reviews</a></li>
|
||||||
|
<li><a href="<?php echo htmlspecialchars(APP_MODERATION_ROOT . 'categories.php'); ?>" <?php if (APP_MODERATION_FILENAME == "categories.php") echo 'class="nav-active"'; ?>>Categories</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<?php } ?>
|
||||||
|
</header>
|
||||||
|
<main>
|
85
includes/moderation_init.php
Normal file
85
includes/moderation_init.php
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY_MODERATION')) die;
|
||||||
|
|
||||||
|
// THIS PHP SCRIPT INCLUDE IS JUST FOR HTML PAGES! IT'S NOT INTENDED FOR OTHER FORMATS
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Enable session strict mode
|
||||||
|
ini_set('session.use_strict_mode', '1');
|
||||||
|
|
||||||
|
// Set session cookie flags
|
||||||
|
ini_set('session.cookie_httponly', '1');
|
||||||
|
if (isset($_SERVER['HTTPS'])) ini_set('session.cookie_secure', '1');
|
||||||
|
|
||||||
|
// Register the custom session handler
|
||||||
|
$sessionHandler = new MySQLSessionHandler($connection);
|
||||||
|
session_set_save_handler(
|
||||||
|
array($sessionHandler, 'open'),
|
||||||
|
array($sessionHandler, 'close'),
|
||||||
|
array($sessionHandler, 'read'),
|
||||||
|
array($sessionHandler, 'write'),
|
||||||
|
array($sessionHandler, 'destroy'),
|
||||||
|
array($sessionHandler, 'gc'),
|
||||||
|
array($sessionHandler, 'create_sid'),
|
||||||
|
array($sessionHandler, 'validate_sid')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (session_start()) {
|
||||||
|
setupHeaders();
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$statement = $connection->prepare("SELECT id, username, is_moderator FROM users WHERE id = ? AND is_suspended = 0 AND is_deleted = 0 AND is_verified = 1");
|
||||||
|
if (!$statement) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
} else {
|
||||||
|
$statement->bind_param("i", $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
} else {
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
|
if (!$row) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
} elseif (!$row['is_moderator']) {
|
||||||
|
http_response_code(403);
|
||||||
|
include 'moderation_notallowed.php';
|
||||||
|
include 'moderation_final.php';
|
||||||
|
include 'final.php';
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
include 'moderation_notallowed.php';
|
||||||
|
include 'moderation_final.php';
|
||||||
|
include 'final.php';
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setupHeaders();
|
||||||
|
http_response_code(403);
|
||||||
|
include 'moderation_notallowed.php';
|
||||||
|
include 'moderation_final.php';
|
||||||
|
include 'final.php';
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$csrfToken = "";
|
||||||
|
if (isset($_SESSION['moderation_csrf'])) {
|
||||||
|
$csrfToken = $_SESSION['moderation_csrf'];
|
||||||
|
} else {
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$csrfToken = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$csrfToken = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$csrfToken = $csrfToken . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['moderation_csrf'] = $csrfToken;
|
||||||
|
}
|
14
includes/moderation_notallowed.php
Normal file
14
includes/moderation_notallowed.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY_MODERATION')) die;
|
||||||
|
|
||||||
|
$moderationNotAllowed = true;
|
||||||
|
$pageTitle = "Not allowed";
|
||||||
|
include 'moderation_header.php';
|
||||||
|
?>
|
||||||
|
<h1>Not allowed</h1>
|
||||||
|
<p>You don't have permissions to moderate.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(APP_ROOT); ?>" class="btn">Return</a></p>
|
||||||
|
<?php
|
||||||
|
include 'moderation_footer.php';
|
||||||
|
?>
|
14
includes/page_404.php
Normal file
14
includes/page_404.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "404 Not Found";
|
||||||
|
$pageDescription = "404 Not Found";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
<p>The page you have requested doesn't exist.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
14
includes/page_500.php
Normal file
14
includes/page_500.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "500 Internal Server Error";
|
||||||
|
$pageDescription = "500 Internal Server Error";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>500 Internal Server Error</h1>
|
||||||
|
<p>Uh oh! Something went wrong on our side!</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
140
includes/page_category.php
Normal file
140
includes/page_category.php
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $categoryData['name'];
|
||||||
|
$pageDescription = 'Explore SVR.JS mods in the ' . $categoryData['name'] . ' category.';
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($categoryData['name']) ?></h1>
|
||||||
|
<?php
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(mods.id) AS count
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN categories ON categories.id = mods.category
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE categories.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1;');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('i', $categoryData['id']);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id,
|
||||||
|
AVG(reviews.rating) AS rating,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN categories ON categories.id = mods.category
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.mod AS `mod`
|
||||||
|
FROM reviews
|
||||||
|
JOIN users ON users.id = reviews.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS reviews ON reviews.mod = mods.id
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE categories.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
GROUP BY mods.id
|
||||||
|
ORDER BY IFNULL(rating, 0) DESC,
|
||||||
|
reviews DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_MODS * ($pageNumber - 1);
|
||||||
|
$pageMods = PAGE_MODS;
|
||||||
|
$statement->bind_param('iii', $categoryData['id'], $firstNumber, $pageMods);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modsPresent = false;
|
||||||
|
while ($mod = $result->fetch_assoc()) {
|
||||||
|
if (!$modsPresent) {
|
||||||
|
echo '<div class="mods">';
|
||||||
|
}
|
||||||
|
$modsPresent = true;
|
||||||
|
echo '<div class="mods-mod-container">
|
||||||
|
<div class="mods-mod-card">
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '"><img src="' . htmlspecialchars(APP_ROOT . 'img/' . (isset($mod['image_ext']) && $mod['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $mod['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $mod['image_ext'])) : 'mod-missing.png')) . '" alt="' . htmlspecialchars($mod['name']) . ' cover image"></a>
|
||||||
|
<div class="mods-mod-card-contents">
|
||||||
|
<h2><a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '">' . htmlspecialchars($mod['name']) . '</a></h2>
|
||||||
|
<p>' . (isset($mod['description']) && $mod['description'] ? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars(shortenDescription($mod['description']))) : "<i>No description</i>") . '</p>
|
||||||
|
<div class="mods-mod-card-bottom">
|
||||||
|
<p class="mods-mod-card-publisher">Publisher: <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($mod['user'])) . '">' . htmlspecialchars($mod['user']) . '</a>' . (isset($_SESSION['user']) && $_SESSION['user'] == $mod['user_id'] ? ' | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($mod['slug'])) . '">Edit mod</a>' : '') . '</p>
|
||||||
|
' . ($mod['rating'] ? '<span class="badge">' . htmlspecialchars(number_format($mod['rating'], 2)) . ' ★</span>' : '') . '
|
||||||
|
<span class="badge">' . ($mod['is_paid'] ? 'Paid' : 'Gratis') . '</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
if ($modsPresent) {
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<p>No mods.</p>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'category/' . urlencode($categoryData['slug']) . '?page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'category/' . urlencode($categoryData['slug']) . '?page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'category/' . urlencode($categoryData['slug']) . '?page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
223
includes/page_changeuserdata.php
Normal file
223
includes/page_changeuserdata.php
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$emailChanged = false;
|
||||||
|
$passwordChanged = false;
|
||||||
|
$emailRequestID = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['action'])) {
|
||||||
|
$errorMessage = "No action specified.";
|
||||||
|
} elseif ($_POST['action'] == "changepassword") {
|
||||||
|
if (!isset($_POST['oldpassword'], $_POST['password'], $_POST['password2']) || !$_POST['oldpassword'] || !$_POST['password'] || !$_POST['password2']) {
|
||||||
|
$errorMessage = "You need to input passwords.";
|
||||||
|
} elseif (!password_verify($_POST['oldpassword'], $userData['password'])) {
|
||||||
|
$errorMessage = "The current password is wrong.";
|
||||||
|
} elseif ($_POST['password'] != $_POST['password2']) {
|
||||||
|
$errorMessage = "Passwords don't match.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$statement->bind_param('si', $hashedPassword, $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$passwordChanged = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($_POST['action'] == "changeemail") {
|
||||||
|
if (!isset($_POST['password'], $_POST['email']) || !$_POST['password'] || !$_POST['email']) {
|
||||||
|
$errorMessage = "You need to input fields.";
|
||||||
|
} elseif (!password_verify($_POST['password'], $userData['password'])) {
|
||||||
|
$errorMessage = "The password is wrong.";
|
||||||
|
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errorMessage = "Invalid email address.";
|
||||||
|
} elseif ($_POST['email'] == $userData['email']) {
|
||||||
|
$errorMessage = "New email address is the same as the old one.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare('SELECT email FROM users WHERE email = ?;');
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_POST['email']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$emailExists = boolval($result->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if ($emailExists) {
|
||||||
|
$errorMessage = "Someone else already uses the email address.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
|
||||||
|
$emailRequestIDError = false;
|
||||||
|
|
||||||
|
while (!$emailRequestID) {
|
||||||
|
$tempEmailRequestID = "";
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$tempEmailRequestID = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$tempEmailRequestID = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$tempEmailRequestID = $tempEmailRequestID . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement = $connection->prepare("SELECT id FROM requests_email WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$emailRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $tempEmailRequestID);
|
||||||
|
$statement->execute();
|
||||||
|
$emailRequestIDExistsResult = $statement->get_result();
|
||||||
|
if (!$emailRequestIDExistsResult) {
|
||||||
|
$emailRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
$statement->close();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$emailRequestIDExists = boolval($emailRequestIDExistsResult->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$emailRequestIDExists) {
|
||||||
|
$emailRequestID = $tempEmailRequestID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$emailRequestIDError) {
|
||||||
|
$statement = $connection->prepare("REPLACE INTO requests_email (
|
||||||
|
id,
|
||||||
|
email,
|
||||||
|
user,
|
||||||
|
request_date
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
NOW()
|
||||||
|
)");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('ssi', $emailRequestID, $_POST['email'], $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$sent = sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $userData['username'],
|
||||||
|
"address" => $_POST['email']
|
||||||
|
]],
|
||||||
|
'Email address change request',
|
||||||
|
"You have requested the change of your email address on SVR.JS Mods directory. Copy and paste the link below to change the email address. The link will expire after one day.\n\n" . str_replace(["\r\n", "\n", "\r"], "", (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'confirm-email?id=' . urlencode($emailRequestID))
|
||||||
|
);
|
||||||
|
if (!$sent) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$emailChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$errorMessage = "Unknown action specified.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($emailChanged) {
|
||||||
|
$pageTitle = "Email address change request sent";
|
||||||
|
$pageDescription = "Check your inbox for the request.";
|
||||||
|
} elseif ($passwordChanged) {
|
||||||
|
$pageTitle = "Password changed";
|
||||||
|
$pageDescription = "Your password has been changed.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Change user data";
|
||||||
|
$pageDescription = "Change your user data in SVR.JS Mods directory.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($emailChanged) { ?>
|
||||||
|
<h1>Email address change request sent</h1>
|
||||||
|
<p>Check your inbox for the request.</p>
|
||||||
|
<?php } elseif ($passwordChanged) { ?>
|
||||||
|
<h1>Password changed</h1>
|
||||||
|
<p>Your password has been changed.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . $userData['username']); ?>" class="btn">View your profile</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Change user data</h1>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<h2>Change password</h2>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'change-user-data') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="oldpassword">Current password:</label>
|
||||||
|
<input type="password" name="oldpassword" id="oldpassword" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password">New password:</label>
|
||||||
|
<input type="password" name="password" id="password" required>
|
||||||
|
<p>Password strength: <span id="password-strength"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password2">Confirm password:</label>
|
||||||
|
<input type="password" name="password2" id="password2" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Change password">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
<input type="hidden" name="action" value="changepassword">
|
||||||
|
</form>
|
||||||
|
<h2>Change email address</h2>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'change-user-data') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password3">Password:</label>
|
||||||
|
<input type="password" name="password" id="password3" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="email">New email address:</label>
|
||||||
|
<input type="email" name="email" id="email" maxlength="255" placeholder="<?php echo htmlspecialchars($userData['email']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Change email address">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
<input type="hidden" name="action" value="changeemail">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
$passwordStrengthMeter = !$emailChanged && !$passwordChanged;
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($passwordChanged) {
|
||||||
|
sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $userData['username'],
|
||||||
|
"address" => $userData['email']
|
||||||
|
]],
|
||||||
|
'Your password has been changed.',
|
||||||
|
"Your password has been changed. If you did it, you are safe - you can ignore the message. If not, contact the administrator of SVR.JS Mods directory immediately, as your account might be compromised."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
83
includes/page_confirmemail.php
Normal file
83
includes/page_confirmemail.php
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !$_GET['id']) {
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "Email address change request ID is not specified.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT email, (NOW() > request_date + INTERVAL 1 DAY) AS expired FROM requests_email WHERE id = ? AND user = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('si', $_GET['id'], $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$request = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
if (!$request) {
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "Invalid request ID.";
|
||||||
|
} else {
|
||||||
|
$expired = false;
|
||||||
|
if ($request['expired']) {
|
||||||
|
$expired = true;
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "Invalid request ID.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$expired) {
|
||||||
|
$statement = $connection->prepare("UPDATE users SET email = ? WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('si', $request['email'], $_SESSION['user']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statement = $connection->prepare("DELETE FROM requests_email WHERE id = ? AND user = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('si', $_GET['id'], $_SESSION['user']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errorMessage) {
|
||||||
|
$pageTitle = "Your email address hasn't been changed";
|
||||||
|
$pageDescription = $errorMessage;
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Your email address has been changed";
|
||||||
|
$pageDescription = "Your email address has been changed.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($errorMessage ? "Your email address hasn't been changed" : "Your email address has been changed") ?></h1>
|
||||||
|
<p><?php echo htmlspecialchars($errorMessage ? $errorMessage : "Your email address has been changed.") ?></p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
147
includes/page_confirmpassword.php
Normal file
147
includes/page_confirmpassword.php
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$passwordChanged = false;
|
||||||
|
$userData = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['password'], $_POST['password2']) || !$_POST['password'] || !$_POST['password2']) {
|
||||||
|
$errorMessage = "You need to input passwords.";
|
||||||
|
} elseif ($_POST['password'] != $_POST['password2']) {
|
||||||
|
$errorMessage = "Passwords don't match.";
|
||||||
|
} elseif (!isset($_POST['id']) || !$_POST['id']) {
|
||||||
|
$errorMessage = "Password change request ID is not specified.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT user, (NOW() > request_date + INTERVAL 1 DAY) AS expired FROM requests_password WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $_POST['id']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$request = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
if (!$request) {
|
||||||
|
$errorMessage = "Invalid request ID.";
|
||||||
|
} else {
|
||||||
|
$expired = false;
|
||||||
|
if ($request['expired']) {
|
||||||
|
$expired = true;
|
||||||
|
$errorMessage = "Invalid request ID.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$expired) {
|
||||||
|
|
||||||
|
$statement = $connection->prepare("SELECT id, username, email, is_suspended, is_verified FROM users WHERE id = ? AND is_deleted = 0");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $request['user']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
if (!$userData) {
|
||||||
|
$errorMessage = "Your account doesn't exist.";
|
||||||
|
} elseif ($userData['is_suspended']) {
|
||||||
|
$errorMessage = "Your account is suspended.";
|
||||||
|
} elseif (!$userData['is_verified']) {
|
||||||
|
$errorMessage = "Your account is not activated yet.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$statement = $connection->prepare("UPDATE users SET password = ? WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$statement->bind_param('si', $hashedPassword, $request['user']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$passwordChanged = true;
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['user'] = $request['user'];
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statement = $connection->prepare("DELETE FROM requests_password WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_POST['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the password.";
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($passwordChanged) {
|
||||||
|
$pageTitle = "Password changed";
|
||||||
|
$pageDescription = "Your password has been changed.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Change password";
|
||||||
|
$pageDescription = "Change your password in SVR.JS Mods directory.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($passwordChanged) { ?>
|
||||||
|
<h1>Password changed</h1>
|
||||||
|
<p>Your password has been changed.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Change password</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'confirm-password') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password">New password:</label>
|
||||||
|
<input type="password" name="password" id="password" required>
|
||||||
|
<p>Password strength: <span id="password-strength"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password2">Confirm password:</label>
|
||||||
|
<input type="password" name="password2" id="password2" required>
|
||||||
|
</div>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Change password">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
<input type="hidden" name="id" value="<?php echo htmlspecialchars(isset($_POST['id']) && $_POST['id'] ? $_POST['id'] : (isset($_GET['id']) && $_GET['id'] ? $_GET['id'] : '')); ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
$passwordStrengthMeter = !$passwordChanged;
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($passwordChanged && $userData) {
|
||||||
|
sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $userData['username'],
|
||||||
|
"address" => $userData['email']
|
||||||
|
]],
|
||||||
|
'Your password has been changed.',
|
||||||
|
"Your password has been changed. If you did it, you are safe - you can ignore the message. If not, contact the administrator of SVR.JS Mods directory immediately, as your account might be compromised."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
86
includes/page_confirmregistration.php
Normal file
86
includes/page_confirmregistration.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) || !$_GET['id']) {
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "Email address change request ID is not specified.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT users.id AS id, users.is_verified AS is_verified FROM requests_register JOIN users ON users.id = requests_register.user WHERE requests_register.id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_GET['id']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "Invalid request ID.";
|
||||||
|
} else {
|
||||||
|
$verified = false;
|
||||||
|
if ($userData['is_verified']) {
|
||||||
|
$verified = true;
|
||||||
|
http_response_code(400);
|
||||||
|
$errorMessage = "The account is already verified.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$verified) {
|
||||||
|
$statement = $connection->prepare("UPDATE users SET is_verified = 1 WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
} else {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['user'] = $userData['id'];
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statement = $connection->prepare("DELETE FROM requests_register WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_GET['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
http_response_code(500);
|
||||||
|
$errorMessage = "An unexpected error occurred while verifiying the account.";
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errorMessage) {
|
||||||
|
$pageTitle = "Your account hasn't been verified";
|
||||||
|
$pageDescription = $errorMessage;
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Your account has been verified";
|
||||||
|
$pageDescription = "Your account has been verified.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($errorMessage ? "Your account hasn't been verified" : "Your account has been verified") ?></h1>
|
||||||
|
<p><?php echo htmlspecialchars($errorMessage ? $errorMessage : "Your account has been verified.") ?></p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
137
includes/page_deleteaccount.php
Normal file
137
includes/page_deleteaccount.php
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$modDiscarded = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['oldpassword']) || !$_POST['oldpassword']) {
|
||||||
|
$errorMessage = "You need to input password.";
|
||||||
|
} elseif (!password_verify($_POST['oldpassword'], $userData['password'])) {
|
||||||
|
$errorMessage = "The password is wrong.";
|
||||||
|
} elseif ($userData['is_moderator']) {
|
||||||
|
$errorMessage = "Can't delete the moderator account.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT slug, image_ext FROM mods_pending WHERE user = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $userData['id']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
$modPendingUploadDirectory = APP_FSROOT . '/img/mods_pending';
|
||||||
|
$coverDeletionError = false;
|
||||||
|
while ($modDataToDiscard = $result->fetch_assoc()) {
|
||||||
|
$pendingCoverImagePathname = isset($modDataToDiscard['image_ext']) && $modDataToDiscard['image_ext'] ? $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToDiscard['slug']) . '.' . str_replace(['/', '\\'], '', $modDataToDiscard['image_ext']) : null;
|
||||||
|
|
||||||
|
if ($pendingCoverImagePathname && file_exists($pendingCoverImagePathname) && !unlink($pendingCoverImagePathname)) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
$coverDeletionError = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if (!$coverDeletionError) {
|
||||||
|
$statement = $connection->prepare("DELETE FROM mods_pending WHERE user = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$statement->close();
|
||||||
|
$statement = $connection->prepare("DELETE FROM reviews WHERE user = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$statement->close();
|
||||||
|
$statement = $connection->prepare("UPDATE users SET email = '', password = '', bio = NULL, is_deleted = 1 WHERE id = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the account.";
|
||||||
|
} else {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
$accountDeleted = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$accountDeleted) {
|
||||||
|
$pageTitle = "Account deleted";
|
||||||
|
$pageDescription = "Your account has been deleted.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Delete account";
|
||||||
|
$pageDescription = "Are you sure to delete your account from SVR.JS Mods directory?";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($accountDeleted) { ?>
|
||||||
|
<h1>Account deleted</h1>
|
||||||
|
<p>Your account has been deleted.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Delete account</h1>
|
||||||
|
<p>Are you sure to delete your account from SVR.JS Mods directory?</p>
|
||||||
|
<ul>
|
||||||
|
<li>All your pending mods will be discarded.</li>
|
||||||
|
<li>All your mods will be removed.</li>
|
||||||
|
<li>All your reviews will be removed.</li>
|
||||||
|
<li>You will not be able to register under the username of your deleted account.</li>
|
||||||
|
</ul>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'delete-account') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="oldpassword">Current password:</label>
|
||||||
|
<input type="password" name="oldpassword" id="oldpassword" required>
|
||||||
|
</div>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Delete account">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($accountDeleted) {
|
||||||
|
sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $userData['username'],
|
||||||
|
"address" => $userData['email']
|
||||||
|
]],
|
||||||
|
'Your account has been deleted.',
|
||||||
|
"Your account has been deleted. If you did it, you are safe - you can ignore the message. If not, contact the administrator of SVR.JS Mods directory immediately, as your account might be compromised."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
75
includes/page_discardmod.php
Normal file
75
includes/page_discardmod.php
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$modDiscarded = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$modPendingUploadDirectory = APP_FSROOT . '/img/mods_pending';
|
||||||
|
$pendingCoverImagePathname = isset($modDataToDiscard['image_ext']) && $modDataToDiscard['image_ext'] ? $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToDiscard['slug']) . '.' . str_replace(['/', '\\'], '', $modDataToDiscard['image_ext']) : null;
|
||||||
|
|
||||||
|
if ($pendingCoverImagePathname && file_exists($pendingCoverImagePathname) && !unlink($pendingCoverImagePathname)) {
|
||||||
|
$errorMessage = "An unexpected error occurred while discarding the mod.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("DELETE FROM mods_pending WHERE slug = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while discarding the mod.";
|
||||||
|
} else {
|
||||||
|
$modSlug = $modDataToDiscard['slug'];
|
||||||
|
$statement->bind_param('s', $modSlug);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while discarding the mod.";
|
||||||
|
} else {
|
||||||
|
$modDiscarded = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$modDiscarded) {
|
||||||
|
$pageTitle = "Discard pending mod";
|
||||||
|
$pageDescription = "Discard a pending SVR.JS mod in SVR.JS Mods directory.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Mod discarded";
|
||||||
|
$pageDescription = "The pending mod has been discarded.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($modDiscarded) { ?>
|
||||||
|
<h1>Mod discarded</h1>
|
||||||
|
<p>The pending mod has been discarded.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods'); ?>" class="btn">View pending mods</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Discard mod</h1>
|
||||||
|
<p>This action will discard the pending <strong>“<?php echo htmlspecialchars($modDataToDiscard['name']); ?>”</strong> mod.</p>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'discard-mod/' . urlencode($modDataToDiscard['slug'])) ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Discard mod">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($modDiscarded) {
|
||||||
|
$moderatorResult = $connection->query("SELECT email AS address, username AS name FROM users WHERE is_moderator = 1;");
|
||||||
|
if ($moderatorResult) {
|
||||||
|
$moderators = [];
|
||||||
|
while ($moderator = $moderatorResult->fetch_assoc()) {
|
||||||
|
array_push($moderators, $moderator);
|
||||||
|
}
|
||||||
|
sendEmail($moderators, 'A pending mod has been discarded', "A pending mod has been discarded:\n\nMod name: " . str_replace(["\r\n", "\r", "\n"], '', $modDataToDiscard['name']) . "\nSlug: " . $modDataToDiscard['slug'] . "\n\nNo action is required.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
14
includes/page_discardmodnotallowed.php
Normal file
14
includes/page_discardmodnotallowed.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "Mod discarding not allowed";
|
||||||
|
$pageDescription = "Mod discarding not allowed";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Mod discarding not allowed</h1>
|
||||||
|
<p>You are not allowed to discard this mod.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
238
includes/page_editmod.php
Normal file
238
includes/page_editmod.php
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$modEdited = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['name'], $_POST['category'], $_POST['link']) || !$_POST['name'] || !$_POST['category'] || !$_POST['link']) {
|
||||||
|
$errorMessage = "You need to input name, category and download page URL.";
|
||||||
|
} elseif (!filter_var($_POST['link'], FILTER_VALIDATE_URL) || !preg_match('/^https?:\\/\\//', $_POST['link'])) {
|
||||||
|
$errorMessage = "Invalid download page URL.";
|
||||||
|
} elseif (isset($_POST['docslink']) && $_POST['docslink'] && (!filter_var($_POST['docslink'], FILTER_VALIDATE_URL) || !preg_match('/^https?:\\/\\//', $_POST['docslink']))) {
|
||||||
|
$errorMessage = "Invalid documentation URL.";
|
||||||
|
} elseif (!filter_var($_POST['category'], FILTER_VALIDATE_INT)) {
|
||||||
|
$errorMessage = "Invalid category.";
|
||||||
|
} else {
|
||||||
|
$categoryID = intval($_POST['category']);
|
||||||
|
$statement = $connection->prepare("SELECT id FROM categories WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the mod.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $categoryID);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the mod.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$isCategoryPresent = boolval($result->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$isCategoryPresent) {
|
||||||
|
$errorMessage = "The selected category doesn't exist.";
|
||||||
|
} else {
|
||||||
|
$fileError = false;
|
||||||
|
$fileExtension = null;
|
||||||
|
$modPendingUploadDirectory = APP_FSROOT . '/img/mods_pending';
|
||||||
|
$modUploadDirectory = APP_FSROOT . '/img/mods';
|
||||||
|
if (isset($_FILES['cover']) && $_FILES['cover']['error'] != UPLOAD_ERR_NO_FILE) {
|
||||||
|
if ($_FILES['cover']['error'] != UPLOAD_ERR_OK) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
} else {
|
||||||
|
$fileTmpPath = $_FILES['cover']['tmp_name'];
|
||||||
|
$fileName = $_FILES['cover']['name'];
|
||||||
|
$fileSize = $_FILES['cover']['size'];
|
||||||
|
$fileType = $_FILES['cover']['type'];
|
||||||
|
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
|
if ($fileSize > IMAGE_MAX_SIZE) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "The cover image is too large. Maximum cover image size: " . formatFileSize(IMAGE_MAX_SIZE);
|
||||||
|
} elseif (!in_array($fileExtension, IMAGE_EXTENSIONS_ALLOWED, true)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "Invalid cover image extension.";
|
||||||
|
} else {
|
||||||
|
$imageType = exif_imagetype($fileTmpPath);
|
||||||
|
if (!$imageType || ($fileType && image_type_to_mime_type($imageType) != $fileType)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "The cover image is either corrupted or of wrong type.";
|
||||||
|
} else {
|
||||||
|
if (!file_exists($modPendingUploadDirectory) && !mkdir($modPendingUploadDirectory, 0777, true)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
}
|
||||||
|
if (!$fileError) {
|
||||||
|
$uploadedCoverImagePathname = $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToEdit['slug']) . '.' . $fileExtension;
|
||||||
|
if (!move_uploaded_file($fileTmpPath, $uploadedCoverImagePathname)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$fileExtension = str_replace(['/', '\\'], '', $modDataToEdit['image_ext']);
|
||||||
|
$liveCoverImagePathname = $modUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToEdit['slug']) . '.' . $fileExtension;
|
||||||
|
if (file_exists($liveCoverImagePathname)) {
|
||||||
|
if (!file_exists($modPendingUploadDirectory) && !mkdir($modPendingUploadDirectory, 0777, true)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while copying the cover image.";
|
||||||
|
}
|
||||||
|
if (!$fileError) {
|
||||||
|
$pendingCoverImagePathname = $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToEdit['slug']) . '.' . $fileExtension;
|
||||||
|
if (!copy($liveCoverImagePathname, $pendingCoverImagePathname)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while copying the cover image.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$fileError) {
|
||||||
|
$statement = $connection->prepare("REPLACE INTO mods_pending (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
category,
|
||||||
|
link,
|
||||||
|
docs_link,
|
||||||
|
user,
|
||||||
|
image_ext,
|
||||||
|
is_paid,
|
||||||
|
is_rejected
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
0
|
||||||
|
);");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the mod.";
|
||||||
|
} else {
|
||||||
|
$modID = $modDataToEdit['pending'] ? $modDataToEdit['id'] : null;
|
||||||
|
$modName = $_POST['name'];
|
||||||
|
$modSlug = $modDataToEdit['slug'];
|
||||||
|
$modDescription = isset($_POST['description']) && $_POST['description'] ? $_POST['description'] : null;
|
||||||
|
$modCategory = $categoryID;
|
||||||
|
$modLink = $_POST['link'];
|
||||||
|
$modDocsLink = isset($_POST['docslink']) && $_POST['docslink'] ? $_POST['docslink'] : null;
|
||||||
|
$modUser = $_SESSION['user'];
|
||||||
|
$modImageExt = $fileExtension;
|
||||||
|
$modIsPaid = isset($_POST['paid']) ? 1 : 0;
|
||||||
|
$statement->bind_param('isssissisi', $modID, $modName, $modSlug, $modDescription, $modCategory, $modLink, $modDocsLink, $modUser, $modImageExt, $modIsPaid);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the mod.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modEdited = true;
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$modEdited) {
|
||||||
|
$pageTitle = "Edit mod";
|
||||||
|
$pageDescription = "Edit the SVR.JS mod in SVR.JS Mods directory.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Mod edited";
|
||||||
|
$pageDescription = "The edited mod is now awaiting moderators' approval.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($modEdited) { ?>
|
||||||
|
<h1>Mod edited</h1>
|
||||||
|
<p>The edited mod is now awaiting moderators' approval.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods'); ?>" class="btn">View pending mods</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Edit mod</h1>
|
||||||
|
<p><?php echo $modDataToEdit['pending'] ? 'You are editing the pending mod entry.' : 'You are editing the mod entry as it is displayed in the mod page.'; ?></p>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($modDataToEdit['slug'])) ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="name">Mod name:</label>
|
||||||
|
<input type="text" id="name" name="name" required maxlength="255" value="<?php echo htmlspecialchars($modDataToEdit['name']); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="cover">Cover image:</label>
|
||||||
|
<input type="file" id="cover" name="cover" accept="<?php echo htmlspecialchars(implode(',', array_map(
|
||||||
|
function ($extension) {
|
||||||
|
return '.' . $extension;
|
||||||
|
},
|
||||||
|
IMAGE_EXTENSIONS_ALLOWED
|
||||||
|
))) ?>">
|
||||||
|
<p>Allowed file extensions for the cover image: <strong><?php echo htmlspecialchars(implode(', ', array_map(
|
||||||
|
function ($extension) {
|
||||||
|
return '.' . $extension;
|
||||||
|
},
|
||||||
|
IMAGE_EXTENSIONS_ALLOWED
|
||||||
|
))) ?></strong></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="category">Category:</label>
|
||||||
|
<select id="category" name="category" required>
|
||||||
|
<?php
|
||||||
|
$result = $connection->query('SELECT id, name FROM categories');
|
||||||
|
if ($result) {
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
echo '<option value="' . htmlspecialchars(number_format($row['id'], 0)) . '"' . ($row['id'] == $modDataToEdit['category_id'] ? ' selected' : '') . '>' . htmlspecialchars($row['name']) . '</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="description">Mod description:</label>
|
||||||
|
<textarea name="description" id="description" maxlength="10000"><?php echo htmlspecialchars(isset($modDataToEdit['description']) ? $modDataToEdit['description'] : ""); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="link">Download page URL:</label>
|
||||||
|
<input type="url" id="link" name="link" required maxlength="255" value="<?php echo htmlspecialchars($modDataToEdit['link']); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="docslink">Documentation URL:</label>
|
||||||
|
<input type="url" id="docslink" name="docslink" maxlength="255" value="<?php echo htmlspecialchars(isset($modDataToEdit['docs_link']) ? $modDataToEdit['docs_link'] : ""); ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-block-checkbox">
|
||||||
|
<input type="checkbox" name="paid" id="paid" <?php echo $modDataToEdit['is_paid'] ? 'checked' : '' ?>>
|
||||||
|
<label for="paid">Paid?</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Edit mod">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($modEdited) {
|
||||||
|
$moderatorResult = $connection->query("SELECT email AS address, username AS name FROM users WHERE is_moderator = 1;");
|
||||||
|
if ($moderatorResult) {
|
||||||
|
$moderators = [];
|
||||||
|
while ($moderator = $moderatorResult->fetch_assoc()) {
|
||||||
|
array_push($moderators, $moderator);
|
||||||
|
}
|
||||||
|
sendEmail($moderators, 'A mod has been edited that requires approval', "A mod has been edited that requires approval:\n\nMod name: " . str_replace(["\r\n", "\r", "\n"], '', $_POST['name']) . "\nSlug: " . $modDataToEdit['slug'] . "\n\nPlease review the mod and approve or reject it.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
14
includes/page_editmodnotallowed.php
Normal file
14
includes/page_editmodnotallowed.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "Mod editing not allowed";
|
||||||
|
$pageDescription = "Mod editing not allowed";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Mod editing not allowed</h1>
|
||||||
|
<p>You are not allowed to edit this mod.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
61
includes/page_editprofile.php
Normal file
61
includes/page_editprofile.php
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$profileEdited = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("UPDATE users SET bio = ? WHERE id = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the profile.";
|
||||||
|
} else {
|
||||||
|
$bio = isset($_POST['bio']) && $_POST['bio'] ? $_POST['bio'] : null;
|
||||||
|
$statement->bind_param('si', $bio, $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while editing the profile.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$profileEdited = true;
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$profileEdited) {
|
||||||
|
$pageTitle = "Edit profile";
|
||||||
|
$pageDescription = "Edit your profile in SVR.JS Mods directory.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Profile edited";
|
||||||
|
$pageDescription = "Your profile has been edited.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($profileEdited) { ?>
|
||||||
|
<h1>Profile edited</h1>
|
||||||
|
<p>Your profile has been edited.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . $userData['username']); ?>" class="btn">View your profile</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Edit profile</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-profile') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<p>Username: <strong><?php echo htmlspecialchars($userData['username']) ?></strong></p>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="bio">Biography:</label>
|
||||||
|
<textarea name="bio" id="bio" maxlength="1000"><?php echo htmlspecialchars(isset($userData['bio']) ? $userData['bio'] : ""); ?></textarea>
|
||||||
|
</div>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Edit profile">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
162
includes/page_forgotpassword.php
Normal file
162
includes/page_forgotpassword.php
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$emailExists = false;
|
||||||
|
$passwordChanged = false;
|
||||||
|
$passwordRequestID = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} else {
|
||||||
|
if (!isset($_POST['email']) || !$_POST['email']) {
|
||||||
|
$errorMessage = "You need to input fields.";
|
||||||
|
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errorMessage = "Invalid email address.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare('SELECT id, username, email, is_suspended, is_verified FROM users WHERE email = ?;');
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_POST['email']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
$emailExists = boolval($userData);
|
||||||
|
if ($emailExists) {
|
||||||
|
if ($userData['is_suspended']) {
|
||||||
|
$errorMessage = "Your account is suspended.";
|
||||||
|
} elseif (!$userData['is_verified']) {
|
||||||
|
$errorMessage = "Your account is not activated yet.";
|
||||||
|
} else {
|
||||||
|
$passwordRequestIDError = false;
|
||||||
|
|
||||||
|
while (!$passwordRequestID) {
|
||||||
|
$tempPasswordRequestID = "";
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$tempPasswordRequestID = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$tempPasswordRequestID = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$tempPasswordRequestID = $tempPasswordRequestID . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement = $connection->prepare("SELECT id FROM requests_password WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$passwordRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $tempPasswordRequestID);
|
||||||
|
$statement->execute();
|
||||||
|
$passwordRequestIDExistsResult = $statement->get_result();
|
||||||
|
if (!$passwordRequestIDExistsResult) {
|
||||||
|
$passwordRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
$statement->close();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$passwordRequestIDExists = boolval($passwordRequestIDExistsResult->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$passwordRequestIDExists) {
|
||||||
|
$passwordRequestID = $tempPasswordRequestID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$passwordRequestIDError) {
|
||||||
|
$statement = $connection->prepare("REPLACE INTO requests_password (
|
||||||
|
id,
|
||||||
|
user,
|
||||||
|
request_date
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
NOW()
|
||||||
|
)");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('si', $passwordRequestID, $userData['id']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while changing the email address.";
|
||||||
|
} else {
|
||||||
|
$passwordRequestToSend = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$passwordRequestToSend = true;
|
||||||
|
}
|
||||||
|
if ($passwordRequestToSend) {
|
||||||
|
$sent = false;
|
||||||
|
if ($emailExists) {
|
||||||
|
$sent = sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $userData['username'],
|
||||||
|
"address" => $_POST['email']
|
||||||
|
]],
|
||||||
|
'Password change request',
|
||||||
|
"You have requested the change of your password on SVR.JS Mods directory. Copy and paste the link below to change the password. The link will expire after one day.\n\n" . str_replace(["\r\n", "\n", "\r"], "", (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'confirm-password?id=' . urlencode($passwordRequestID))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$sent = sendEmail(
|
||||||
|
[[
|
||||||
|
"address" => $_POST['email']
|
||||||
|
]],
|
||||||
|
'Email address not associated with an account - password change request failed',
|
||||||
|
"Someone attempted to change the password of an account which is not associated with your email address. No action is required."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!$sent) {
|
||||||
|
$errorMessage = "Can't send password change request email message.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($passwordRequestToSend) {
|
||||||
|
$pageTitle = "Password change request sent";
|
||||||
|
$pageDescription = "Check your inbox for the request.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Forgot password?";
|
||||||
|
$pageDescription = "Change your user data in SVR.JS Mods directory.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($passwordRequestToSend) { ?>
|
||||||
|
<h1>Password change request sent</h1>
|
||||||
|
<p>Check your inbox for the request.</p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Forgot password?</h1>
|
||||||
|
<p>If you forgot your password, you can change it - just input your email address associated with your account.</p>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'forgot-password') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="email">Email address:</label>
|
||||||
|
<input type="email" name="email" id="email" maxlength="255" placeholder="<?php echo htmlspecialchars($userData['email']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Send password change request">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
53
includes/page_index.php
Normal file
53
includes/page_index.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<section class="hero">
|
||||||
|
<div class="wrapper">
|
||||||
|
<h1>Expand SVR.JS functionality with mods</h1>
|
||||||
|
<p>SVR.JS Mods directory allows you to find SVR.JS mods to expand the functionality of your SVR.JS web server, and enhance your SVR.JS experience.</p>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search') ?>" method="get" class="search-form">
|
||||||
|
<input type="text" name="q" placeholder="Search for SVR.JS mods...">
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<main class="content">
|
||||||
|
<h2 class="category-headline">Categories</h2>
|
||||||
|
<?php
|
||||||
|
$result = $connection->query("SELECT
|
||||||
|
categories.id AS id,
|
||||||
|
categories.name AS name,
|
||||||
|
categories.slug AS slug,
|
||||||
|
(
|
||||||
|
SELECT COUNT(mods.id)
|
||||||
|
FROM mods
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE mods.category = categories.id
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
LIMIT 1
|
||||||
|
) AS count
|
||||||
|
FROM categories;");
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching categories.</p>";
|
||||||
|
} else {
|
||||||
|
$categoriesPresent = false;
|
||||||
|
while ($category = $result->fetch_assoc()) {
|
||||||
|
$categoriesPresent = true;
|
||||||
|
echo '<div class="category">
|
||||||
|
<h3><a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'category/' . urlencode($category['slug'])) . '">' . htmlspecialchars($category['name']) . '</a></h3>
|
||||||
|
<p>Mods: ' . htmlspecialchars(number_format($category['count'], 0)) . '</p>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
if (!$categoriesPresent) {
|
||||||
|
echo '<p>No categories.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
98
includes/page_login.php
Normal file
98
includes/page_login.php
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$redirected = false;
|
||||||
|
$redirect = null;
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['redirect']) && strlen($_POST['redirect']) > 0 && $_POST['redirect'][0] == "/" && (strlen($_POST['redirect']) == 1 || $_POST['redirect'][1] != "/")) {
|
||||||
|
$redirect = $_POST['redirect'];
|
||||||
|
} elseif (isset($_GET['redirect']) && strlen($_GET['redirect']) > 0 && $_GET['redirect'][0] == "/" && (strlen($_GET['redirect']) == 1 || $_GET['redirect'][1] != "/")) {
|
||||||
|
$redirect = $_GET['redirect'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
header('Location: ' . ($redirect ? $redirect : (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/')));
|
||||||
|
http_response_code(302);
|
||||||
|
$redirected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['username'], $_POST['password']) || !$_POST['username'] || !$_POST['password']) {
|
||||||
|
$errorMessage = "You need to input username/password.";
|
||||||
|
} else {
|
||||||
|
$passwordHash = '';
|
||||||
|
$suspended = false;
|
||||||
|
$verified = false;
|
||||||
|
$userID = 0;
|
||||||
|
$statement = $connection->prepare('SELECT id, password, is_verified, is_suspended FROM users WHERE LOWER(username) = LOWER(?)');
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while logging in.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $_POST['username']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while logging in.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$row = $result->fetch_assoc();
|
||||||
|
if ($row) {
|
||||||
|
$passwordHash = $row['password'];
|
||||||
|
$suspended = boolval($row['is_suspended']);
|
||||||
|
$verified = boolval($row['is_verified']);
|
||||||
|
$userID = $row['id'];
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if (!password_verify($_POST['password'], $passwordHash)) {
|
||||||
|
$errorMessage = "Invalid username/password.";
|
||||||
|
} elseif ($suspended) {
|
||||||
|
$errorMessage = "Your account is suspended.";
|
||||||
|
} elseif (!$verified) {
|
||||||
|
$errorMessage = "Your account is not activated yet.";
|
||||||
|
} else {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['user'] = $userID;
|
||||||
|
header('Location: ' . ($redirect ? $redirect : APP_ROOT));
|
||||||
|
http_response_code(302);
|
||||||
|
$redirected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$redirected) {
|
||||||
|
|
||||||
|
$pageTitle = "Log in";
|
||||||
|
$pageDescription = "Log into SVR.JS Mods directory";
|
||||||
|
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Log in</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login') ?>" method="post" class="form">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<p>You don't have an account? <a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'register') ?>">Register</a></p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'forgot-password') ?>">Forgot password?</a></p>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Log in">
|
||||||
|
</div>
|
||||||
|
<?php if ($redirect) echo '<input type="hidden" name="redirect" value="' . htmlspecialchars($redirect) . '">'; ?>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
}
|
||||||
|
?>
|
18
includes/page_logout.php
Normal file
18
includes/page_logout.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$redirected = false;
|
||||||
|
$redirect = null;
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['redirect']) && strlen($_POST['redirect']) > 0 && $_POST['redirect'][0] == "/" && (strlen($_POST['redirect']) == 1 || $_POST['redirect'][1] != "/")) {
|
||||||
|
$redirect = $_POST['redirect'];
|
||||||
|
} elseif (isset($_GET['redirect']) && strlen($_GET['redirect']) > 0 && $_GET['redirect'][0] == "/" && (strlen($_GET['redirect']) == 1 || $_GET['redirect'][1] != "/")) {
|
||||||
|
$redirect = $_GET['redirect'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SESSION['user']) && $_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['_csrf']) && $_POST['_csrf'] == $_SESSION['csrf']) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
session_regenerate_id(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: ' . ($redirect ? $redirect : APP_ROOT));
|
||||||
|
http_response_code(302);
|
59
includes/page_mod.php
Normal file
59
includes/page_mod.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $modData['name'];
|
||||||
|
$pageDescription = $modData['description'] ? shortenDescription(str_replace(["\r\n", "\n", "\r"], " ", $modData['description'])) : 'Download and install the "' . $modData['name'] . '" mod.';
|
||||||
|
$pageImage = (isset($modData['image_ext']) && $modData['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $modData['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $modData['image_ext'])) : 'mod-missing.png');
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<div class="mod-header">
|
||||||
|
<h1><?php echo htmlspecialchars($modData['name']) ?></h1>
|
||||||
|
<p class="mod-badges">
|
||||||
|
<span class="badge"><?php echo $modData['is_paid'] ? 'Paid' : 'Gratis'; ?></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<img class="mod-cover" src="<?php echo htmlspecialchars(APP_ROOT . 'img/' . (isset($modData['image_ext']) && $modData['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $modData['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $modData['image_ext'])) : 'mod-missing.png')) ?>" alt="<?php echo htmlspecialchars($modData['name']); ?> cover image">
|
||||||
|
<p class="mod-links button-container">
|
||||||
|
<a href="<?php echo htmlspecialchars($modData['link']); ?>" class="btn">Download the mod</a>
|
||||||
|
<?php if ($modData['docs_link']) echo '<a href="' . htmlspecialchars($modData['docs_link']) . '" class="btn btn-secondary">Read the documentation</a>'; ?>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Publisher:</strong> <a href="<?php
|
||||||
|
echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($modData['user']));
|
||||||
|
?>"><?php echo htmlspecialchars($modData['user']) ?></a> | <strong>Category:</strong> <?php echo isset($modData['category']) && $modData['category']
|
||||||
|
? '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'category/' . urlencode($modData['category_slug'])) . '">' . htmlspecialchars($modData['category']) . '</a>'
|
||||||
|
: 'Invalid category';
|
||||||
|
?><?php if ($_SESSION['user'] == $modData['user_id']) echo ' | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($modData['slug'])) . '">Edit mod</a> | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'remove-mod/' . urlencode($modData['slug'])) . '">Remove mod</a>';
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<?php
|
||||||
|
if ($modData['rating']) {
|
||||||
|
$stars = round($modData['rating']);
|
||||||
|
|
||||||
|
echo htmlspecialchars(number_format($modData['rating'], 2));
|
||||||
|
echo ' ';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $stars; $i++) {
|
||||||
|
echo '<span class="rating-star rating-star-higlighted">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = $stars; $i < 5; $i++) {
|
||||||
|
echo '<span class="rating-star">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ' | ';
|
||||||
|
}
|
||||||
|
?><strong>Reviews</strong>: <?php echo htmlspecialchars(number_format($modData['reviews'], 0)); ?> | <a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug'])) ?>">View reviews</a>
|
||||||
|
</p>
|
||||||
|
<h2>Description</h2>
|
||||||
|
<p>
|
||||||
|
<?php echo isset($modData['description']) && $modData['description']
|
||||||
|
? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars($modData['description']))
|
||||||
|
: "<i>No description</i>";
|
||||||
|
?>
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
22
includes/page_modremoved.php
Normal file
22
includes/page_modremoved.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "Mod removed";
|
||||||
|
$pageDescription = "Mod removed";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Mod removed</h1>
|
||||||
|
<p><?php
|
||||||
|
if ($modData['is_user_suspended']) {
|
||||||
|
echo 'The mod is unavailable due to its publisher being suspended.';
|
||||||
|
} elseif ($modData['is_user_deleted']) {
|
||||||
|
echo 'The mod is unavailable due to its publisher being deleted.';
|
||||||
|
} else {
|
||||||
|
echo 'The mod has been removed either by user or by moderator.';
|
||||||
|
}
|
||||||
|
?></p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
126
includes/page_pendingmods.php
Normal file
126
includes/page_pendingmods.php
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$pageTitle = "Pending mods";
|
||||||
|
$pageDescription = 'View pending SVR.JS mods.';
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Pending mods</h1>
|
||||||
|
<?php
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(mods_pending.id) AS count
|
||||||
|
FROM mods_pending
|
||||||
|
JOIN users ON users.id = mods_pending.user
|
||||||
|
WHERE users.id = ?
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1;');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('i', $_SESSION['user']);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
mods_pending.id AS id,
|
||||||
|
mods_pending.name AS name,
|
||||||
|
mods_pending.slug AS slug,
|
||||||
|
mods_pending.description AS description,
|
||||||
|
mods_pending.image_ext AS image_ext,
|
||||||
|
mods_pending.is_paid AS is_paid,
|
||||||
|
mods_pending.is_rejected AS is_rejected,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id
|
||||||
|
FROM mods_pending
|
||||||
|
JOIN users ON users.id = mods_pending.user
|
||||||
|
WHERE users.id = ?
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
ORDER BY mods_pending.id DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_MODS * ($pageNumber - 1);
|
||||||
|
$pageMods = PAGE_MODS;
|
||||||
|
$statement->bind_param('iii', $_SESSION['user'], $firstNumber, $pageMods);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modsPresent = false;
|
||||||
|
while ($mod = $result->fetch_assoc()) {
|
||||||
|
if (!$modsPresent) {
|
||||||
|
echo '<div class="mods">';
|
||||||
|
}
|
||||||
|
$modsPresent = true;
|
||||||
|
echo '<div class="mods-mod-container">
|
||||||
|
<div class="mods-mod-card">
|
||||||
|
<img src="' . htmlspecialchars(APP_ROOT . 'img/' . (isset($mod['image_ext']) && $mod['image_ext'] ? 'mods_pending/' . urlencode(str_replace(['/', '\\'], '', $mod['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $mod['image_ext'])) : 'mod-missing.png')) . '" alt="' . htmlspecialchars($mod['name']) . ' cover image">
|
||||||
|
<div class="mods-mod-card-contents">
|
||||||
|
<h2>' . htmlspecialchars($mod['name']) . '</h2>
|
||||||
|
<p>' . (isset($mod['description']) && $mod['description'] ? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars(shortenDescription($mod['description']))) : "<i>No description</i>") . '</p>
|
||||||
|
<div class="mods-mod-card-bottom">
|
||||||
|
<p class="mods-mod-card-publisher">' . (isset($_SESSION['user']) && $_SESSION['user'] == $mod['user_id'] ? '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($mod['slug'])) . '">Edit mod</a> | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'discard-mod/' . urlencode($mod['slug'])) . '">Discard mod</a>' : '') . '</p>
|
||||||
|
<span class="badge">' . ($mod['is_rejected'] ? 'Rejected' : 'Pending') . '</span>
|
||||||
|
<span class="badge">' . ($mod['is_paid'] ? 'Paid' : 'Gratis') . '</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
if ($modsPresent) {
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<p>No mods.</p>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods?page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods?page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods?page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
221
includes/page_register.php
Normal file
221
includes/page_register.php
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$redirected = false;
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/'));
|
||||||
|
http_response_code(302);
|
||||||
|
$redirected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$registered = false;
|
||||||
|
$errorMessage = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['username'], $_POST['email'], $_POST['password'], $_POST['password2']) || !$_POST['username'] || !$_POST['email'] || !$_POST['password'] || !$_POST['password2']) {
|
||||||
|
$errorMessage = "You need to input username, email address and passwords.";
|
||||||
|
} elseif (!preg_match('/^[a-zA-Z0-9]+$/', $_POST['username'])) {
|
||||||
|
$errorMessage = "Invalid username.";
|
||||||
|
} elseif (HCAPTCHA_ENABLED && (!isset($_POST['h-captcha-response']) || !$_POST['h-captcha-response'])) {
|
||||||
|
$errorMessage = "You need to answer the CAPTCHA.";
|
||||||
|
} elseif (!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
|
||||||
|
$errorMessage = "Invalid email address.";
|
||||||
|
} elseif (HCAPTCHA_ENABLED && !checkHCaptcha($_POST['h-captcha-response'])) {
|
||||||
|
$errorMessage = "CAPTCHA verification failed";
|
||||||
|
} else {
|
||||||
|
$isNotAPossibleSpammer = true;
|
||||||
|
if (STOPFORUMSPAM_ENABLED) $isNotAPossibleSpammer = checkStopForumSpam($_POST['username'], $_POST['email']);
|
||||||
|
if (is_null($isNotAPossibleSpammer)) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else if (!$isNotAPossibleSpammer) {
|
||||||
|
$errorMessage = "Possible spammer detected.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare('SELECT id FROM users WHERE LOWER(username) = LOWER(?) AND email = ?');
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('ss', $_POST['username'], $_POST['email']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userExists = boolval($result->fetch_assoc());
|
||||||
|
if ($userExists) {
|
||||||
|
$errorMessage = "Someone already uses either your email address or your username.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("INSERT INTO users (
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
email,
|
||||||
|
bio,
|
||||||
|
is_verified,
|
||||||
|
is_moderator,
|
||||||
|
is_suspended,
|
||||||
|
is_deleted
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else {
|
||||||
|
$username = $_POST['username'];
|
||||||
|
$hashedPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$email = $_POST['email'];
|
||||||
|
$statement->bind_param('sss', $username, $hashedPassword, $email);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else {
|
||||||
|
$userID = $statement->insert_id;
|
||||||
|
$verificationRequestToSend = false;
|
||||||
|
$verificationRequestIDError = false;
|
||||||
|
|
||||||
|
while (!$verificationRequestID) {
|
||||||
|
$tempVerificationRequestID = "";
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$tempVerificationRequestID = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$tempVerificationRequestID = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$tempVerificationRequestID = $tempVerificationRequestID . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement = $connection->prepare("SELECT id FROM requests_register WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$verificationRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $tempVerificationRequestID);
|
||||||
|
$statement->execute();
|
||||||
|
$verificationRequestIDExistsResult = $statement->get_result();
|
||||||
|
if (!$verificationRequestIDExistsResult) {
|
||||||
|
$verificationRequestIDError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
$statement->close();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$verificationRequestIDExists = boolval($verificationRequestIDExistsResult->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$verificationRequestIDExists) {
|
||||||
|
$verificationRequestID = $tempVerificationRequestID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$verificationRequestIDError) {
|
||||||
|
$statement = $connection->prepare("REPLACE INTO requests_register (
|
||||||
|
id,
|
||||||
|
user
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?
|
||||||
|
);");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('si', $verificationRequestID, $userID);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while registering the account.";
|
||||||
|
} else {
|
||||||
|
$verificationRequestToSend = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($verificationRequestToSend) {
|
||||||
|
$sent = sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $_POST['username'],
|
||||||
|
"address" => $_POST['email']
|
||||||
|
]],
|
||||||
|
'Account verification request',
|
||||||
|
"You have just registered an account on SVR.JS Mods directory, and it's awaiting verification. Copy and paste the link below to verify the account.\n\n" . str_replace(["\r\n", "\n", "\r"], "", (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : 'localhost')) . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'confirm-registration?id=' . urlencode($verificationRequestID))
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($sent) {
|
||||||
|
$registered = true;
|
||||||
|
} else {
|
||||||
|
$errorMessage = "Can't send password change request email message.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$redirected) {
|
||||||
|
|
||||||
|
if ($registered) {
|
||||||
|
$pageTitle = "Account verification request sent";
|
||||||
|
$pageDescription = "Check your inbox for the request.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Register";
|
||||||
|
$pageDescription = "Register an account in SVR.JS Mods directory";
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($registered) { ?>
|
||||||
|
<h1>Account verification request sent</h1>
|
||||||
|
<p>Check your inbox for the request.</p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Register</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'register') ?>" method="post" class="form">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" id="username" name="username" maxlength="255" pattern="^[a-zA-Z0-9]+$" required>
|
||||||
|
<p>Username can consist of letters and numbers.</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="email">Email address:</label>
|
||||||
|
<input type="email" name="email" id="email" maxlength="255" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
<p>Password strength: <span id="password-strength"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="password2">Confirm password:</label>
|
||||||
|
<input type="password" id="password2" name="password2" required>
|
||||||
|
</div>
|
||||||
|
<?php if (HCAPTCHA_ENABLED) { ?>
|
||||||
|
<div class="form-block form-captcha-container">
|
||||||
|
<div class="h-captcha" data-sitekey="<?php echo htmlspecialchars(HCAPTCHA_SITE_KEY); ?>"></div>
|
||||||
|
</div>
|
||||||
|
<?php } ?>
|
||||||
|
<p>You already have an account? <a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login') ?>">Log in</a></p>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Register">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
$passwordStrengthMeter = !$registered;
|
||||||
|
$hCaptchaForm = HCAPTCHA_ENABLED && !$registered;
|
||||||
|
include 'footer.php';
|
||||||
|
}
|
||||||
|
?>
|
89
includes/page_removemod.php
Normal file
89
includes/page_removemod.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$modRemoved = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$modPendingUploadDirectory = APP_FSROOT . '/img/mods_pending';
|
||||||
|
$pendingCoverImagePathname = isset($modDataToRemove['pending_image_ext']) && $modDataToRemove['pending_image_ext'] ? $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToRemove['slug']) . '.' . $modDataToDiscard['pending_image_ext'] : null;
|
||||||
|
|
||||||
|
if ($pendingCoverImagePathname && file_exists($pendingCoverImagePathname) && !unlink($pendingCoverImagePathname)) {
|
||||||
|
$errorMessage = "An unexpected error occurred while removing the mod.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("DELETE FROM mods_pending WHERE slug = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while removing the mod.";
|
||||||
|
} else {
|
||||||
|
$modSlug = $modDataToDiscard['slug'];
|
||||||
|
$statement->bind_param('s', $modSlug);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while removing the mod.";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
|
||||||
|
$statement = $connection->prepare("UPDATE mods SET is_removed = 1 WHERE slug = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while removing the mod.";
|
||||||
|
} else {
|
||||||
|
$modSlug = $modDataToRemove['slug'];
|
||||||
|
$statement->bind_param('s', $modSlug);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while removing the mod.";
|
||||||
|
} else {
|
||||||
|
$modRemoved = true;
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$modRemoved) {
|
||||||
|
$pageTitle = "Remove mod";
|
||||||
|
$pageDescription = "Remove a pending SVR.JS mod in SVR.JS Mods directory.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Mod removed";
|
||||||
|
$pageDescription = "The mod has been removed.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($modRemoved) { ?>
|
||||||
|
<h1>Mod removed</h1>
|
||||||
|
<p>The mod has been removed.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods'); ?>" class="btn">View mods</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Remove mod</h1>
|
||||||
|
<p>Are you sure to remove the <strong>“<?php echo htmlspecialchars($modDataToRemove['name']); ?>”</strong> mod?</p>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'remove-mod/' . urlencode($modDataToRemove['slug'])) ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Remove mod">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($modRemoved) {
|
||||||
|
$moderatorResult = $connection->query("SELECT email AS address, username AS name FROM users WHERE is_moderator = 1;");
|
||||||
|
if ($moderatorResult) {
|
||||||
|
$moderators = [];
|
||||||
|
while ($moderator = $moderatorResult->fetch_assoc()) {
|
||||||
|
array_push($moderators, $moderator);
|
||||||
|
}
|
||||||
|
sendEmail($moderators, 'A mod has been removed', "A mod has been removed:\n\nMod name: " . str_replace(["\r\n", "\r", "\n"], '', $modDataToRemove['name']) . "\nSlug: " . $modDataToRemove['slug'] . "\n\nNo action is required.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
14
includes/page_removemodnotallowed.php
Normal file
14
includes/page_removemodnotallowed.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "Mod removal not allowed";
|
||||||
|
$pageDescription = "Mod removal not allowed";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Mod removal not allowed</h1>
|
||||||
|
<p>You are not allowed to remove this mod.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
319
includes/page_reviews.php
Normal file
319
includes/page_reviews.php
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$reviewSubmitted = false;
|
||||||
|
$reviewDeleted = false;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['action'])) {
|
||||||
|
$errorMessage = "No action specified.";
|
||||||
|
} elseif ($_POST['action'] == "submit") {
|
||||||
|
if (!isset($_POST['rating'], $_POST['review']) || !$_POST['rating'] || !$_POST['review']) {
|
||||||
|
$errorMessage = "You need to input rating and review.";
|
||||||
|
} elseif (!filter_var($_POST['rating'], FILTER_VALIDATE_INT)) {
|
||||||
|
$errorMessage = "Invalid rating.";
|
||||||
|
} else {
|
||||||
|
$rating = intval($_POST['rating']);
|
||||||
|
if ($rating < 1 || $rating > 5) {
|
||||||
|
$errorMessage = "Invalid rating.";
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("REPLACE INTO reviews (
|
||||||
|
`mod`,
|
||||||
|
user,
|
||||||
|
rating,
|
||||||
|
review
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?
|
||||||
|
);");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the review.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('iiis', $modData['id'], $_SESSION['user'], $rating, $_POST['review']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the review.";
|
||||||
|
} else {
|
||||||
|
$reviewSubmitted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($_POST['action'] == "delete") {
|
||||||
|
$statement = $connection->prepare("DELETE FROM reviews WHERE `mod` = ? AND user = ?");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the review.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('ii', $modData['id'], $_SESSION['user']);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while deleting the review.";
|
||||||
|
} else {
|
||||||
|
$reviewDeleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$errorMessage = "Unknown action specified.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reviewSubmitted) {
|
||||||
|
$pageTitle = "Review submitted";
|
||||||
|
$pageDescription = "Your review has been submitted.";
|
||||||
|
} elseif ($reviewDeleted) {
|
||||||
|
$pageTitle = "Review deleted";
|
||||||
|
$pageDescription = "Your review has been deleted.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = 'Reviews for "' . $modData['name'] . '" mod';
|
||||||
|
$pageDescription = 'See reviews for the "' . $modData['name'] . '" mod on SVR.JS Mods directory.';
|
||||||
|
$pageImage = (isset($modData['image_ext']) && $modData['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $modData['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $modData['image_ext'])) : 'mod-missing.png');
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($reviewSubmitted) { ?>
|
||||||
|
<h1>Review submitted</h1>
|
||||||
|
<p>Your review has been submitted.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug'])); ?>" class="btn">Return to mod reviews</a></p>
|
||||||
|
<?php } elseif ($reviewDeleted) { ?>
|
||||||
|
<h1>Review deleted</h1>
|
||||||
|
<p>Your review has been deleted.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug'])); ?>" class="btn">Return to mod reviews</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Reviews for “<?php echo htmlspecialchars($modData['name']) ?>” mod</h1>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($modData['slug'])); ?>">Return to the mod page</a></p>
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<?php
|
||||||
|
if ($modData['rating']) {
|
||||||
|
$stars = round($modData['rating']);
|
||||||
|
|
||||||
|
echo '<p class="rating">';
|
||||||
|
|
||||||
|
echo '<span class="rating-rating">' . htmlspecialchars(number_format($modData['rating'], 2)) . '</span>';
|
||||||
|
|
||||||
|
echo '<span class="rating-stars">';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $stars; $i++) {
|
||||||
|
echo '<span class="rating-star rating-star-higlighted">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = $stars; $i < 5; $i++) {
|
||||||
|
echo '<span class="rating-star">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</span></p>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<p><strong>Reviews</strong>: <?php echo htmlspecialchars(number_format($modData['reviews'], 0)); ?></p>
|
||||||
|
<h2>Submit a review</h2>
|
||||||
|
<?php if (!isset($_SESSION['user'])) { ?>
|
||||||
|
<p>You need to be logged in to submit a review on this mod.</p>
|
||||||
|
<?php } elseif ($_SESSION['user'] == $modData['user_id']) { ?>
|
||||||
|
<p>As a mod publisher, you cannot submit a review on this mod.</p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<?php
|
||||||
|
$userReview = null;
|
||||||
|
$statement = $connection->prepare('SELECT rating, review FROM reviews WHERE `mod` = ? AND user = ?;');
|
||||||
|
if ($statement) {
|
||||||
|
$statement->bind_param('ii', $modData['id'], $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if ($result) {
|
||||||
|
$userReview = $result->fetch_assoc();
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug'])) ?>" method="post" class="form">
|
||||||
|
<div class="form-block-checkbox">
|
||||||
|
<span>Rating:</span>
|
||||||
|
<span class="rating-select">
|
||||||
|
<input type="radio" name="rating" id="rate-5" value="5" required <?php echo $userReview && $userReview['rating'] == 5 ? 'checked' : ''; ?>>
|
||||||
|
<label for="rate-5" title="5 stars">★</label>
|
||||||
|
<input type="radio" name="rating" id="rate-4" value="4" required <?php echo $userReview && $userReview['rating'] == 4 ? 'checked' : ''; ?>>
|
||||||
|
<label for="rate-4" title="4 stars">★</label>
|
||||||
|
<input type="radio" name="rating" id="rate-3" value="3" required <?php echo $userReview && $userReview['rating'] == 3 ? 'checked' : ''; ?>>
|
||||||
|
<label for="rate-3" title="3 stars">★</label>
|
||||||
|
<input type="radio" name="rating" id="rate-2" value="2" required <?php echo $userReview && $userReview['rating'] == 2 ? 'checked' : ''; ?>>
|
||||||
|
<label for="rate-2" title="2 stars">★</label>
|
||||||
|
<input type="radio" name="rating" id="rate-1" value="1" required <?php echo $userReview && $userReview['rating'] == 1 ? 'checked' : ''; ?>>
|
||||||
|
<label for="rate-1" title="1 star">★</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="review">Review:</label>
|
||||||
|
<textarea name="review" id="review" maxlength="1000" required><?php echo $userReview && $userReview['review'] ? htmlspecialchars($userReview['review']) : ''; ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="<?php echo $userReview ? 'Edit review' : 'Submit review' ?>">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
<input type="hidden" name="action" value="submit">
|
||||||
|
</form>
|
||||||
|
<?php if ($userReview) { ?>
|
||||||
|
<h2>Delete your review</h2>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug'])) ?>" method="post" class="form">
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Delete review">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
<?php } ?>
|
||||||
|
<h2>Reviews</h2>
|
||||||
|
<?php
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(reviews.id) AS count
|
||||||
|
FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id, mods.is_removed AS is_removed, mods.name AS name, mods.slug AS slug FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
JOIN users ON users.id = reviews.user
|
||||||
|
WHERE mods.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1;');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('i', $modData['id']);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.review AS review,
|
||||||
|
mods.name AS mod_name,
|
||||||
|
mods.slug AS mod_slug,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id
|
||||||
|
FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id, mods.is_removed AS is_removed, mods.name AS name, mods.slug AS slug FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
JOIN users ON users.id = reviews.user
|
||||||
|
WHERE mods.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
ORDER BY reviews.id DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_REVIEWS * ($pageNumber - 1);
|
||||||
|
$pageReviews = PAGE_REVIEWS;
|
||||||
|
$statement->bind_param('iii', $modData['id'], $firstNumber, $pageReviews);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$reviewsPresent = false;
|
||||||
|
while ($review = $result->fetch_assoc()) {
|
||||||
|
$reviewsPresent = true;
|
||||||
|
echo '<div class="review"><div class="review-header">';
|
||||||
|
|
||||||
|
$stars = round($review['rating']);
|
||||||
|
|
||||||
|
echo '<span class="rating-stars">';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $stars; $i++) {
|
||||||
|
echo '<span class="rating-star rating-star-higlighted">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = $stars; $i < 5; $i++) {
|
||||||
|
echo '<span class="rating-star">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ' | by <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($review['user'])) . '">' . htmlspecialchars($review['user']) . '</a> | on <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($review['mod_slug'])) . '">' . htmlspecialchars($review['mod_name']) . '</a></div><p>' . str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars($review['review'])) . '</div>';
|
||||||
|
}
|
||||||
|
if (!$reviewsPresent) {
|
||||||
|
echo '<p>No reviews.</p>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug']) . '?page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug']) . '?page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'reviews/' . urlencode($modData['slug']) . '?page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($reviewSubmitted) {
|
||||||
|
$username = null;
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$usernameStatement = $connection->prepare('SELECT username FROM users WHERE id = ?;');
|
||||||
|
if ($usernameStatement) {
|
||||||
|
$usernameStatement->bind_param('i', $_SESSION['user']);
|
||||||
|
$usernameStatement->execute();
|
||||||
|
$usernameResult = $usernameStatement->get_result();
|
||||||
|
if ($usernameResult) {
|
||||||
|
$usernameRow = $usernameResult->fetch_assoc();
|
||||||
|
if ($usernameRow) {
|
||||||
|
$username = $usernameRow['username'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$usernameStatement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEmail(
|
||||||
|
[[
|
||||||
|
"name" => $modData['user'],
|
||||||
|
"address" => $modData['user_email']
|
||||||
|
]],
|
||||||
|
($username ? $username : 'Someone') . ' has left a review on your mod',
|
||||||
|
($username ? $username : 'Someone') . " has left you a " . intval($_POST['rating']) . "-star review on your \"" . $modData['name'] . "\" mod. The contents of the review are below:\n\n" . $_POST['review']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
164
includes/page_search.php
Normal file
164
includes/page_search.php
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<?php
|
||||||
|
$searchQuery = isset($_GET['q']) && $_GET['q'] ? $_GET['q'] : "";
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $searchQuery ? "$searchQuery - Search" : $searchQuery;
|
||||||
|
$pageDescription = "Search for SVR.JS mods";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Search</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search') ?>" method="get" class="search-form">
|
||||||
|
<input type="text" name="q" placeholder="Search for SVR.JS mods..." <?php if ($searchQuery) echo 'value="' . htmlspecialchars($searchQuery) . '" '; ?>>
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</form>
|
||||||
|
<?php
|
||||||
|
if (trim($searchQuery)) {
|
||||||
|
$queryLike = '%' . str_replace(array('!', '%', '_', '['), array('!!', '!%', '!_', '!['), $searchQuery) . '%';
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(mods.id) AS count
|
||||||
|
FROM mods
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
AND (
|
||||||
|
MATCH (mods.name, mods.description) AGAINST (? IN NATURAL LANGUAGE MODE)
|
||||||
|
OR mods.name LIKE ?
|
||||||
|
OR mods.description LIKE ?
|
||||||
|
);');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('sss', $searchQuery, $queryLike, $queryLike);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
categories.name AS category,
|
||||||
|
categories.slug AS category_slug,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id,
|
||||||
|
AVG(reviews.rating) AS rating,
|
||||||
|
COUNT(reviews.id) AS reviews,
|
||||||
|
MATCH (mods.name, mods.description) AGAINST (? IN NATURAL LANGUAGE MODE) AS score
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN categories ON categories.id = mods.category
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.mod AS `mod`
|
||||||
|
FROM reviews
|
||||||
|
JOIN users ON users.id = reviews.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS reviews ON reviews.mod = mods.id
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
GROUP BY mods.id
|
||||||
|
HAVING (
|
||||||
|
score > 0
|
||||||
|
OR mods.name LIKE ?
|
||||||
|
OR mods.description LIKE ?
|
||||||
|
)
|
||||||
|
ORDER BY score DESC,
|
||||||
|
IFNULL(rating, 0) DESC,
|
||||||
|
reviews DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_MODS * ($pageNumber - 1);
|
||||||
|
$pageMods = PAGE_MODS;
|
||||||
|
$statement->bind_param('sssii', $searchQuery, $queryLike, $queryLike, $firstNumber, $pageMods);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modsPresent = false;
|
||||||
|
while ($mod = $result->fetch_assoc()) {
|
||||||
|
if (!$modsPresent) {
|
||||||
|
echo '<div class="mods">';
|
||||||
|
}
|
||||||
|
$modsPresent = true;
|
||||||
|
echo '<div class="mods-mod-container">
|
||||||
|
<div class="mods-mod-card">
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '"><img src="' . htmlspecialchars(APP_ROOT . 'img/' . (isset($mod['image_ext']) && $mod['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $mod['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $mod['image_ext'])) : 'mod-missing.png')) . '" alt="' . htmlspecialchars($mod['name']) . ' cover image"></a>
|
||||||
|
<div class="mods-mod-card-contents">
|
||||||
|
<h2><a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '">' . htmlspecialchars($mod['name']) . '</a></h2>
|
||||||
|
<p>' . (isset($mod['description']) && $mod['description'] ? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars(shortenDescription($mod['description']))) : "<i>No description</i>") . '</p>
|
||||||
|
<div class="mods-mod-card-bottom">
|
||||||
|
<p class="mods-mod-card-publisher">Publisher: <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($mod['user'])) . '">' . htmlspecialchars($mod['user']) . '</a>' . (isset($_SESSION['user']) && $_SESSION['user'] == $mod['user_id'] ? ' | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($mod['slug'])) . '">Edit mod</a>' : '') . '</p>
|
||||||
|
' . ($mod['rating'] ? '<span class="badge">' . htmlspecialchars(number_format($mod['rating'], 2)) . ' ★</span>' : '') . '
|
||||||
|
<span class="badge">' . ($mod['is_paid'] ? 'Paid' : 'Gratis') . '</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
if ($modsPresent) {
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<p>No mods found matching the <b>“' . htmlspecialchars($searchQuery) . '”</b> query.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Check your search query for misspellings</li>
|
||||||
|
<li>Try using different keywords</li>
|
||||||
|
<li>Try replacing some keywords with more general ones</li>
|
||||||
|
</ul>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search' . ($searchQuery ? '?q=' . urlencode($searchQuery) . '&' : '?') . 'page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search' . ($searchQuery ? '?q=' . urlencode($searchQuery) . '&' : '?') . 'page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'search' . ($searchQuery ? '?q=' . urlencode($searchQuery) . '&' : '?') . 'page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
258
includes/page_submit.php
Normal file
258
includes/page_submit.php
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
$errorMessage = null;
|
||||||
|
$modSubmitted = false;
|
||||||
|
$slug = null;
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
if (!isset($_POST['_csrf']) || $_POST['_csrf'] != $_SESSION['csrf']) {
|
||||||
|
$errorMessage = "Potential CSRF attack detected.";
|
||||||
|
} elseif (!isset($_POST['name'], $_POST['category'], $_POST['link']) || !$_POST['name'] || !$_POST['category'] || !$_POST['link']) {
|
||||||
|
$errorMessage = "You need to input name, category and download page URL.";
|
||||||
|
} elseif (!filter_var($_POST['link'], FILTER_VALIDATE_URL) || !preg_match('/^https?:\\/\\//', $_POST['link'])) {
|
||||||
|
$errorMessage = "Invalid download page URL.";
|
||||||
|
} elseif (isset($_POST['docslink']) && $_POST['docslink'] && (!filter_var($_POST['docslink'], FILTER_VALIDATE_URL) || !preg_match('/^https?:\\/\\//', $_POST['docslink']))) {
|
||||||
|
$errorMessage = "Invalid documentation URL.";
|
||||||
|
} elseif (!filter_var($_POST['category'], FILTER_VALIDATE_INT)) {
|
||||||
|
$errorMessage = "Invalid category.";
|
||||||
|
} else {
|
||||||
|
$categoryID = intval($_POST['category']);
|
||||||
|
$statement = $connection->prepare("SELECT id FROM categories WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $categoryID);
|
||||||
|
$statement->execute();
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$isCategoryPresent = boolval($result->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$isCategoryPresent) {
|
||||||
|
$errorMessage = "The selected category doesn't exist.";
|
||||||
|
} else {
|
||||||
|
$slugError = false;
|
||||||
|
$tempSlug = null;
|
||||||
|
$tempSlugCount = 1;
|
||||||
|
while (is_null($slug)) {
|
||||||
|
if (!$tempSlug) {
|
||||||
|
$tempSlug = strtolower($_POST['name']);
|
||||||
|
$tempSlug = preg_replace('/[^a-zA-Z0-9]+/', '-', $tempSlug);
|
||||||
|
$tempSlug = preg_replace('/^-+/', '', $tempSlug);
|
||||||
|
$tempSlug = preg_replace('/-+$/', '', $tempSlug);
|
||||||
|
}
|
||||||
|
|
||||||
|
$statement = $connection->prepare("SELECT slug FROM mods WHERE slug = ? UNION SELECT slug FROM mods_pending WHERE slug = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
$slugError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$tempSlug2 = $tempSlug . ($tempSlugCount > 1 ? '-' . strval($tempSlugCount) : '');
|
||||||
|
$statement->bind_param('ss', $tempSlug2, $tempSlug2);
|
||||||
|
$statement->execute();
|
||||||
|
$slugExistsResult = $statement->get_result();
|
||||||
|
if (!$slugExistsResult) {
|
||||||
|
$slugError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
$statement->close();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
$slugExists = boolval($slugExistsResult->fetch_assoc());
|
||||||
|
$statement->close();
|
||||||
|
if (!$slugExists) {
|
||||||
|
$slug = $tempSlug2;
|
||||||
|
} else {
|
||||||
|
$tempSlugCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$slugError) {
|
||||||
|
$fileError = false;
|
||||||
|
$fileExtension = null;
|
||||||
|
$modPendingUploadDirectory = APP_FSROOT . '/img/mods_pending';
|
||||||
|
$modUploadDirectory = APP_FSROOT . '/img/mods';
|
||||||
|
if (isset($_FILES['cover']) && $_FILES['cover']['error'] != UPLOAD_ERR_NO_FILE) {
|
||||||
|
if ($_FILES['cover']['error'] != UPLOAD_ERR_OK) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
} else {
|
||||||
|
$fileTmpPath = $_FILES['cover']['tmp_name'];
|
||||||
|
$fileName = $_FILES['cover']['name'];
|
||||||
|
$fileSize = $_FILES['cover']['size'];
|
||||||
|
$fileType = $_FILES['cover']['type'];
|
||||||
|
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
|
||||||
|
|
||||||
|
if ($fileSize > IMAGE_MAX_SIZE) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "The cover image is too large. Maximum cover image size: " . formatFileSize(IMAGE_MAX_SIZE);
|
||||||
|
} elseif (!in_array($fileExtension, IMAGE_EXTENSIONS_ALLOWED, true)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "Invalid cover image extension.";
|
||||||
|
} else {
|
||||||
|
$imageType = exif_imagetype($fileTmpPath);
|
||||||
|
if (!$imageType || ($fileType && image_type_to_mime_type($imageType) != $fileType)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "The cover image is either corrupted or of wrong type.";
|
||||||
|
} else {
|
||||||
|
if (!file_exists($modPendingUploadDirectory) && !mkdir($modPendingUploadDirectory, 0777, true)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
}
|
||||||
|
if (!$fileError) {
|
||||||
|
$uploadedCoverImagePathname = $modPendingUploadDirectory . '/' . str_replace(['/', '\\'], '', $modDataToEdit['slug']) . '.' . $fileExtension;
|
||||||
|
if (!move_uploaded_file($fileTmpPath, $uploadedCoverImagePathname)) {
|
||||||
|
$fileError = true;
|
||||||
|
$errorMessage = "An unexpected error occurred while uploading the cover image.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$fileError) {
|
||||||
|
$statement = $connection->prepare("INSERT INTO mods_pending (
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
category,
|
||||||
|
link,
|
||||||
|
docs_link,
|
||||||
|
user,
|
||||||
|
image_ext,
|
||||||
|
is_paid,
|
||||||
|
is_rejected
|
||||||
|
) VALUES (
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
?,
|
||||||
|
0
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (!$statement) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
} else {
|
||||||
|
$modName = $_POST['name'];
|
||||||
|
$modSlug = $slug;
|
||||||
|
$modDescription = isset($_POST['description']) && $_POST['description'] ? $_POST['description'] : null;
|
||||||
|
$modCategory = $categoryID;
|
||||||
|
$modLink = $_POST['link'];
|
||||||
|
$modDocsLink = isset($_POST['docslink']) && $_POST['docslink'] ? $_POST['docslink'] : null;
|
||||||
|
$modUser = $_SESSION['user'];
|
||||||
|
$modImageExt = $fileExtension;
|
||||||
|
$modIsPaid = isset($_POST['paid']) ? 1 : 0;
|
||||||
|
$statement->bind_param('sssissisi', $modName, $modSlug, $modDescription, $modCategory, $modLink, $modDocsLink, $modUser, $modImageExt, $modIsPaid);
|
||||||
|
if (!$statement->execute()) {
|
||||||
|
$errorMessage = "An unexpected error occurred while submitting the mod.";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modSubmitted = true;
|
||||||
|
$statement->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$modSubmitted) {
|
||||||
|
$pageTitle = "Submit mod";
|
||||||
|
$pageDescription = "Submit the SVR.JS mod in SVR.JS Mods directory.";
|
||||||
|
} else {
|
||||||
|
$pageTitle = "Mod submitted";
|
||||||
|
$pageDescription = "The submitted mod is now awaiting moderators' approval.";
|
||||||
|
}
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<?php if ($modSubmitted) { ?>
|
||||||
|
<h1>Mod submitted</h1>
|
||||||
|
<p>The submitted mod is now awaiting moderators' approval.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods'); ?>" class="btn">View pending mods</a></p>
|
||||||
|
<?php } else { ?>
|
||||||
|
<h1>Submit mod</h1>
|
||||||
|
<form action="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'submit') ?>" method="post" class="form" enctype="multipart/form-data">
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="name">Mod name:</label>
|
||||||
|
<input type="text" id="name" name="name" required maxlength="255">
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="cover">Cover image:</label>
|
||||||
|
<input type="file" id="cover" name="cover" accept="<?php echo htmlspecialchars(implode(',', array_map(
|
||||||
|
function ($extension) {
|
||||||
|
return '.' . $extension;
|
||||||
|
},
|
||||||
|
IMAGE_EXTENSIONS_ALLOWED
|
||||||
|
))) ?>">
|
||||||
|
<p>Allowed file extensions for the cover image: <strong><?php echo htmlspecialchars(implode(', ', array_map(
|
||||||
|
function ($extension) {
|
||||||
|
return '.' . $extension;
|
||||||
|
},
|
||||||
|
IMAGE_EXTENSIONS_ALLOWED
|
||||||
|
))) ?></strong></p>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="category">Category:</label>
|
||||||
|
<select id="category" name="category" required>
|
||||||
|
<?php
|
||||||
|
$result = $connection->query('SELECT id, name FROM categories');
|
||||||
|
if ($result) {
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
echo '<option value="' . htmlspecialchars(strval($row['id'])) . '">' . htmlspecialchars($row['name']) . '</option>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="description">Mod description:</label>
|
||||||
|
<textarea name="description" id="description" maxlength="10000"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="link">Download page URL:</label>
|
||||||
|
<input type="url" id="link" name="link" required maxlength="255">
|
||||||
|
</div>
|
||||||
|
<div class="form-block">
|
||||||
|
<label for="docslink">Documentation URL:</label>
|
||||||
|
<input type="url" id="docslink" name="docslink" maxlength="255">
|
||||||
|
</div>
|
||||||
|
<div class="form-block-checkbox">
|
||||||
|
<input type="checkbox" name="paid" id="paid">
|
||||||
|
<label for="paid">Paid?</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($errorMessage) echo '<p class="form-error">' . htmlspecialchars($errorMessage) . '</p>'; ?>
|
||||||
|
<div class="form-block">
|
||||||
|
<input type="submit" value="Submit mod">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_csrf" value="<?php echo htmlspecialchars($_SESSION['csrf']) ?>">
|
||||||
|
</form>
|
||||||
|
<?php } ?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
|
||||||
|
if ($modSubmitted) {
|
||||||
|
$moderatorResult = $connection->query("SELECT email AS address, username AS name FROM users WHERE is_moderator = 1;");
|
||||||
|
if ($moderatorResult) {
|
||||||
|
$moderators = [];
|
||||||
|
while ($moderator = $moderatorResult->fetch_assoc()) {
|
||||||
|
array_push($moderators, $moderator);
|
||||||
|
}
|
||||||
|
sendEmail($moderators, 'A mod has been submitted that requires approval', "A mod has been submitted that requires approval:\n\nMod name: " . str_replace(["\r\n", "\r", "\n"], '', $_POST['name']) . "\nSlug: " . $slug . "\n\nPlease review the mod and approve or reject it.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
70
includes/page_tos.php
Normal file
70
includes/page_tos.php
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "Terms of Service";
|
||||||
|
$pageDescription = "Learn your rights and responsibilities with SVR.JS Mods directory. Our Terms of Service ensure a transparent and fair experience for all users.";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>Terms of Service</h1>
|
||||||
|
<p><em>Effective date: December 24, 2024</em></p>
|
||||||
|
<p>Welcome to the SVR.JS Mods directory (the "Service"). By accessing or using the Service, you agree to comply with and be bound by the following terms and conditions ("Terms"). If you do not agree to these Terms, please do not use the Service.</p>
|
||||||
|
<h2>1. Acceptance of Terms</h2>
|
||||||
|
<p>By using the Service, you affirm that you are at least 18 years old or have the legal capacity to enter into this agreement under the laws of your jurisdiction.</p>
|
||||||
|
<h2>2. Account Registration</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Eligibility</strong>: To submit mods or leave ratings, you must create an account.</li>
|
||||||
|
<li><strong>Accuracy of Information</strong>: You agree to provide accurate and up-to-date information during the registration process.</li>
|
||||||
|
<li><strong>Account Security</strong>: You are responsible for maintaining the confidentiality of your account credentials and for any activity that occurs under your account.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>3. Submitting Mods</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Ownership and Rights</strong>: By submitting a mod, you affirm that you own the mod or have the necessary rights to submit it.</li>
|
||||||
|
<li><strong>Content Standards</strong>: Mods submitted must comply with the following guidelines:<ol>
|
||||||
|
<li><strong>No Malicious Code</strong>: Mods must not contain viruses, malware, spyware, or any other harmful components.</li>
|
||||||
|
<li><strong>Originality</strong>: Mods must be original or properly credited to the original creators with appropriate permissions.</li>
|
||||||
|
<li><strong>Compliance with Laws</strong>: Mods must not violate any applicable laws or regulations.</li>
|
||||||
|
<li><strong>Respectful Content</strong>: Mods must not include offensive, discriminatory, or otherwise inappropriate content.</li>
|
||||||
|
<li><strong>Proper Documentation</strong>: Mods should include clear instructions for installation and use.</li>
|
||||||
|
<li><strong>No Unauthorized Data Collection</strong>: Mods must not collect user data without explicit permission and transparency.</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
<li><strong>Moderation</strong>: All mods are subject to review by our moderators. We reserve the right to accept, reject, or remove mods at our sole discretion.</li>
|
||||||
|
<li><strong>License Grant</strong>: By submitting a mod, you grant the Service a worldwide, non-exclusive, royalty-free license to host, display, and distribute your mod.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>4. User Ratings</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Fair Use</strong>: Users may leave ratings and reviews on mods. You agree to provide honest and constructive feedback.</li>
|
||||||
|
<li><strong>Prohibited Behavior</strong>: Fake reviews, spam, or abusive language are prohibited and may result in account suspension or termination.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>5. Prohibited Activities</h2>
|
||||||
|
<p>When using the Service, you agree not to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Submit mods containing malware, viruses, or harmful code.</li>
|
||||||
|
<li>Impersonate other users or submit mods under a false identity.</li>
|
||||||
|
<li>Use the Service to harass, threaten, or otherwise harm others.</li>
|
||||||
|
<li>Circumvent or attempt to circumvent moderation processes.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>6. Intellectual Property</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Ownership</strong>: The Service retains ownership of its name, logo, and other trademarks. Third-party mod creators retain ownership of their mods, subject to the license granted to the Service.</li>
|
||||||
|
<li><strong>Claims</strong>: If you believe a mod infringes your intellectual property rights, please contact us at <em>support@svrjs.org</em> with detailed information.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>7. Termination of Access</h2>
|
||||||
|
<p>We reserve the right to suspend or terminate your access to the Service at any time for violations of these Terms or other policies.</p>
|
||||||
|
<h2>8. Disclaimers</h2>
|
||||||
|
<ul>
|
||||||
|
<li><strong>No Warranty</strong>: The Service is provided "as is" without warranties of any kind, either express or implied.</li>
|
||||||
|
<li><strong>Limitation of Liability</strong>: We are not liable for any damages arising from the use of the Service, including but not limited to issues caused by mods downloaded from the directory.</li>
|
||||||
|
</ul>
|
||||||
|
<h2>9. Changes to the Terms</h2>
|
||||||
|
<p>We may update these Terms from time to time. Changes will be posted on this page, and your continued use of the Service constitutes acceptance of the updated Terms.</p>
|
||||||
|
<h2>10. Governing Law</h2>
|
||||||
|
<p>These Terms are governed by the laws of Poland, without regard to its conflict of law principles.</p>
|
||||||
|
<h2>11. Contact Us</h2>
|
||||||
|
<p>If you have any questions about these Terms, please contact us at <em>support@svrjs.org</em>.</p>
|
||||||
|
<p>Thank you for using the SVR.JS Mods directory!</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
32
includes/page_user.php
Normal file
32
includes/page_user.php
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $userData['username'] . "'s profile";
|
||||||
|
$pageDescription = "View the " . $userData['username'] . "'s profile on SVR.JS Mods directory.";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($userData['username']); ?>'s profile</h1>
|
||||||
|
<p><?php echo isset($userData['bio']) && $userData['bio']
|
||||||
|
? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars($userData['bio']))
|
||||||
|
: "<i>No biography</i>";
|
||||||
|
?></p>
|
||||||
|
<p><b>Mods:</b> <?php echo htmlspecialchars(number_format($userData['mods'], 0)); ?> | <a href="<?php
|
||||||
|
echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-mods/' . urlencode($userData['username']));
|
||||||
|
?>">View <?php echo htmlspecialchars($userData['username']); ?>'s mods</a></p>
|
||||||
|
<p><b>Reviews:</b> <?php echo htmlspecialchars(number_format($userData['reviews'], 0)); ?> | <a href="<?php
|
||||||
|
echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-reviews/' . urlencode($userData['username']));
|
||||||
|
?>">View <?php echo htmlspecialchars($userData['username']); ?>'s reviews</a></p>
|
||||||
|
<?php
|
||||||
|
if (isset($_SESSION['user']) && $userData['id'] == $_SESSION['user']) {
|
||||||
|
echo '<div class="button-container">
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-profile') . '" class="btn">Edit your profile</a>
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'pending-mods') . '" class="btn btn-secondary">View pending mods</a>
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'change-user-data') . '" class="btn btn-secondary">Change user data</a>
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'delete-account') . '" class="btn btn-secondary">Delete account</a>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
14
includes/page_userdeleted.php
Normal file
14
includes/page_userdeleted.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "User profile no longer exists";
|
||||||
|
$pageDescription = "The user has deleted their account.";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>User profile no longer exists</h1>
|
||||||
|
<p>The user has deleted their account.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
139
includes/page_usermods.php
Normal file
139
includes/page_usermods.php
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $userData['username'] . "'s mods";
|
||||||
|
$pageDescription = 'Explore SVR.JS mods published by ' . $userData['username'] . '.';
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($userData['username']) ?>'s mods</h1>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($userData['username'])); ?>">Return to user's profile</a></p>
|
||||||
|
<?php
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(mods.id) AS count
|
||||||
|
FROM mods
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE users.username = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1;');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('s', $userData['username']);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id,
|
||||||
|
AVG(reviews.rating) AS rating,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.mod AS `mod`
|
||||||
|
FROM reviews
|
||||||
|
JOIN users ON users.id = reviews.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS reviews ON reviews.mod = mods.id
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE users.username = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
GROUP BY mods.id
|
||||||
|
ORDER BY IFNULL(rating, 0) DESC,
|
||||||
|
reviews DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_MODS * ($pageNumber - 1);
|
||||||
|
$pageMods = PAGE_MODS;
|
||||||
|
$statement->bind_param('sii', $userData['username'], $firstNumber, $pageMods);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching mods.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modsPresent = false;
|
||||||
|
while ($mod = $result->fetch_assoc()) {
|
||||||
|
if (!$modsPresent) {
|
||||||
|
echo '<div class="mods">';
|
||||||
|
}
|
||||||
|
$modsPresent = true;
|
||||||
|
echo '<div class="mods-mod-container">
|
||||||
|
<div class="mods-mod-card">
|
||||||
|
<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '"><img src="' . htmlspecialchars(APP_ROOT . 'img/' . (isset($mod['image_ext']) && $mod['image_ext'] ? 'mods/' . urlencode(str_replace(['/', '\\'], '', $mod['slug'])) . '.' . urlencode(str_replace(['/', '\\'], '', $mod['image_ext'])) : 'mod-missing.png')) . '" alt="' . htmlspecialchars($mod['name']) . ' cover image"></a>
|
||||||
|
<div class="mods-mod-card-contents">
|
||||||
|
<h2><a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($mod['slug'])) . '">' . htmlspecialchars($mod['name']) . '</a></h2>
|
||||||
|
<p>' . (isset($mod['description']) && $mod['description'] ? str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars(shortenDescription($mod['description']))) : "<i>No description</i>") . '</p>
|
||||||
|
<div class="mods-mod-card-bottom">
|
||||||
|
<p class="mods-mod-card-publisher">Publisher: <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($mod['user'])) . '">' . htmlspecialchars($mod['user']) . '</a>' . (isset($_SESSION['user']) && $_SESSION['user'] == $mod['user_id'] ? ' | <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'edit-mod/' . urlencode($mod['slug'])) . '">Edit mod</a>' : '') . '</p>
|
||||||
|
' . ($mod['rating'] ? '<span class="badge">' . htmlspecialchars(number_format($mod['rating'], 2)) . ' ★</span>' : '') . '
|
||||||
|
<span class="badge">' . ($mod['is_paid'] ? 'Paid' : 'Gratis') . '</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
}
|
||||||
|
if ($modsPresent) {
|
||||||
|
echo '</div>';
|
||||||
|
} else {
|
||||||
|
echo '<p>No mods.</p>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-mods/' . urlencode($userData['username']) . '?page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-mods/' . urlencode($userData['username']) . '?page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-mods/' . urlencode($userData['username']) . '?page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
130
includes/page_userreviews.php
Normal file
130
includes/page_userreviews.php
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = $userData['username'] . "'s reviews";
|
||||||
|
$pageDescription = 'View reviews by ' . $userData['username'] . '.';
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1><?php echo htmlspecialchars($userData['username']) ?>'s reviews</h1>
|
||||||
|
<p><a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($userData['username'])); ?>">Return to user's profile</a></p>
|
||||||
|
<?php
|
||||||
|
$countStatement = $connection->prepare('SELECT COUNT(reviews.id) AS count
|
||||||
|
FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id, mods.is_removed AS is_removed, mods.name AS name, mods.slug AS slug FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
JOIN users ON users.id = reviews.user
|
||||||
|
WHERE users.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1;');
|
||||||
|
if (!$countStatement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$countStatement->bind_param('i', $userData['id']);
|
||||||
|
$countStatement->execute();
|
||||||
|
|
||||||
|
$countResult = $countStatement->get_result();
|
||||||
|
|
||||||
|
if (!$countResult) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
$countStatement->close();
|
||||||
|
} else {
|
||||||
|
$countRow = $countResult->fetch_assoc();
|
||||||
|
$countStatement->close();
|
||||||
|
if (!$countRow) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$modCount = $countRow['count'];
|
||||||
|
$totalPages = ceil($modCount / PAGE_MODS);
|
||||||
|
$statement = $connection->prepare('SELECT
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.review AS review,
|
||||||
|
mods.name AS mod_name,
|
||||||
|
mods.slug AS mod_slug,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id
|
||||||
|
FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id, mods.is_removed AS is_removed, mods.name AS name, mods.slug AS slug FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
JOIN users ON users.id = reviews.user
|
||||||
|
WHERE users.id = ?
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
AND users.is_suspended = 0
|
||||||
|
AND users.is_deleted = 0
|
||||||
|
AND users.is_verified = 1
|
||||||
|
ORDER BY reviews.id DESC
|
||||||
|
LIMIT ?,?;');
|
||||||
|
if (!$statement) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
} else {
|
||||||
|
$pageNumber = isset($_GET['page']) && filter_var($_GET['page'], FILTER_VALIDATE_INT) ? intval($_GET['page']) : 1;
|
||||||
|
$firstNumber = PAGE_REVIEWS * ($pageNumber - 1);
|
||||||
|
$pageReviews = PAGE_REVIEWS;
|
||||||
|
$statement->bind_param('iii', $userData['id'], $firstNumber, $pageReviews);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
|
||||||
|
if (!$result) {
|
||||||
|
echo "<p>An unexpected error occurred while fetching reviews.</p>";
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$reviewsPresent = false;
|
||||||
|
while ($review = $result->fetch_assoc()) {
|
||||||
|
$reviewsPresent = true;
|
||||||
|
echo '<div class="review"><div class="review-header">';
|
||||||
|
|
||||||
|
$stars = round($review['rating']);
|
||||||
|
|
||||||
|
echo '<span class="rating-stars">';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $stars; $i++) {
|
||||||
|
echo '<span class="rating-star rating-star-higlighted">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = $stars; $i < 5; $i++) {
|
||||||
|
echo '<span class="rating-star">★</span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ' | by <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user/' . urlencode($review['user'])) . '">' . htmlspecialchars($review['user']) . '</a> | on <a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'mod/' . urlencode($review['mod_slug'])) . '">' . htmlspecialchars($review['mod_name']) . '</a></div><p>' . str_replace(["\r\n", "\n", "\r"], '<br/>', htmlspecialchars($review['review'])) . '</div>';
|
||||||
|
}
|
||||||
|
if (!$reviewsPresent) {
|
||||||
|
echo '<p>No reviews.</p>';
|
||||||
|
}
|
||||||
|
$statement->close();
|
||||||
|
if ($totalPages > 1) {
|
||||||
|
$begPage = $pageNumber - 2;
|
||||||
|
$endPage = $pageNumber + 2;
|
||||||
|
if ($endPage > $totalPages) {
|
||||||
|
$begPage -= $endPage - $totalPages;
|
||||||
|
$endPage = $totalPages;
|
||||||
|
}
|
||||||
|
if ($begPage < 1) {
|
||||||
|
$endPage += 1 - $begPage;
|
||||||
|
$begPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="pagination">';
|
||||||
|
echo $pageNumber <= 1 ? '<span>‹</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-reviews/' . urlencode($userData['username']) . '?page=' . urlencode(strval($pageNumber - 1))) . '">‹</a>';
|
||||||
|
for ($i = 0; $i < ($totalPages > 5 ? 5 : $totalPages); $i++) {
|
||||||
|
echo $pageNumber == $begPage + $i ? '<span>' . htmlspecialchars(strval($begPage + $i)) . '</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-reviews/' . urlencode($userData['username']) . '?page=' . urlencode(strval($begPage + $i))) . '">' . htmlspecialchars(strval($begPage + $i)) . '</a>';
|
||||||
|
}
|
||||||
|
echo $pageNumber >= $totalPages ? '<span>›</span>' : '<a href="' . htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'user-reviews/' . urlencode($userData['username']) . '?page=' . urlencode(strval($pageNumber + 1))) . '">›</a>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
14
includes/page_usersuspended.php
Normal file
14
includes/page_usersuspended.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
$pageTitle = "User suspended";
|
||||||
|
$pageDescription = "User suspended";
|
||||||
|
include 'header.php';
|
||||||
|
?>
|
||||||
|
<main class="content">
|
||||||
|
<h1>User suspended</h1>
|
||||||
|
<p>The user has been suspended due to violations of <a href="<?php echo htmlspecialchars((URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'tos') ?>">Terms of Service</a>.</p>
|
||||||
|
<p><a href="<?php echo htmlspecialchars(URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') ?>" class="btn">Return to home</a></p>
|
||||||
|
</main>
|
||||||
|
<?php
|
||||||
|
include 'footer.php';
|
||||||
|
?>
|
811
includes/pages.php
Normal file
811
includes/pages.php
Normal file
|
@ -0,0 +1,811 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
// THIS PHP SCRIPT INCLUDE IS JUST FOR HTML PAGES! IT'S NOT INTENDED FOR OTHER FORMATS
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
// Disable caching
|
||||||
|
header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
|
||||||
|
header("Pragma: no-cache");
|
||||||
|
if (COMPRESSION_ENABLED) {
|
||||||
|
header("Vary: Accept-Encoding, Cookie");
|
||||||
|
} else {
|
||||||
|
header("Vary: Cookie");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "Expires" header
|
||||||
|
header_remove("Expires");
|
||||||
|
|
||||||
|
// Enable session strict mode
|
||||||
|
ini_set('session.use_strict_mode', '1');
|
||||||
|
|
||||||
|
// Set session cookie flags
|
||||||
|
ini_set('session.cookie_httponly', '1');
|
||||||
|
if (isset($_SERVER['HTTPS'])) ini_set('session.cookie_secure', '1');
|
||||||
|
|
||||||
|
// Register the custom session handler
|
||||||
|
$sessionHandler = new MySQLSessionHandler($connection);
|
||||||
|
session_set_save_handler(
|
||||||
|
array($sessionHandler, 'open'),
|
||||||
|
array($sessionHandler, 'close'),
|
||||||
|
array($sessionHandler, 'read'),
|
||||||
|
array($sessionHandler, 'write'),
|
||||||
|
array($sessionHandler, 'destroy'),
|
||||||
|
array($sessionHandler, 'gc'),
|
||||||
|
array($sessionHandler, 'create_sid'),
|
||||||
|
array($sessionHandler, 'validate_sid')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (session_start()) {
|
||||||
|
setupHeaders();
|
||||||
|
|
||||||
|
$csrfToken = "";
|
||||||
|
if (isset($_SESSION['csrf'])) {
|
||||||
|
$csrfToken = $_SESSION['csrf'];
|
||||||
|
} else {
|
||||||
|
if (function_exists('random_bytes')) {
|
||||||
|
$csrfToken = bin2hex(random_bytes(32));
|
||||||
|
} else {
|
||||||
|
$csrfToken = '';
|
||||||
|
for ($i = 0; $i < 32; $i++) {
|
||||||
|
$csrfToken = $csrfToken . bin2hex(rand(0, 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$_SESSION['csrf'] = $csrfToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_SESSION['user'])) {
|
||||||
|
$statement = $connection->prepare("SELECT id, username FROM users WHERE id = ? AND is_suspended = 0 AND is_deleted = 0 AND is_verified = 1");
|
||||||
|
if (!$statement) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
} else {
|
||||||
|
$statement->bind_param("i", $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result || !$result->fetch_assoc()) {
|
||||||
|
unset($_SESSION['user']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$segments = explode('/', isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : "/");
|
||||||
|
array_shift($segments);
|
||||||
|
|
||||||
|
if (count($segments) == 0 || (count($segments) == 1 && $segments[0] == '')) {
|
||||||
|
header("Cache-Control: public, max-age=60");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_index.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'tos') {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_tos.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'search') {
|
||||||
|
header("Cache-Control: public, max-age=60");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_search.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'login') {
|
||||||
|
include 'page_login.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'register') {
|
||||||
|
include 'page_register.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'logout') {
|
||||||
|
include 'page_logout.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'forgot-password') {
|
||||||
|
include 'page_forgotpassword.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'confirm-password') {
|
||||||
|
include 'page_confirmpassword.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'confirm-registration') {
|
||||||
|
include 'page_confirmregistration.php';
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'pending-mods') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
include 'page_pendingmods.php';
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'submit') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
include 'page_submit.php';
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'confirm-email') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
include 'page_confirmemail.php';
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'delete-account') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT id, username, password, is_moderator, email FROM users WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} else {
|
||||||
|
include 'page_deleteaccount.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'edit-profile') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT id, username, bio FROM users WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} else {
|
||||||
|
include 'page_editprofile.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 1 && $segments[0] == 'change-user-data') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT id, username, password, email, bio FROM users WHERE id = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('i', $_SESSION['user']);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} else {
|
||||||
|
include 'page_changeuserdata.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'category') {
|
||||||
|
$statement = $connection->prepare("SELECT id, name, slug FROM categories WHERE slug = ?");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$categoryData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$categoryData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: public, max-age=60");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_category.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'user') {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
users.id AS id,
|
||||||
|
users.username AS username,
|
||||||
|
users.bio AS bio,
|
||||||
|
users.is_suspended AS is_suspended,
|
||||||
|
users.is_deleted AS is_deleted,
|
||||||
|
COUNT(mods.id) AS mods,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM users
|
||||||
|
LEFT JOIN mods ON mods.user = users.id
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT reviews.id, reviews.user FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
) AS reviews ON reviews.user = users.id
|
||||||
|
WHERE users.is_verified = 1
|
||||||
|
GROUP BY users.id
|
||||||
|
HAVING LOWER(users.username) = LOWER(?);");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($userData['is_deleted']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_userdeleted.php';
|
||||||
|
} elseif ($userData['is_suspended']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_usersuspended.php';
|
||||||
|
} else {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
}
|
||||||
|
include 'page_user.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'user-mods') {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
users.id AS id,
|
||||||
|
users.username AS username,
|
||||||
|
users.bio AS bio,
|
||||||
|
users.is_suspended AS is_suspended,
|
||||||
|
users.is_deleted AS is_deleted,
|
||||||
|
COUNT(mods.id) AS mods
|
||||||
|
FROM users
|
||||||
|
LEFT JOIN mods ON mods.user = users.id
|
||||||
|
AND mods.is_removed = 0
|
||||||
|
WHERE users.is_verified = 1
|
||||||
|
GROUP BY users.id
|
||||||
|
HAVING LOWER(users.username) = LOWER(?);");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($userData['is_deleted']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_userdeleted.php';
|
||||||
|
} elseif ($userData['is_suspended']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_usersuspended.php';
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: public, max-age=60");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_usermods.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'user-reviews') {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
users.id AS id,
|
||||||
|
users.username AS username,
|
||||||
|
users.bio AS bio,
|
||||||
|
users.is_suspended AS is_suspended,
|
||||||
|
users.is_deleted AS is_deleted,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM users
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT reviews.id, reviews.user FROM reviews
|
||||||
|
JOIN (
|
||||||
|
SELECT mods.id AS id FROM mods
|
||||||
|
JOIN users ON users.id = mods.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS mods ON mods.id = reviews.mod
|
||||||
|
) AS reviews ON reviews.user = users.id
|
||||||
|
WHERE users.is_verified = 1
|
||||||
|
GROUP BY users.id
|
||||||
|
HAVING LOWER(users.username) = LOWER(?);");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$userData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$userData) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($userData['is_deleted']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_userdeleted.php';
|
||||||
|
} elseif ($userData['is_suspended']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_usersuspended.php';
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: public, max-age=60");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_userreviews.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'mod') {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
categories.name AS category,
|
||||||
|
categories.slug AS category_slug,
|
||||||
|
mods.link AS link,
|
||||||
|
mods.docs_link AS docs_link,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
mods.is_removed AS is_removed,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id,
|
||||||
|
AVG(reviews.rating) AS rating,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN categories ON categories.id = mods.category
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.mod AS `mod`
|
||||||
|
FROM reviews
|
||||||
|
JOIN users ON users.id = reviews.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS reviews ON reviews.mod = mods.id
|
||||||
|
WHERE mods.slug = ?
|
||||||
|
GROUP BY mods.id;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$modData || !$modData['is_user_verified']) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($modData['is_removed'] || $modData['is_user_suspended'] || $modData['is_user_deleted']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_modremoved.php';
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
include 'page_mod.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'edit-mod') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
mods.category AS category_id,
|
||||||
|
mods.link AS link,
|
||||||
|
mods.docs_link AS docs_link,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
mods.is_removed AS is_removed,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.id AS user_id,
|
||||||
|
0 AS pending
|
||||||
|
FROM mods
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE mods.slug = ?;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$currentModData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if ($currentModData && !$currentModData['is_user_verified']) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($currentModData && ($currentModData['is_removed'] || $currentModData['is_user_suspended'] || $currentModData['is_user_deleted'])) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_modremoved.php';
|
||||||
|
} elseif ($currentModData && ($currentModData['user_id'] != $_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(403);
|
||||||
|
include 'page_editmodnotallowed.php';
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods_pending.id AS id,
|
||||||
|
mods_pending.name AS name,
|
||||||
|
mods_pending.slug AS slug,
|
||||||
|
mods_pending.description AS description,
|
||||||
|
mods_pending.category AS category_id,
|
||||||
|
mods_pending.link AS link,
|
||||||
|
mods_pending.docs_link AS docs_link,
|
||||||
|
mods_pending.image_ext AS image_ext,
|
||||||
|
mods_pending.is_paid AS is_paid,
|
||||||
|
mods_pending.is_rejected AS is_rejected,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.id AS user_id,
|
||||||
|
1 AS pending
|
||||||
|
FROM mods_pending
|
||||||
|
JOIN users ON users.id = mods_pending.user
|
||||||
|
WHERE mods_pending.slug = ?;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$pendingModData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
$modDataToEdit = $pendingModData && !($currentModData && $pendingModData['is_rejected']) ? $pendingModData : $currentModData;
|
||||||
|
|
||||||
|
if (!$modDataToEdit) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($modDataToEdit['user_id'] != $_SESSION['user']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(403);
|
||||||
|
include 'page_editmodnotallowed.php';
|
||||||
|
} else {
|
||||||
|
include 'page_editmod.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'reviews') {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
categories.name AS category,
|
||||||
|
categories.slug AS category_slug,
|
||||||
|
mods.link AS link,
|
||||||
|
mods.docs_link AS docs_link,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
mods.is_removed AS is_removed,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.username AS user,
|
||||||
|
users.id AS user_id,
|
||||||
|
users.email AS user_email,
|
||||||
|
AVG(reviews.rating) AS rating,
|
||||||
|
COUNT(reviews.id) AS reviews
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN categories ON categories.id = mods.category
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
reviews.rating AS rating,
|
||||||
|
reviews.id AS id,
|
||||||
|
reviews.mod AS `mod`
|
||||||
|
FROM reviews
|
||||||
|
JOIN users ON users.id = reviews.user AND users.is_verified = 1 AND users.is_deleted = 0 AND users.is_suspended = 0
|
||||||
|
) AS reviews ON reviews.mod = mods.id
|
||||||
|
WHERE mods.slug = ?
|
||||||
|
GROUP BY mods.id;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modData = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$modData || !$modData['is_user_verified']) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif ($modData['is_removed'] || $modData['is_user_suspended'] || $modData['is_user_deleted']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_modremoved.php';
|
||||||
|
} else {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
}
|
||||||
|
include 'page_reviews.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'discard-mod') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods_pending.id AS id,
|
||||||
|
mods_pending.name AS name,
|
||||||
|
mods_pending.slug AS slug,
|
||||||
|
mods_pending.description AS description,
|
||||||
|
mods_pending.category AS category_id,
|
||||||
|
mods_pending.link AS link,
|
||||||
|
mods_pending.docs_link AS docs_link,
|
||||||
|
mods_pending.image_ext AS image_ext,
|
||||||
|
mods_pending.is_paid AS is_paid,
|
||||||
|
mods_pending.is_rejected AS is_rejected,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.id AS user_id
|
||||||
|
FROM mods_pending
|
||||||
|
JOIN users ON users.id = mods_pending.user
|
||||||
|
WHERE mods_pending.slug = ?;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modDataToDiscard = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$modDataToDiscard || !$modDataToDiscard['is_user_verified']) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif (($modDataToDiscard['is_removed'] || $modDataToDiscard['is_user_suspended'] || $modDataToDiscard['is_user_deleted'])) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_modremoved.php';
|
||||||
|
} elseif ($modDataToDiscard['user_id'] != $_SESSION['user']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(403);
|
||||||
|
include 'page_discardmodnotallowed.php';
|
||||||
|
} else {
|
||||||
|
include 'page_discardmod.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (count($segments) == 2 && $segments[0] == 'remove-mod') {
|
||||||
|
if (!isset($_SESSION['user'])) {
|
||||||
|
header("Cache-Control: public, max-age=30");
|
||||||
|
header_remove("Pragma");
|
||||||
|
header('Location: ' . (URL_REWRITTEN ? APP_ROOT : APP_ROOT . APP_FILENAME . '/') . 'login?redirect=' . urlencode($_SERVER['REQUEST_URI']));
|
||||||
|
http_response_code(302);
|
||||||
|
} else {
|
||||||
|
$statement = $connection->prepare("SELECT
|
||||||
|
mods.id AS id,
|
||||||
|
mods.name AS name,
|
||||||
|
mods.slug AS slug,
|
||||||
|
mods.description AS description,
|
||||||
|
mods.category AS category_id,
|
||||||
|
mods.link AS link,
|
||||||
|
mods.docs_link AS docs_link,
|
||||||
|
mods.image_ext AS image_ext,
|
||||||
|
mods_pending.image_ext AS pending_image_ext,
|
||||||
|
mods.is_paid AS is_paid,
|
||||||
|
mods.is_removed AS is_removed,
|
||||||
|
users.is_suspended AS is_user_suspended,
|
||||||
|
users.is_verified AS is_user_verified,
|
||||||
|
users.is_deleted AS is_user_deleted,
|
||||||
|
users.id AS user_id
|
||||||
|
FROM mods
|
||||||
|
LEFT JOIN mods_pending ON mods.slug = mods_pending.slug
|
||||||
|
JOIN users ON users.id = mods.user
|
||||||
|
WHERE mods.slug = ?;");
|
||||||
|
if (!$statement) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
} else {
|
||||||
|
$statement->bind_param('s', $segments[1]);
|
||||||
|
$statement->execute();
|
||||||
|
|
||||||
|
$result = $statement->get_result();
|
||||||
|
if (!$result) {
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
$statement->close();
|
||||||
|
} else {
|
||||||
|
$modDataToRemove = $result->fetch_assoc();
|
||||||
|
$statement->close();
|
||||||
|
|
||||||
|
if (!$modDataToRemove || !$modDataToRemove['is_user_verified']) {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
} elseif (($modDataToRemove['is_removed'] || $modDataToRemove['is_user_suspended'] || $modDataToRemove['is_user_deleted'])) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(410);
|
||||||
|
include 'page_modremoved.php';
|
||||||
|
} elseif ($modDataToRemove['user_id'] != $_SESSION['user']) {
|
||||||
|
header("Cache-Control: public, max-age=300");
|
||||||
|
header_remove("Pragma");
|
||||||
|
http_response_code(403);
|
||||||
|
include 'page_removemodnotallowed.php';
|
||||||
|
} else {
|
||||||
|
include 'page_removemod.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
include 'page_404.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_id()) session_write_close();
|
||||||
|
} else {
|
||||||
|
setupHeaders();
|
||||||
|
http_response_code(500);
|
||||||
|
include 'page_500.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ob_get_status()) {
|
||||||
|
$content = ob_get_clean();
|
||||||
|
|
||||||
|
if ($content && COMPRESSION_ENABLED && isset($_SERVER['HTTP_ACCEPT_ENCODING'])) {
|
||||||
|
$acceptEncoding = array_map(function ($encoding) {
|
||||||
|
return trim($encoding);
|
||||||
|
}, explode(',', $_SERVER['HTTP_ACCEPT_ENCODING']));
|
||||||
|
|
||||||
|
if (in_array('gzip', $acceptEncoding)) {
|
||||||
|
$content = gzencode($content, 9);
|
||||||
|
header('Content-Encoding: gzip');
|
||||||
|
} elseif (in_array('deflate', $acceptEncoding)) {
|
||||||
|
$content = gzdeflate($content, 9);
|
||||||
|
header('Content-Encoding: deflate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo $content;
|
||||||
|
}
|
159
includes/utils.php
Normal file
159
includes/utils.php
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
<?php
|
||||||
|
if (!defined('SVRJS_MOD_DIRECTORY')) die;
|
||||||
|
|
||||||
|
use PHPMailer\PHPMailer\PHPMailer;
|
||||||
|
use PHPMailer\PHPMailer\SMTP;
|
||||||
|
use PHPMailer\PHPMailer\Exception;
|
||||||
|
|
||||||
|
function sendEmail($recipients, $subject, $message)
|
||||||
|
{
|
||||||
|
// If no recipients, return true
|
||||||
|
if (count($recipients) == 0) return true;
|
||||||
|
|
||||||
|
$mail = new PHPMailer(false);
|
||||||
|
|
||||||
|
// Email server data (SMTP or PHP mail function)
|
||||||
|
if (EMAIL_SMTP) {
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = EMAIL_SMTP_HOST;
|
||||||
|
if (EMAIL_SMTP_AUTHENTICATED) {
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = EMAIL_SMTP_USERNAME;
|
||||||
|
$mail->Password = EMAIL_SMTP_PASSWORD;
|
||||||
|
}
|
||||||
|
if (EMAIL_SMTP_ENCRYPTION == 1) {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
|
||||||
|
} elseif (EMAIL_SMTP_ENCRYPTION == 2) {
|
||||||
|
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||||
|
}
|
||||||
|
$mail->Port = EMAIL_SMTP_PORT;
|
||||||
|
} else {
|
||||||
|
$mail->isMail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email sender and recipient
|
||||||
|
$mail->setFrom(EMAIL_FROM, EMAIL_FROM_NAME);
|
||||||
|
foreach ($recipients as $recipient) {
|
||||||
|
if (isset($recipient['name'])) $mail->addAddress($recipient['address'], $recipient['name']);
|
||||||
|
else $mail->addAddress($recipient['address']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email message
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $message;
|
||||||
|
|
||||||
|
return $mail->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function shortenDescription($description)
|
||||||
|
{
|
||||||
|
if (strlen($description) <= 160) {
|
||||||
|
return $description;
|
||||||
|
} else {
|
||||||
|
return substr($description, 0, 157) . '...';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFileSize($size)
|
||||||
|
{
|
||||||
|
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
$formattedSize = $size;
|
||||||
|
|
||||||
|
for ($i = 0; $size >= 1024 && $i < count($units) - 1; $i++) {
|
||||||
|
$size /= 1024;
|
||||||
|
$formattedSize = round($size, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedSize . $units[$i];
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkHCaptcha($hCaptchaResponse)
|
||||||
|
{
|
||||||
|
if (!HCAPTCHA_ENABLED) return true;
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'secret' => HCAPTCHA_SECRET_KEY,
|
||||||
|
'response' => $hCaptchaResponse
|
||||||
|
);
|
||||||
|
|
||||||
|
$verify = curl_init();
|
||||||
|
|
||||||
|
if (!$verify) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_setopt($verify, CURLOPT_URL, 'https://hcaptcha.com/siteverify');
|
||||||
|
curl_setopt($verify, CURLOPT_POST, true);
|
||||||
|
curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||||
|
curl_setopt($verify, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($verify);
|
||||||
|
|
||||||
|
if (!$response) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseData = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($responseData && $responseData['success']) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkStopForumSpam($username, $email)
|
||||||
|
{
|
||||||
|
if (!STOPFORUMSPAM_ENABLED) return true;
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'username' => $username,
|
||||||
|
'email' => $email,
|
||||||
|
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
|
'json'
|
||||||
|
);
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
if (!$ch) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, 'http://api.stopforumspam.org/api');
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
|
||||||
|
if (!$response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseData = json_decode($response, true);
|
||||||
|
|
||||||
|
if ($responseData && $responseData['success']) {
|
||||||
|
return !(
|
||||||
|
(STOPFORUMSPAM_CHECK_USERNAME && $responseData && $responseData['username'] && $responseData['username']['appears'] && $responseData['username']['frequency'] >= STOPFORUMSPAM_THRESHOLD) ||
|
||||||
|
(STOPFORUMSPAM_CHECK_EMAIL && $responseData && $responseData['email'] && $responseData['email']['appears'] && $responseData['email']['frequency'] >= STOPFORUMSPAM_THRESHOLD) ||
|
||||||
|
(STOPFORUMSPAM_CHECK_IP && $responseData && $responseData['ip'] && $responseData['ip']['appears'] && $responseData['ip']['frequency'] >= STOPFORUMSPAM_THRESHOLD)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupHeaders()
|
||||||
|
{
|
||||||
|
// Disable caching
|
||||||
|
header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
|
||||||
|
header("Pragma: no-cache");
|
||||||
|
if (COMPRESSION_ENABLED) {
|
||||||
|
header("Vary: Accept-Encoding, Cookie");
|
||||||
|
} else {
|
||||||
|
header("Vary: Cookie");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "Expires" header
|
||||||
|
header_remove("Expires");
|
||||||
|
}
|
13
index.php
Normal file
13
index.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
define("SVRJS_MOD_DIRECTORY", null);
|
||||||
|
include 'config.php';
|
||||||
|
$appRoot = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
|
if ($appRoot[strlen($appRoot) - 1] != "/") $appRoot = $appRoot . '/';
|
||||||
|
define('APP_ROOT', $appRoot);
|
||||||
|
define('APP_FSROOT', dirname(__FILE__));
|
||||||
|
define('APP_FILENAME', basename($_SERVER['SCRIPT_NAME']));
|
||||||
|
define('APP_MODERATION_ROOT', $appRoot . 'moderation/');
|
||||||
|
include 'vendor/autoload.php';
|
||||||
|
include 'includes/init.php';
|
||||||
|
include 'includes/pages.php';
|
||||||
|
include 'includes/final.php';
|
284
init.sql
Normal file
284
init.sql
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
/*!999999\- enable the sandbox mode */
|
||||||
|
-- MariaDB dump 10.19 Distrib 10.11.8-MariaDB, for debian-linux-gnu (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: localhost Database: svrjs_mods
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 10.11.8-MariaDB-0ubuntu0.24.04.1
|
||||||
|
|
||||||
|
-- This is a SQL script that intializes the database for SVR.JS Mods directory.
|
||||||
|
-- It's recommended to change the default password hash and email address.
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `categories`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `categories`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `categories` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`slug` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `slug` (`slug`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `categories`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `categories` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `categories` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `categories` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `mods`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `mods`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `mods` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`slug` varchar(255) NOT NULL,
|
||||||
|
`description` varchar(10000) DEFAULT NULL,
|
||||||
|
`category` int(11) NOT NULL,
|
||||||
|
`link` varchar(255) NOT NULL,
|
||||||
|
`docs_link` varchar(255) DEFAULT NULL,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
`image_ext` varchar(10) DEFAULT NULL,
|
||||||
|
`is_paid` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`is_removed` int(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `slug` (`slug`),
|
||||||
|
FULLTEXT KEY `name` (`name`,`description`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mods`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mods` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mods` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mods` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `mods_pending`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `mods_pending`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `mods_pending` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`slug` varchar(255) NOT NULL,
|
||||||
|
`description` varchar(10000) DEFAULT NULL,
|
||||||
|
`category` int(11) NOT NULL,
|
||||||
|
`link` varchar(255) NOT NULL,
|
||||||
|
`docs_link` varchar(255) DEFAULT NULL,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
`image_ext` varchar(10) DEFAULT NULL,
|
||||||
|
`is_paid` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`is_rejected` int(11) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `slug` (`slug`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mods_pending`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mods_pending` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mods_pending` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mods_pending` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `requests_email`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `requests_email`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `requests_email` (
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
`request_date` datetime NOT NULL,
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE KEY `user_id` (`user`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `requests_email`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `requests_email` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `requests_email` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `requests_email` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `requests_password`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `requests_password`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `requests_password` (
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
`request_date` datetime NOT NULL,
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE KEY `user_id` (`user`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `requests_password`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `requests_password` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `requests_password` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `requests_password` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `requests_register`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `requests_register`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `requests_register` (
|
||||||
|
`id` varchar(255) NOT NULL,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
UNIQUE KEY `user_id` (`user`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `requests_register`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `requests_register` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `requests_register` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `requests_register` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `reviews`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `reviews`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `reviews` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`user` int(11) NOT NULL,
|
||||||
|
`mod` int(11) NOT NULL,
|
||||||
|
`rating` tinyint(4) NOT NULL,
|
||||||
|
`review` varchar(1000) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `user_id` (`user`,`mod`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `reviews`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `reviews` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `reviews` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `reviews` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `sessions`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `sessions`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `sessions` (
|
||||||
|
`id` varchar(128) NOT NULL,
|
||||||
|
`data` text NOT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `sessions`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `sessions` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `sessions` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `sessions` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `users`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `users`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `users` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`username` varchar(255) NOT NULL,
|
||||||
|
`password` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
`bio` varchar(1000) DEFAULT NULL,
|
||||||
|
`is_verified` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`is_moderator` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`is_suspended` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`is_deleted` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `username` (`username`) USING BTREE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `users`
|
||||||
|
--
|
||||||
|
|
||||||
|
-- It's recommended to change the default password hash and email address.
|
||||||
|
-- Replace "svrjs" with desired username, "$2y$10$1kVr2tcxGdbpL7/8aigzoO0FQvDLW2WxaThEd2L6PE1ykkn0T9nsS" with a password hash (you can hash it using "password_hash" function in PHP CLI), and "unknown@example.com" with your email address.
|
||||||
|
|
||||||
|
LOCK TABLES `users` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
|
||||||
|
INSERT INTO `users` VALUES
|
||||||
|
(1,'svrjs','$2y$10$1kVr2tcxGdbpL7/8aigzoO0FQvDLW2WxaThEd2L6PE1ykkn0T9nsS','unknown@example.com',NULL,1,1,0,0);
|
||||||
|
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
|
-- Dump completed on 2024-12-27 15:02:30
|
1
js/analytics.js
Normal file
1
js/analytics.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
// Insert the analytics script
|
1
js/cookieconsent.min.js
vendored
Normal file
1
js/cookieconsent.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
js/cookieconsentinit.js
Normal file
11
js/cookieconsentinit.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
window.cookieconsent.initialise({
|
||||||
|
palette: {
|
||||||
|
popup: { background: "#007000" },
|
||||||
|
button: { background: "#e5e5e5" },
|
||||||
|
},
|
||||||
|
theme: "classic",
|
||||||
|
location: true,
|
||||||
|
content: {
|
||||||
|
href: "https://svrjs.org/privacy"
|
||||||
|
}
|
||||||
|
});
|
23
js/hamburger.js
Normal file
23
js/hamburger.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
var header = document.getElementById("header");
|
||||||
|
var hamburgerMenu = document.getElementById("header-hamburger");
|
||||||
|
var nav = document.getElementById("header-nav");
|
||||||
|
header.className += " header-js";
|
||||||
|
var className = header.className;
|
||||||
|
var oldHamburgerTabIndex = hamburgerMenu.tabIndex;
|
||||||
|
|
||||||
|
function toggleHamburger() {
|
||||||
|
if (header.className.match(/(?:^| )header-nav-opened( |$)/)) {
|
||||||
|
header.className = className;
|
||||||
|
document.documentElement.style.overflow = null;
|
||||||
|
hamburgerMenu.tabIndex = oldHamburgerTabIndex;
|
||||||
|
} else {
|
||||||
|
header.className += " header-nav-opened";
|
||||||
|
document.documentElement.style.overflow = "hidden";
|
||||||
|
hamburgerMenu.tabIndex = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hamburgerMenu.onclick = toggleHamburger;
|
||||||
|
hamburgerMenu.onkeydown = function (e) {
|
||||||
|
if (e.key == "Enter") toggleHamburger();
|
||||||
|
}
|
326
js/html5shiv.js
vendored
Normal file
326
js/html5shiv.js
vendored
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
/**
|
||||||
|
* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
|
||||||
|
*/
|
||||||
|
; (function (window, document) {
|
||||||
|
/*jshint evil:true */
|
||||||
|
/** version */
|
||||||
|
var version = '3.7.3';
|
||||||
|
|
||||||
|
/** Preset options */
|
||||||
|
var options = window.html5 || {};
|
||||||
|
|
||||||
|
/** Used to skip problem elements */
|
||||||
|
var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
|
||||||
|
|
||||||
|
/** Not all elements can be cloned in IE **/
|
||||||
|
var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
|
||||||
|
|
||||||
|
/** Detect whether the browser supports default html5 styles */
|
||||||
|
var supportsHtml5Styles;
|
||||||
|
|
||||||
|
/** Name of the expando, to work with multiple documents or to re-shiv one document */
|
||||||
|
var expando = '_html5shiv';
|
||||||
|
|
||||||
|
/** The id for the the documents expando */
|
||||||
|
var expanID = 0;
|
||||||
|
|
||||||
|
/** Cached data for each document */
|
||||||
|
var expandoData = {};
|
||||||
|
|
||||||
|
/** Detect whether the browser supports unknown elements */
|
||||||
|
var supportsUnknownElements;
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
var a = document.createElement('a');
|
||||||
|
a.innerHTML = '<xyz></xyz>';
|
||||||
|
//if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles
|
||||||
|
supportsHtml5Styles = ('hidden' in a);
|
||||||
|
|
||||||
|
supportsUnknownElements = a.childNodes.length == 1 || (function () {
|
||||||
|
// assign a false positive if unable to shiv
|
||||||
|
(document.createElement)('a');
|
||||||
|
var frag = document.createDocumentFragment();
|
||||||
|
return (
|
||||||
|
typeof frag.cloneNode == 'undefined' ||
|
||||||
|
typeof frag.createDocumentFragment == 'undefined' ||
|
||||||
|
typeof frag.createElement == 'undefined'
|
||||||
|
);
|
||||||
|
}());
|
||||||
|
} catch (e) {
|
||||||
|
// assign a false positive if detection fails => unable to shiv
|
||||||
|
supportsHtml5Styles = true;
|
||||||
|
supportsUnknownElements = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a style sheet with the given CSS text and adds it to the document.
|
||||||
|
* @private
|
||||||
|
* @param {Document} ownerDocument The document.
|
||||||
|
* @param {String} cssText The CSS text.
|
||||||
|
* @returns {StyleSheet} The style element.
|
||||||
|
*/
|
||||||
|
function addStyleSheet(ownerDocument, cssText) {
|
||||||
|
var p = ownerDocument.createElement('p'),
|
||||||
|
parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
|
||||||
|
|
||||||
|
p.innerHTML = 'x<style>' + cssText + '</style>';
|
||||||
|
return parent.insertBefore(p.lastChild, parent.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of `html5.elements` as an array.
|
||||||
|
* @private
|
||||||
|
* @returns {Array} An array of shived element node names.
|
||||||
|
*/
|
||||||
|
function getElements() {
|
||||||
|
var elements = html5.elements;
|
||||||
|
return typeof elements == 'string' ? elements.split(' ') : elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the built-in list of html5 elements
|
||||||
|
* @memberOf html5
|
||||||
|
* @param {String|Array} newElements whitespace separated list or array of new element names to shiv
|
||||||
|
* @param {Document} ownerDocument The context document.
|
||||||
|
*/
|
||||||
|
function addElements(newElements, ownerDocument) {
|
||||||
|
var elements = html5.elements;
|
||||||
|
if (typeof elements != 'string') {
|
||||||
|
elements = elements.join(' ');
|
||||||
|
}
|
||||||
|
if (typeof newElements != 'string') {
|
||||||
|
newElements = newElements.join(' ');
|
||||||
|
}
|
||||||
|
html5.elements = elements + ' ' + newElements;
|
||||||
|
shivDocument(ownerDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data associated to the given document
|
||||||
|
* @private
|
||||||
|
* @param {Document} ownerDocument The document.
|
||||||
|
* @returns {Object} An object of data.
|
||||||
|
*/
|
||||||
|
function getExpandoData(ownerDocument) {
|
||||||
|
var data = expandoData[ownerDocument[expando]];
|
||||||
|
if (!data) {
|
||||||
|
data = {};
|
||||||
|
expanID++;
|
||||||
|
ownerDocument[expando] = expanID;
|
||||||
|
expandoData[expanID] = data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a shived element for the given nodeName and document
|
||||||
|
* @memberOf html5
|
||||||
|
* @param {String} nodeName name of the element
|
||||||
|
* @param {Document|DocumentFragment} ownerDocument The context document.
|
||||||
|
* @returns {Object} The shived element.
|
||||||
|
*/
|
||||||
|
function createElement(nodeName, ownerDocument, data) {
|
||||||
|
if (!ownerDocument) {
|
||||||
|
ownerDocument = document;
|
||||||
|
}
|
||||||
|
if (supportsUnknownElements) {
|
||||||
|
return ownerDocument.createElement(nodeName);
|
||||||
|
}
|
||||||
|
if (!data) {
|
||||||
|
data = getExpandoData(ownerDocument);
|
||||||
|
}
|
||||||
|
var node;
|
||||||
|
|
||||||
|
if (data.cache[nodeName]) {
|
||||||
|
node = data.cache[nodeName].cloneNode();
|
||||||
|
} else if (saveClones.test(nodeName)) {
|
||||||
|
node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
|
||||||
|
} else {
|
||||||
|
node = data.createElem(nodeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid adding some elements to fragments in IE < 9 because
|
||||||
|
// * Attributes like `name` or `type` cannot be set/changed once an element
|
||||||
|
// is inserted into a document/fragment
|
||||||
|
// * Link elements with `src` attributes that are inaccessible, as with
|
||||||
|
// a 403 response, will cause the tab/window to crash
|
||||||
|
// * Script elements appended to fragments will execute when their `src`
|
||||||
|
// or `text` property is set
|
||||||
|
return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a shived DocumentFragment for the given document
|
||||||
|
* @memberOf html5
|
||||||
|
* @param {Document} ownerDocument The context document.
|
||||||
|
* @returns {Object} The shived DocumentFragment.
|
||||||
|
*/
|
||||||
|
function createDocumentFragment(ownerDocument, data) {
|
||||||
|
if (!ownerDocument) {
|
||||||
|
ownerDocument = document;
|
||||||
|
}
|
||||||
|
if (supportsUnknownElements) {
|
||||||
|
return ownerDocument.createDocumentFragment();
|
||||||
|
}
|
||||||
|
data = data || getExpandoData(ownerDocument);
|
||||||
|
var clone = data.frag.cloneNode(),
|
||||||
|
i = 0,
|
||||||
|
elems = getElements(),
|
||||||
|
l = elems.length;
|
||||||
|
for (; i < l; i++) {
|
||||||
|
clone.createElement(elems[i]);
|
||||||
|
}
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shivs the `createElement` and `createDocumentFragment` methods of the document.
|
||||||
|
* @private
|
||||||
|
* @param {Document|DocumentFragment} ownerDocument The document.
|
||||||
|
* @param {Object} data of the document.
|
||||||
|
*/
|
||||||
|
function shivMethods(ownerDocument, data) {
|
||||||
|
if (!data.cache) {
|
||||||
|
data.cache = {};
|
||||||
|
data.createElem = ownerDocument.createElement;
|
||||||
|
data.createFrag = ownerDocument.createDocumentFragment;
|
||||||
|
data.frag = data.createFrag();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ownerDocument.createElement = function (nodeName) {
|
||||||
|
//abort shiv
|
||||||
|
if (!html5.shivMethods) {
|
||||||
|
return data.createElem(nodeName);
|
||||||
|
}
|
||||||
|
return createElement(nodeName, ownerDocument, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
|
||||||
|
'var n=f.cloneNode(),c=n.createElement;' +
|
||||||
|
'h.shivMethods&&(' +
|
||||||
|
// unroll the `createElement` calls
|
||||||
|
getElements().join().replace(/[\w\-:]+/g, function (nodeName) {
|
||||||
|
data.createElem(nodeName);
|
||||||
|
data.frag.createElement(nodeName);
|
||||||
|
return 'c("' + nodeName + '")';
|
||||||
|
}) +
|
||||||
|
');return n}'
|
||||||
|
)(html5, data.frag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shivs the given document.
|
||||||
|
* @memberOf html5
|
||||||
|
* @param {Document} ownerDocument The document to shiv.
|
||||||
|
* @returns {Document} The shived document.
|
||||||
|
*/
|
||||||
|
function shivDocument(ownerDocument) {
|
||||||
|
if (!ownerDocument) {
|
||||||
|
ownerDocument = document;
|
||||||
|
}
|
||||||
|
var data = getExpandoData(ownerDocument);
|
||||||
|
|
||||||
|
if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
|
||||||
|
data.hasCSS = !!addStyleSheet(ownerDocument,
|
||||||
|
// corrects block display not defined in IE6/7/8/9
|
||||||
|
'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
|
||||||
|
// adds styling not present in IE6/7/8/9
|
||||||
|
'mark{background:#FF0;color:#000}' +
|
||||||
|
// hides non-rendered elements
|
||||||
|
'template{display:none}'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!supportsUnknownElements) {
|
||||||
|
shivMethods(ownerDocument, data);
|
||||||
|
}
|
||||||
|
return ownerDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `html5` object is exposed so that more elements can be shived and
|
||||||
|
* existing shiving can be detected on iframes.
|
||||||
|
* @type Object
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* // options can be changed before the script is included
|
||||||
|
* html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false };
|
||||||
|
*/
|
||||||
|
var html5 = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array or space separated string of node names of the elements to shiv.
|
||||||
|
* @memberOf html5
|
||||||
|
* @type Array|String
|
||||||
|
*/
|
||||||
|
'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* current version of html5shiv
|
||||||
|
*/
|
||||||
|
'version': version,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag to indicate that the HTML5 style sheet should be inserted.
|
||||||
|
* @memberOf html5
|
||||||
|
* @type Boolean
|
||||||
|
*/
|
||||||
|
'shivCSS': (options.shivCSS !== false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is equal to true if a browser supports creating unknown/HTML5 elements
|
||||||
|
* @memberOf html5
|
||||||
|
* @type boolean
|
||||||
|
*/
|
||||||
|
'supportsUnknownElements': supportsUnknownElements,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A flag to indicate that the document's `createElement` and `createDocumentFragment`
|
||||||
|
* methods should be overwritten.
|
||||||
|
* @memberOf html5
|
||||||
|
* @type Boolean
|
||||||
|
*/
|
||||||
|
'shivMethods': (options.shivMethods !== false),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string to describe the type of `html5` object ("default" or "default print").
|
||||||
|
* @memberOf html5
|
||||||
|
* @type String
|
||||||
|
*/
|
||||||
|
'type': 'default',
|
||||||
|
|
||||||
|
// shivs the document according to the specified `html5` object options
|
||||||
|
'shivDocument': shivDocument,
|
||||||
|
|
||||||
|
//creates a shived element
|
||||||
|
createElement: createElement,
|
||||||
|
|
||||||
|
//creates a shived documentFragment
|
||||||
|
createDocumentFragment: createDocumentFragment,
|
||||||
|
|
||||||
|
//extends list of elements
|
||||||
|
addElements: addElements
|
||||||
|
};
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
// expose html5
|
||||||
|
window.html5 = html5;
|
||||||
|
|
||||||
|
// shiv the document
|
||||||
|
shivDocument(document);
|
||||||
|
|
||||||
|
if (typeof module == 'object' && module.exports) {
|
||||||
|
module.exports = html5;
|
||||||
|
}
|
||||||
|
|
||||||
|
}(typeof window !== "undefined" ? window : this, document));
|
35
js/passwordStrength.js
Normal file
35
js/passwordStrength.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
document.getElementById('password').oninput = function () {
|
||||||
|
var password = this.value;
|
||||||
|
var strengthText = document.getElementById('password-strength');
|
||||||
|
var strength = getPasswordStrength(password);
|
||||||
|
|
||||||
|
strengthText.innerHTML = strength.text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
strengthText.className = strength.class;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getPasswordStrength(password) {
|
||||||
|
var strength = { text: '', class: '' };
|
||||||
|
|
||||||
|
if (password.length < 6) {
|
||||||
|
strength.text = 'Weak';
|
||||||
|
strength.class = 'password-weak';
|
||||||
|
} else if (password.length < 10) {
|
||||||
|
strength.text = 'Medium';
|
||||||
|
strength.class = 'password-medium';
|
||||||
|
} else {
|
||||||
|
var hasUpperCase = /[A-Z]/.test(password);
|
||||||
|
var hasLowerCase = /[a-z]/.test(password);
|
||||||
|
var hasNumbers = /\d/.test(password);
|
||||||
|
var hasSpecialChars = /[!@#$%^&*(),.?":{}|<>]/.test(password);
|
||||||
|
|
||||||
|
if (hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChars) {
|
||||||
|
strength.text = 'Strong';
|
||||||
|
strength.class = 'password-strong';
|
||||||
|
} else {
|
||||||
|
strength.text = 'Medium';
|
||||||
|
strength.class = 'password-medium';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strength;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue