| @@ -0,0 +1,387 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | znote_csrf_protect_public_post(); | |
| 3 | + | logged_in_redirect(); | |
| 4 | + | theme_open(); | |
| 5 | + | if (function_exists('tco_recovery_open')) { | |
| 6 | + | tco_recovery_open(); | |
| 7 | + | } | |
| 8 | + | if ($config['mailserver']['accountRecovery']) { | |
| 9 | + | // Fetch, sanitize and assign POST and GET variables. | |
| 10 | + | $mode = (isset($_GET['mode']) && !empty($_GET['mode'])) ? getValue($_GET['mode'] ?? null) : false; | |
| 11 | + | $email = (isset($_POST['email']) && !empty($_POST['email'])) ? getValue($_POST['email'] ?? null) : false; | |
| 12 | + | $character = (isset($_POST['character']) && !empty($_POST['character'])) ? getValue($_POST['character'] ?? null) : false; | |
| 13 | + | if (!$email && !empty($_POST['email_rcv'])) { | |
| 14 | + | $email = getValue($_POST['email_rcv'] ?? null); | |
| 15 | + | } | |
| 16 | + | if (!$character && !empty($_POST['nick'])) { | |
| 17 | + | $character = getValue($_POST['nick'] ?? null); | |
| 18 | + | } | |
| 19 | + | $password = (isset($_POST['password']) && !empty($_POST['password'])) ? getValue($_POST['password'] ?? null) : false; | |
| 20 | + | $username = (isset($_POST['username']) && !empty($_POST['username'])) ? getValue($_POST['username'] ?? null) : false; | |
| 21 | + | //data_dump($_GET, $_POST, "Posted data."); | |
| 22 | + | ||
| 23 | + | if (!empty($_POST)) { | |
| 24 | + | $status = true; | |
| 25 | + | if ($config['use_captcha']) { | |
| 26 | + | if(!verifyGoogleReCaptcha($_POST['g-recaptcha-response'])) { | |
| 27 | + | $status = false; | |
| 28 | + | } | |
| 29 | + | } | |
| 30 | + | if ($status) { | |
| 31 | + | if (isset($_POST['action_type'])) { | |
| 32 | + | $actionType = getValue($_POST['action_type'] ?? ''); | |
| 33 | + | ||
| 34 | + | if ($actionType === 'email') { | |
| 35 | + | $account = false; | |
| 36 | + | if ($email) { | |
| 37 | + | $account = db()->fetchOne(" | |
| 38 | + | SELECT `a`.`id`, `a`.`name`, `a`.`email`, `za`.`activekey` | |
| 39 | + | FROM `accounts` AS `a` | |
| 40 | + | INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` | |
| 41 | + | WHERE `a`.`email` = ? | |
| 42 | + | ORDER BY `a`.`id` ASC | |
| 43 | + | LIMIT 1; | |
| 44 | + | ", [$email]); | |
| 45 | + | } elseif ($character) { | |
| 46 | + | $account = db()->fetchOne(" | |
| 47 | + | SELECT `a`.`id`, `a`.`name`, `a`.`email`, `za`.`activekey` | |
| 48 | + | FROM `players` AS `p` | |
| 49 | + | INNER JOIN `accounts` AS `a` ON `p`.`account_id` = `a`.`id` | |
| 50 | + | INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` | |
| 51 | + | WHERE `p`.`name` = ? | |
| 52 | + | LIMIT 1; | |
| 53 | + | ", [$character]); | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | if (is_array($account) && !empty($account['email'])) { | |
| 57 | + | $recoverylink = $config['site_url'] . '/recovery.php?action=lostaccount_reset&a=' . (int)$account['id'] . '&k=' . (int)$account['activekey']; | |
| 58 | + | $mailer = new Mail($config['mailserver']); | |
| 59 | + | $title = t('recovery.mail_subject_lostaccount', ['host' => $_SERVER['HTTP_HOST']]); | |
| 60 | + | $body = '<h1>' . t('recovery.title') . '</h1>'; | |
| 61 | + | $body .= '<p>' . t('recovery.mail_lostaccount_intro') . '</p>'; | |
| 62 | + | $body .= '<p><a href="' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '" target="_BLANK">' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '</a></p>'; | |
| 63 | + | $body .= '<p>' . t('recovery.mail_ignore') . '</p>'; | |
| 64 | + | $body .= '<hr><p>' . t('recovery.mail_noreply') . '</p>'; | |
| 65 | + | $mailer->sendMail((string)$account['email'], $title, $body, (string)$account['name']); | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | ?> | |
| 69 | + | <h1><?= t('recovery.found') ?></h1> | |
| 70 | + | <p><?= t('recovery.sent_generic') ?></p> | |
| 71 | + | <?php | |
| 72 | + | } else { | |
| 73 | + | ?> | |
| 74 | + | <h1><?= t('recovery.title') ?></h1> | |
| 75 | + | <p><?= t('recovery.not_automated') ?></p> | |
| 76 | + | <?php | |
| 77 | + | } | |
| 78 | + | } elseif (!$username) { | |
| 79 | + | // Recover username | |
| 80 | + | $salt = ''; | |
| 81 | + | if (znote_server_adapter()->normalizedEngine() === 'TFS_03' && config('salt') === true) { | |
| 82 | + | $saltdata = db()->fetchOne( | |
| 83 | + | "SELECT `salt` FROM `accounts` WHERE `email` = ? LIMIT 1;", | |
| 84 | + | [$email] | |
| 85 | + | ); | |
| 86 | + | if ($saltdata !== false) $salt .= $saltdata['salt']; | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | if (znote_server_adapter()->accountIdentityColumn() !== 'id') | |
| 90 | + | $candidate = db()->fetchOne( | |
| 91 | + | "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`name`, `a`.`password` | |
| 92 | + | FROM `players` `p` | |
| 93 | + | INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id` | |
| 94 | + | WHERE `p`.`name` = ? AND `a`.`email` = ? | |
| 95 | + | LIMIT 1;", | |
| 96 | + | [$character, $email] | |
| 97 | + | ); | |
| 98 | + | else | |
| 99 | + | $candidate = db()->fetchOne( | |
| 100 | + | "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`id` AS `name`, `a`.`password` | |
| 101 | + | FROM `players` `p` | |
| 102 | + | INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id` | |
| 103 | + | WHERE `p`.`name` = ? AND `a`.`email` = ? | |
| 104 | + | LIMIT 1;", | |
| 105 | + | [$character, $email] | |
| 106 | + | ); | |
| 107 | + | ||
| 108 | + | $user = false; | |
| 109 | + | if ($candidate !== false && user_verify_login_password((int)$candidate['account_id'], (string)$password, (string)$candidate['password'], $salt)) { | |
| 110 | + | $user = $candidate; | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | if ($user !== false) { | |
| 114 | + | // Found user | |
| 115 | + | ||
| 116 | + | $mailer = new Mail($config['mailserver']); | |
| 117 | + | $title = t('recovery.mail_subject_username', ['host' => $_SERVER['HTTP_HOST']]); | |
| 118 | + | $body = '<h1>' . t('recovery.title') . '</h1>'; | |
| 119 | + | $body .= '<p>' . t('recovery.your_username2') . ' <b>' . htmlspecialchars((string)$user['name'], ENT_QUOTES, 'UTF-8') . '</b><br>'; | |
| 120 | + | $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>'; | |
| 121 | + | $body .= '<hr>' . t('recovery.mail_noreply') . '</p>'; | |
| 122 | + | $mailer->sendMail($email, $title, $body, $user['name']); | |
| 123 | + | ||
| 124 | + | ?> | |
| 125 | + | <h1><?= t('recovery.found') ?></h1> | |
| 126 | + | <p><?= t('recovery.sent_username2') ?> <b><?php echo $email; ?></b>.</p> | |
| 127 | + | <p><?= t('recovery.check_junk') ?></p> | |
| 128 | + | <?php | |
| 129 | + | } else { | |
| 130 | + | // Wrong submitted info | |
| 131 | + | ?> | |
| 132 | + | <h1><?= t('recovery.failed') ?></h1> | |
| 133 | + | <p><?= t('recovery.wrong_data') ?></p> | |
| 134 | + | <?php | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | } elseif (!$password) { | |
| 138 | + | // Recover password | |
| 139 | + | $newpass = rand(100000000, 999999999); | |
| 140 | + | $salt = ''; | |
| 141 | + | if (znote_server_adapter()->normalizedEngine() !== 'TFS_03') { | |
| 142 | + | // TFS 0.2 and 1.0 | |
| 143 | + | $password = sha1($newpass); | |
| 144 | + | } else { | |
| 145 | + | // TFS 0.3/4 | |
| 146 | + | if (config('salt') === true) { | |
| 147 | + | $saltdata = db()->fetchOne( | |
| 148 | + | "SELECT `salt` FROM `accounts` WHERE `email` = ? LIMIT 1;", | |
| 149 | + | [$email] | |
| 150 | + | ); | |
| 151 | + | if ($saltdata !== false) $salt .= $saltdata['salt']; | |
| 152 | + | } | |
| 153 | + | $password = sha1($salt.$newpass); | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | if (znote_server_adapter()->accountIdentityColumn() !== 'id') | |
| 157 | + | $user = db()->fetchOne( | |
| 158 | + | "SELECT `p`.`id` AS `player_id`, `a`.`name`, `a`.`id` AS `account_id` | |
| 159 | + | FROM `players` `p` | |
| 160 | + | INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id` | |
| 161 | + | WHERE `p`.`name` = ? AND `a`.`email` = ? AND `a`.`name` = ? | |
| 162 | + | LIMIT 1;", | |
| 163 | + | [$character, $email, $username] | |
| 164 | + | ); | |
| 165 | + | else | |
| 166 | + | $user = db()->fetchOne( | |
| 167 | + | "SELECT `p`.`id` AS `player_id`, `a`.`id` AS `account_id`, `a`.`id` AS `name` | |
| 168 | + | FROM `players` `p` | |
| 169 | + | INNER JOIN `accounts` `a` ON `p`.`account_id` = `a`.`id` | |
| 170 | + | WHERE `p`.`name` = ? AND `a`.`email` = ? AND `a`.`id` = ? | |
| 171 | + | LIMIT 1;", | |
| 172 | + | [$character, $email, $username] | |
| 173 | + | ); | |
| 174 | + | ||
| 175 | + | if ($user !== false) { | |
| 176 | + | // Found user | |
| 177 | + | // Give him the new password | |
| 178 | + | db()->execute( | |
| 179 | + | "UPDATE `accounts` SET `password` = ? WHERE `id` = ? LIMIT 1;", | |
| 180 | + | [$password, (int)$user['account_id']] | |
| 181 | + | ); | |
| 182 | + | user_set_website_password_hash((int)$user['account_id'], (string)$newpass); | |
| 183 | + | // Send him a mail with the new password | |
| 184 | + | $mailer = new Mail($config['mailserver']); | |
| 185 | + | $title = t('recovery.mail_subject_password', ['host' => $_SERVER['HTTP_HOST']]); | |
| 186 | + | $body = '<h1>' . t('recovery.title') . '</h1>'; | |
| 187 | + | $body .= '<p>' . t('recovery.new_password') . ' <b>' . htmlspecialchars((string)$newpass, ENT_QUOTES, 'UTF-8') . '</b><br>'; | |
| 188 | + | $body .= t('recovery.recommend_change_password') . ' <br>'; | |
| 189 | + | $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>'; | |
| 190 | + | $body .= '<hr>' . t('recovery.mail_noreply') . '</p>'; | |
| 191 | + | $mailer->sendMail($email, $title, $body, $user['name']); | |
| 192 | + | ?> | |
| 193 | + | <h1><?= t('recovery.found') ?></h1> | |
| 194 | + | <p><?= t('recovery.sent_password') ?> <b><?php echo $email; ?></b>.</p> | |
| 195 | + | <p><?= t('recovery.check_junk') ?></p> | |
| 196 | + | <?php | |
| 197 | + | } else { | |
| 198 | + | // Wrong submitted info | |
| 199 | + | ?> | |
| 200 | + | <h1><?= t('recovery.failed') ?></h1> | |
| 201 | + | <p><?= t('recovery.wrong_data') ?></p> | |
| 202 | + | <?php | |
| 203 | + | } | |
| 204 | + | } else { // Token | |
| 205 | + | $candidate = db()->fetchOne( | |
| 206 | + | "SELECT `a`.`id`, `a`.`name`, `a`.`password`, `za`.`activekey` | |
| 207 | + | FROM `accounts` AS `a` | |
| 208 | + | INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` | |
| 209 | + | WHERE `a`.`name` = ? AND `a`.`email` = ? | |
| 210 | + | LIMIT 1;", | |
| 211 | + | [$username, $email] | |
| 212 | + | ); | |
| 213 | + | $user = false; | |
| 214 | + | if ($candidate !== false && user_verify_login_password((int)$candidate['id'], (string)$password, (string)$candidate['password'])) { | |
| 215 | + | $user = $candidate; | |
| 216 | + | } | |
| 217 | + | if ($user !== false) { | |
| 218 | + | // Found user | |
| 219 | + | $recoverylink = $config['site_url'] . '/recovery.php?a='.$user['id'].'&k='.$user['activekey']; | |
| 220 | + | $mailer = new Mail($config['mailserver']); | |
| 221 | + | $title = $config['site_title'] . ': ' . t('recovery.remove_2fa') . ' link'; | |
| 222 | + | $body = '<h1>' . t('recovery.remove_2fa') . '</h1>'; | |
| 223 | + | $body .= '<p>' . t('recovery.remove_2fa_confirm_link') . '<br>'; | |
| 224 | + | $body .= '<a href="' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '" target="_BLANK">' . htmlspecialchars($recoverylink, ENT_QUOTES, 'UTF-8') . '</a><br>'; | |
| 225 | + | $body .= t('recovery.mail_enjoy_stay', ['site' => $config['mailserver']['fromName']]) . ' <br>'; | |
| 226 | + | $body .= '<hr>' . t('recovery.mail_noreply') . '</p>'; | |
| 227 | + | $mailer->sendMail($email, $title, $body, $user['name']); | |
| 228 | + | ?> | |
| 229 | + | <h1><?= t('recovery.confirm_email') ?></h1> | |
| 230 | + | <p><?= t('recovery.sent_link') ?> <b><?php echo $email; ?></b>.</p> | |
| 231 | + | <p><?= t('recovery.click_link') ?> <?= t('common.2fa') ?>.</p> | |
| 232 | + | <p><?= t('recovery.check_junk') ?></p> | |
| 233 | + | <?php | |
| 234 | + | } else { | |
| 235 | + | // Wrong submitted info | |
| 236 | + | ?> | |
| 237 | + | <h1><?= t('recovery.failed') ?></h1> | |
| 238 | + | <p><?= t('recovery.wrong_data') ?></p> | |
| 239 | + | <?php | |
| 240 | + | } | |
| 241 | + | ||
| 242 | + | ||
| 243 | + | } | |
| 244 | + | } else echo t('recovery.captcha_wrong'); | |
| 245 | + | } else { | |
| 246 | + | ||
| 247 | + | $recoveryAction = (isset($_GET['action']) && !empty($_GET['action'])) ? getValue($_GET['action'] ?? null) : false; | |
| 248 | + | $a = (isset($_GET['a']) && !empty($_GET['a'])) ? (int)$_GET['a'] : false; | |
| 249 | + | $k = (isset($_GET['k']) && !empty($_GET['k'])) ? (int)$_GET['k'] : false; | |
| 250 | + | ||
| 251 | + | // '. t('recovery.remove_2fa'). ' | |
| 252 | + | if ($a !== false && $k !== false && $recoveryAction === 'lostaccount_reset') { | |
| 253 | + | $account = db()->fetchOne( | |
| 254 | + | "SELECT `a`.`id`, `a`.`name`, `a`.`email` | |
| 255 | + | FROM `accounts` AS `a` | |
| 256 | + | INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` | |
| 257 | + | WHERE `a`.`id` = ? AND `za`.`activekey` = ? | |
| 258 | + | LIMIT 1;", | |
| 259 | + | [$a, $k] | |
| 260 | + | ); | |
| 261 | + | if ($account !== false && !empty($account['email'])) { | |
| 262 | + | $newpass = substr(sha1(random_bytes(32)), 0, 12); | |
| 263 | + | if (znote_server_adapter()->normalizedEngine() === 'TFS_03' && config('salt') === true) { | |
| 264 | + | user_change_password03((int)$account['id'], $newpass); | |
| 265 | + | } else { | |
| 266 | + | user_change_password((int)$account['id'], $newpass); | |
| 267 | + | } | |
| 268 | + | db()->execute("UPDATE `znote_accounts` SET `activekey` = ? WHERE `account_id` = ? LIMIT 1;", [rand(100000000, 999999999), (int)$account['id']]); | |
| 269 | + | ||
| 270 | + | $mailer = new Mail($config['mailserver']); | |
| 271 | + | $title = t('recovery.mail_subject_recovered', ['host' => $_SERVER['HTTP_HOST']]); | |
| 272 | + | $body = '<h1>' . t('recovery.title') . '</h1>'; | |
| 273 | + | $body .= '<p>' . t('recovery.your_username2') . ' <b>' . htmlspecialchars((string)$account['name'], ENT_QUOTES, 'UTF-8') . '</b><br>'; | |
| 274 | + | $body .= t('recovery.new_password') . ' <b>' . htmlspecialchars($newpass, ENT_QUOTES, 'UTF-8') . '</b></p>'; | |
| 275 | + | $body .= '<p>' . t('recovery.recommend_change_password_now') . '</p>'; | |
| 276 | + | $body .= '<hr><p>' . t('recovery.mail_noreply') . '</p>'; | |
| 277 | + | $mailer->sendMail((string)$account['email'], $title, $body, (string)$account['name']); | |
| 278 | + | ?> | |
| 279 | + | <h1><?= t('recovery.found') ?></h1> | |
| 280 | + | <p><?= t('recovery.sent_generic') ?></p> | |
| 281 | + | <?php | |
| 282 | + | } else { | |
| 283 | + | ?> | |
| 284 | + | <h1><?= t('recovery.verify_failed2') ?></h1> | |
| 285 | + | <p><?= t('recovery.cannot_auth') ?></p> | |
| 286 | + | <?php | |
| 287 | + | } | |
| 288 | + | } elseif ($a !== false && $k !== false && !engineIsCanary()) { | |
| 289 | + | $account = db()->fetchOne( | |
| 290 | + | "SELECT `a`.`id`, `a`.`secret`, `za`.`secret` | |
| 291 | + | FROM `accounts` AS `a` | |
| 292 | + | INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` | |
| 293 | + | WHERE `a`.`id` = ? AND `za`.`activekey` = ? | |
| 294 | + | LIMIT 1;", | |
| 295 | + | [$a, $k] | |
| 296 | + | ); | |
| 297 | + | if ($account !== false) { | |
| 298 | + | db()->execute("UPDATE `accounts` SET `secret` = NULL WHERE `id` = ? LIMIT 1;", [$a]); | |
| 299 | + | db()->execute("UPDATE `znote_accounts` SET `secret` = NULL WHERE `account_id` = ? LIMIT 1;", [$a]); | |
| 300 | + | ?> | |
| 301 | + | <h1><?= t('recovery.2fa_disabled') ?></h1> | |
| 302 | + | <p><?= t('recovery.2fa_disabled_text') ?></p> | |
| 303 | + | <?php | |
| 304 | + | } else { | |
| 305 | + | ?> | |
| 306 | + | <h1><?= t('recovery.verify_failed2') ?></h1> | |
| 307 | + | <p><?= t('recovery.cannot_auth') ?></p> | |
| 308 | + | <?php | |
| 309 | + | } | |
| 310 | + | } else { // Regular view | |
| 311 | + | ?> | |
| 312 | + | <h2><?= t('recovery.welcome_title') ?></h2> | |
| 313 | + | ||
| 314 | + | <p><?= t('recovery.intro_text') ?></p> | |
| 315 | + | ||
| 316 | + | <p><?= t('recovery.can_intro') ?></p> | |
| 317 | + | ||
| 318 | + | <ul class="CustomBulletPointList"> | |
| 319 | + | <li><?= t('recovery.can_new_password') ?></li> | |
| 320 | + | <li><?= t('recovery.can_hacked') ?></li> | |
| 321 | + | <li><?= t('recovery.can_change_email') ?></li> | |
| 322 | + | <li><?= t('recovery.can_new_key') ?></li> | |
| 323 | + | <li><?= t('recovery.can_remove_auth') ?></li> | |
| 324 | + | <li><?= t('recovery.can_disable_email_auth') ?></li> | |
| 325 | + | </ul> | |
| 326 | + | ||
| 327 | + | <p><?= t('recovery.first_step') ?></p> | |
| 328 | + | ||
| 329 | + | <?php | |
| 330 | + | if (in_array($mode, array('username', 'password', 'token'))) { | |
| 331 | + | ?> | |
| 332 | + | <form action="" method="POST"> | |
| 333 | + | <label for="email"><?= t('recovery.email_label') ?></label><input type="text" name="email" placeholder="name@mail.com"><br> | |
| 334 | + | <label for="<?= t('common.character') ?>"><?= t('common.label_character') ?> </label><input type="text" name="character"><br> | |
| 335 | + | <?php | |
| 336 | + | ||
| 337 | + | if ($mode === 'password') { | |
| 338 | + | echo '<label for="username">'. t('common.label_username2'). '</label> <input type="text" name="username"><br>'; | |
| 339 | + | } elseif ($mode === 'username') { | |
| 340 | + | echo '<label for="password">'. t('common.label_password') .'</label> <input type="password" name="password"><br>'; | |
| 341 | + | } elseif ($mode === 'token') { | |
| 342 | + | echo '<label for="username">'. t('common.label_username2') .'</label> <input type="text" name="username"><br>'; | |
| 343 | + | echo '<label for="password">'. t('common.label_password') .'</label> <input type="password" name="password"><br>'; | |
| 344 | + | } | |
| 345 | + | ||
| 346 | + | if ($config['use_captcha']) { | |
| 347 | + | ?> | |
| 348 | + | <div class="g-recaptcha" data-sitekey="<?php echo $config['captcha_site_key']; ?>"></div> | |
| 349 | + | <?php | |
| 350 | + | } | |
| 351 | + | ?> | |
| 352 | + | <input type="submit" value="<?= t('recovery.submit') ?>"> | |
| 353 | + | </form> | |
| 354 | + | <?php | |
| 355 | + | } else { | |
| 356 | + | ?> | |
| 357 | + | <?php if (function_exists('tco_panel_open')) { tco_panel_open(t('recovery.panel_title')); } ?> | |
| 358 | + | <form action="" method="post" class="lostaccount-form"> | |
| 359 | + | <input type="hidden" name="character" value=""> | |
| 360 | + | <div class="lostaccount-field-title"><?= t('recovery.field_character') ?></div> | |
| 361 | + | <input type="text" name="nick" size="40" autofocus> | |
| 362 | + | <div class="lostaccount-field-title"><?= t('recovery.field_email') ?></div> | |
| 363 | + | <input type="text" name="email_rcv" size="40"> | |
| 364 | + | <div class="lostaccount-field-title"><?= t('recovery.field_action') ?></div> | |
| 365 | + | <label class="lostaccount-option"><input type="radio" name="action_type" value="email" checked> <?= t('recovery.option_email') ?></label> | |
| 366 | + | <label class="lostaccount-option"><input type="radio" name="action_type" value="reckey"> <?= t('recovery.option_reckey') ?></label> | |
| 367 | + | <label class="lostaccount-option"><input type="radio" name="action_type" value="no_char"> <?= t('recovery.option_no_char') ?></label> | |
| 368 | + | <?php if ($config['use_captcha']) { ?> | |
| 369 | + | <div class="g-recaptcha" data-sitekey="<?php echo $config['captcha_site_key']; ?>"></div> | |
| 370 | + | <?php } ?> | |
| 371 | + | <input type="submit" value="<?= t('recovery.submit') ?>"> | |
| 372 | + | </form> | |
| 373 | + | <?php if (function_exists('tco_panel_close')) { tco_panel_close(); } ?> | |
| 374 | + | <?php | |
| 375 | + | } | |
| 376 | + | } | |
| 377 | + | } | |
| 378 | + | } else { | |
| 379 | + | ?> | |
| 380 | + | <h1><?= t('recovery.disabled') ?></h1> | |
| 381 | + | <p><?= t('recovery.disabled_text2') ?></p> | |
| 382 | + | <?php | |
| 383 | + | } | |
| 384 | + | if (function_exists('tco_recovery_close')) { | |
| 385 | + | tco_recovery_close(); | |
| 386 | + | } | |
| 387 | + | theme_close(); ?> |
| @@ -0,0 +1,158 @@ | |||
| 1 | + | <?php | |
| 2 | + | require_once 'engine/init.php'; | |
| 3 | + | logged_in_redirect(); | |
| 4 | + | theme_open(); | |
| 5 | + | require_once('config.countries.php'); | |
| 6 | + | ||
| 7 | + | if (empty($_POST) === false) { | |
| 8 | + | // $_POST[''] | |
| 9 | + | $required_fields = array('username', 'password', 'password_again', 'email', 'selected'); | |
| 10 | + | foreach($_POST as $key=>$value) { | |
| 11 | + | if (empty($value) && in_array($key, $required_fields) === true) { | |
| 12 | + | $errors[] = t('reg.fill_all'); | |
| 13 | + | break 1; | |
| 14 | + | } | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | // check errors (= user exist, pass long enough | |
| 18 | + | if (empty($errors) === true) { | |
| 19 | + | /* Token used for cross site scripting security */ | |
| 20 | + | if (!Token::isValid($_POST['token'] ?? null)) { | |
| 21 | + | $errors[] = t('login.token_invalid'); | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | if ($config['use_captcha']) { | |
| 25 | + | if(!verifyGoogleReCaptcha($_POST['g-recaptcha-response'])) { | |
| 26 | + | $errors[] = t('reg.captcha'); | |
| 27 | + | } | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | if (user_exist($_POST['username']) === true) { | |
| 31 | + | $errors[] = t('reg.name_taken'); | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | // Don't allow "default admin names in config.php" access to register. | |
| 35 | + | $isNoob = in_array(strtolower($_POST['username']), $config['page_admin_access']) ? true : false; | |
| 36 | + | if ($isNoob) { | |
| 37 | + | $errors[] = t('reg.name_blocked'); | |
| 38 | + | } | |
| 39 | + | if ($config['client'] >= 830) { | |
| 40 | + | if (preg_match("/^[a-zA-Z0-9]+$/", $_POST['username']) == false) { | |
| 41 | + | $errors[] = t('reg.name_chars'); | |
| 42 | + | } | |
| 43 | + | } else { | |
| 44 | + | if (preg_match("/^[0-9]+$/", $_POST['username']) == false) { | |
| 45 | + | $errors[] = t('reg.name_digits'); | |
| 46 | + | } | |
| 47 | + | if ((int)$_POST['username'] < 100000 || (int)$_POST['username'] > 999999999) { | |
| 48 | + | $errors[] = t('reg.name_length_num'); | |
| 49 | + | } | |
| 50 | + | } | |
| 51 | + | // name restriction | |
| 52 | + | $resname = explode(" ", $_POST['username']); | |
| 53 | + | foreach($resname as $res) { | |
| 54 | + | if(in_array(strtolower($res), $config['invalidNameTags'])) { | |
| 55 | + | $errors[] = t('reg.restricted_word'); | |
| 56 | + | } | |
| 57 | + | else if(strlen($res) == 1) { | |
| 58 | + | $errors[] = t('reg.words_too_short'); | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | if (strlen($_POST['username']) > 32) { | |
| 62 | + | $errors[] = t('reg.name_too_long'); | |
| 63 | + | } | |
| 64 | + | // end name restriction | |
| 65 | + | if (strlen($_POST['password']) < 6) { | |
| 66 | + | $errors[] = t('reg.pw_too_short'); | |
| 67 | + | } | |
| 68 | + | if (strlen($_POST['password']) > 29) { | |
| 69 | + | $errors[] = t('reg.pw_too_long'); | |
| 70 | + | } | |
| 71 | + | if ($_POST['password'] !== $_POST['password_again']) { | |
| 72 | + | $errors[] = t('reg.pw_mismatch'); | |
| 73 | + | } | |
| 74 | + | if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL) === false) { | |
| 75 | + | $errors[] = t('reg.email_invalid'); | |
| 76 | + | } | |
| 77 | + | if (user_email_exist($_POST['email']) === true) { | |
| 78 | + | $errors[] = t('reg.email_taken'); | |
| 79 | + | } | |
| 80 | + | if ($_POST['selected'] != 1) { | |
| 81 | + | $errors[] = t('reg.accept_rules'); | |
| 82 | + | } | |
| 83 | + | if ($config['validate_IP'] === true) { | |
| 84 | + | if (validate_ip(getIP()) === false) { | |
| 85 | + | $errors[] = t('reg.bad_ip'); | |
| 86 | + | } | |
| 87 | + | } | |
| 88 | + | if (strlen($_POST['flag']) < 1) { | |
| 89 | + | $errors[] = t('reg.choose_country'); | |
| 90 | + | } | |
| 91 | + | } | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | ?> | |
| 95 | + | <?php view('register_header'); ?> | |
| 96 | + | <?php | |
| 97 | + | if (isset($_GET['success']) && empty($_GET['success'])) { | |
| 98 | + | view('register_success', ['emailRequired' => (bool)$config['mailserver']['register']]); | |
| 99 | + | } elseif (isset($_GET['authenticate']) && empty($_GET['authenticate'])) { | |
| 100 | + | // Authenticate user, fetch user id and activation key | |
| 101 | + | $auid = (isset($_GET['u']) && (int)$_GET['u'] > 0) ? (int)$_GET['u'] : false; | |
| 102 | + | $akey = (isset($_GET['k']) && (int)$_GET['k'] > 0) ? (int)$_GET['k'] : false; | |
| 103 | + | // Find a match | |
| 104 | + | $user = db()->fetchOne("SELECT `id`, `active`, `active_email` FROM `znote_accounts` WHERE `account_id` = ? AND `activekey` = ? LIMIT 1;", [$auid, $akey]); | |
| 105 | + | if ($user !== false) { | |
| 106 | + | $userId = (int) $user['id']; | |
| 107 | + | $active = (int) $user['active']; | |
| 108 | + | $active_email = (int) $user['active_email']; | |
| 109 | + | // Enable the account to login | |
| 110 | + | if ($active == 0 || $active_email == 0) { | |
| 111 | + | db()->execute("UPDATE `znote_accounts` SET `active` = '1', `active_email` = '1' WHERE `id` = ? LIMIT 1;", [$userId]); | |
| 112 | + | } | |
| 113 | + | view('register_authenticate_result', ['ok' => true]); | |
| 114 | + | } else { | |
| 115 | + | view('register_authenticate_result', ['ok' => false]); | |
| 116 | + | } | |
| 117 | + | } else { | |
| 118 | + | if (empty($_POST) === false && empty($errors) === true) { | |
| 119 | + | if ($config['log_ip']) { | |
| 120 | + | znote_visitor_insert_detailed_data(1); | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | //Register | |
| 124 | + | $register_data = array( | |
| 125 | + | 'name' => $_POST['username'], | |
| 126 | + | 'password' => $_POST['password'], | |
| 127 | + | 'email' => $_POST['email'], | |
| 128 | + | 'created' => time(), | |
| 129 | + | 'ip' => getIPLong(), | |
| 130 | + | 'flag' => $_POST['flag'] | |
| 131 | + | ); | |
| 132 | + | ||
| 133 | + | $accountId = user_create_account($register_data, $config['mailserver']); | |
| 134 | + | ||
| 135 | + | $createPremiumDays = max(0, min(999, (int)($config['account_create_premdays'] ?? 0))); | |
| 136 | + | if ($accountId > 0 && $createPremiumDays > 0) { | |
| 137 | + | user_account_add_premdays($accountId, $createPremiumDays); | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | // Plugins can react to a new account: a welcome bonus, a webhook, an | |
| 141 | + | // entry in a referral ledger. | |
| 142 | + | znote_hook('account.registered', array( | |
| 143 | + | 'name' => $register_data['name'] ?? '', | |
| 144 | + | 'email' => $register_data['email'] ?? '', | |
| 145 | + | )); | |
| 146 | + | if (!$config['mailserver']['debug']) header('Location: register.php?success'); | |
| 147 | + | exit(); | |
| 148 | + | //End register | |
| 149 | + | ||
| 150 | + | } else if (empty($errors) === false){ | |
| 151 | + | echo '<font color="red"><b>'; | |
| 152 | + | echo output_errors($errors); | |
| 153 | + | echo '</b></font>'; | |
| 154 | + | } | |
| 155 | + | view('register_form'); | |
| 156 | + | } | |
| 157 | + | theme_close(); | |
| 158 | + | ?> |
| @@ -0,0 +1,50 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; theme_open(); | |
| 2 | + | // Calculate integer values into days, hours, minutes, seconds | |
| 3 | + | function toDuration($ms) { | |
| 4 | + | $duration['day'] = $ms / (24 * 60 * 60 * 1000); | |
| 5 | + | if (($duration['day'] - (int)$duration['day']) > 0) | |
| 6 | + | $duration['hour'] = ($duration['day'] - (int)$duration['day']) * 24; | |
| 7 | + | if (isset($duration['hour'])) { | |
| 8 | + | if (($duration['hour'] - (int)$duration['hour']) > 0) | |
| 9 | + | $duration['minute'] = ($duration['hour'] - (int)$duration['hour']) * 60; | |
| 10 | + | if (isset($duration['minute'])) { | |
| 11 | + | if (($duration['minute'] - (int)$duration['minute']) > 0) | |
| 12 | + | $duration['second'] = ($duration['minute'] - (int)$duration['minute']) * 60; | |
| 13 | + | } | |
| 14 | + | } | |
| 15 | + | $tmp = array(); | |
| 16 | + | foreach ($duration as $type => $value) { | |
| 17 | + | if ($value >= 1) { | |
| 18 | + | $pluralType = ((int)$value === 1) ? $type : $type . 's'; | |
| 19 | + | if ($type !== 'second') $tmp[] = (int)$value . " $pluralType"; | |
| 20 | + | else $tmp[] = $value . " $pluralType"; | |
| 21 | + | } | |
| 22 | + | } | |
| 23 | + | return implode(', ', $tmp); | |
| 24 | + | } | |
| 25 | + | function toYesNo($bool) { | |
| 26 | + | return ($bool) ? 'Yes' : 'No'; | |
| 27 | + | } | |
| 28 | + | $serverAdmin = (user_logged_in() && is_admin($user_data)); | |
| 29 | + | $showStagesForm = false; | |
| 30 | + | $showConfigForm = false; | |
| 31 | + | $stagesUpdated = false; | |
| 32 | + | $stagesFailed = false; | |
| 33 | + | ||
| 34 | + | $stagesData = serverdata_load('stages'); | |
| 35 | + | if ($stagesData === false && is_file(serverdata_file('stages.xml'))) { | |
| 36 | + | serverdata_rebuild('stages'); | |
| 37 | + | $stagesData = serverdata_load('stages'); | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | $luaConfig = serverdata_load('config'); | |
| 41 | + | ||
| 42 | + | $stages = false; | |
| 43 | + | ||
| 44 | + | view('serverinfo'); | |
| 45 | + | ||
| 46 | + | if (!minimap_was_rendered()) { | |
| 47 | + | minimap_render(); | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | theme_close(); |
| @@ -0,0 +1,67 @@ | |||
| 1 | + | <?php | |
| 2 | + | require_once 'engine/init.php'; | |
| 3 | + | protect_page(); | |
| 4 | + | theme_open(); | |
| 5 | + | require_once('config.countries.php'); | |
| 6 | + | ||
| 7 | + | if (empty($_POST) === false) { | |
| 8 | + | // $_POST[''] | |
| 9 | + | /* Token used for cross site scripting security */ | |
| 10 | + | if (!Token::isValid($_POST['token'])) { | |
| 11 | + | $errors[] = t('login.token_invalid'); | |
| 12 | + | } | |
| 13 | + | $required_fields = array('new_email', 'new_flag'); | |
| 14 | + | foreach($_POST as $key=>$value) { | |
| 15 | + | if (empty($value) && in_array($key, $required_fields) === true) { | |
| 16 | + | $errors[] = t('reg.fill_all'); | |
| 17 | + | break 1; | |
| 18 | + | } | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | if (empty($errors) === true) { | |
| 22 | + | if (filter_var($_POST['new_email'], FILTER_VALIDATE_EMAIL) === false) { | |
| 23 | + | $errors[] = t('reg.email_invalid'); | |
| 24 | + | } else if (user_email_exist($_POST['new_email']) === true && $user_data['email'] !== $_POST['new_email']) { | |
| 25 | + | $errors[] = t('reg.email_taken'); | |
| 26 | + | } | |
| 27 | + | } | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | /** | |
| 31 | + | * What the view has to render: 'success', 'errors' or 'form'. | |
| 32 | + | * The account write stays here - a theme must never carry it. | |
| 33 | + | */ | |
| 34 | + | $formState = 'form'; | |
| 35 | + | ||
| 36 | + | if (isset($_GET['success']) === true && empty($_GET['success']) === true) { | |
| 37 | + | $formState = 'success'; | |
| 38 | + | ||
| 39 | + | } elseif (empty($_POST) === false && empty($errors) === true) { | |
| 40 | + | ||
| 41 | + | $update_data = array( | |
| 42 | + | 'email' => $_POST['new_email'] | |
| 43 | + | ); | |
| 44 | + | ||
| 45 | + | $update_znote_data = array( | |
| 46 | + | 'flag' => getValue($_POST['new_flag'] ?? null), | |
| 47 | + | 'active_email' => '0' | |
| 48 | + | ); | |
| 49 | + | ||
| 50 | + | // If the address was previously verified, take back the bonus points. | |
| 51 | + | if ($user_znote_data['active_email'] > 0) { | |
| 52 | + | $update_znote_data['points'] = $user_znote_data['points'] - $config['mailserver']['verify_email_points']; | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | user_update_account($update_data); | |
| 56 | + | user_update_znote_account($update_znote_data); | |
| 57 | + | ||
| 58 | + | header('Location: settings.php?success'); | |
| 59 | + | exit; | |
| 60 | + | ||
| 61 | + | } elseif (empty($errors) === false) { | |
| 62 | + | $formState = 'errors'; | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | view('settings'); | |
| 66 | + | ||
| 67 | + | theme_close(); |
| @@ -0,0 +1,188 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | znote_csrf_protect_public_post(); | |
| 3 | + | theme_open(); | |
| 4 | + | ||
| 5 | + | if (isset($_GET['callback']) && $_GET['callback'] === 'processing') { | |
| 6 | + | echo '<script>alert(' . json_encode(t('shop.payment_processing')) . ');</script>'; | |
| 7 | + | } | |
| 8 | + | ||
| 9 | + | // Import from config: | |
| 10 | + | $shop = $config['shop']; | |
| 11 | + | if ($shop['loginToView'] === true) protect_page(); | |
| 12 | + | $loggedin = user_logged_in(); | |
| 13 | + | ||
| 14 | + | function shop_db_offer_columns(): array { | |
| 15 | + | $columns = array(); | |
| 16 | + | $rows = db()->fetchAll("SHOW COLUMNS FROM `znote_shop_offers`;"); | |
| 17 | + | ||
| 18 | + | if (is_array($rows)) { | |
| 19 | + | foreach ($rows as $row) { | |
| 20 | + | if (!empty($row['Field'])) { | |
| 21 | + | $columns[(string)$row['Field']] = true; | |
| 22 | + | } | |
| 23 | + | } | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | return $columns; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | function shop_load_db_offers(): array { | |
| 30 | + | $columns = shop_db_offer_columns(); | |
| 31 | + | $where = !empty($columns['active']) ? "WHERE `active` = 1" : ""; | |
| 32 | + | $order = !empty($columns['sort_order']) | |
| 33 | + | ? "ORDER BY `sort_order` ASC, `id` ASC" | |
| 34 | + | : "ORDER BY `id` ASC"; | |
| 35 | + | ||
| 36 | + | $rows = db()->fetchAll(" | |
| 37 | + | SELECT `id`, `type`, `itemid`, `count`, `description`, `points` | |
| 38 | + | FROM `znote_shop_offers` | |
| 39 | + | {$where} | |
| 40 | + | {$order}; | |
| 41 | + | "); | |
| 42 | + | ||
| 43 | + | if (!is_array($rows)) { | |
| 44 | + | return array(); | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | $offers = array(); | |
| 48 | + | foreach ($rows as $row) { | |
| 49 | + | $itemid = (int)$row['itemid']; | |
| 50 | + | if ((int)$row['type'] === 5 && $itemid > 0) { | |
| 51 | + | $male = (int)floor($itemid / 10000); | |
| 52 | + | $female = (int)($itemid % 10000); | |
| 53 | + | $itemid = array($male, $female); | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | $offers[(int)$row['id']] = array( | |
| 57 | + | 'type' => (int)$row['type'], | |
| 58 | + | 'itemid' => $itemid, | |
| 59 | + | 'count' => (int)$row['count'], | |
| 60 | + | 'description' => (string)$row['description'], | |
| 61 | + | 'points' => (int)$row['points'], | |
| 62 | + | ); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | return $offers; | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | $shop_list = shop_load_db_offers(); | |
| 69 | + | ||
| 70 | + | if ($loggedin === true) { | |
| 71 | + | $postedShopSession = (string)($_POST['session'] ?? ''); | |
| 72 | + | $storedShopSession = (string)($_SESSION['shop_session'] ?? ''); | |
| 73 | + | if (!empty($_POST['buy']) && $postedShopSession !== '' && $storedShopSession !== '' && hash_equals($storedShopSession, $postedShopSession)) { | |
| 74 | + | unset($_SESSION['shop_session']); | |
| 75 | + | $time = time(); | |
| 76 | + | $cid = (int)$user_data['id']; | |
| 77 | + | // Sanitizing post, setting default buy value | |
| 78 | + | $buy = false; | |
| 79 | + | $post = (int)$_POST['buy']; | |
| 80 | + | ||
| 81 | + | foreach ($shop_list as $key => $value) { | |
| 82 | + | if ($key === $post) { | |
| 83 | + | $buy = $value; | |
| 84 | + | } | |
| 85 | + | } | |
| 86 | + | if ($buy === false) die("Error: Shop offer ID mismatch."); | |
| 87 | + | ||
| 88 | + | // Plugins may adjust what this offer costs - a discount code, a happy | |
| 89 | + | // hour, a loyalty rebate. The filtered value is what gets checked, | |
| 90 | + | // charged and logged, so the three can never disagree. | |
| 91 | + | $buy['points'] = max(0, (int)znote_hook_filter('shop.price', (int)$buy['points'], array( | |
| 92 | + | 'account_id' => $cid, | |
| 93 | + | 'offer_id' => $post, | |
| 94 | + | 'offer' => $buy, | |
| 95 | + | ))); | |
| 96 | + | ||
| 97 | + | // If this is an outfit offer, convert array into an integer. | |
| 98 | + | if ($buy['type'] == 5) { | |
| 99 | + | if (is_array($buy['itemid'])) { | |
| 100 | + | if (COUNT($buy['itemid']) == 2) $buy['itemid'] = ($buy['itemid'][0] * 1000) + $buy['itemid'][1]; | |
| 101 | + | else $buy['itemid'] = $buy['itemid'][0]; | |
| 102 | + | } | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | $db = db(); | |
| 106 | + | if (!$db->beginTransaction()) { | |
| 107 | + | die("Failed to start shop transaction."); | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | $data = $db->fetchOne("SELECT `points` FROM `znote_accounts` WHERE `account_id` = ? LIMIT 1 FOR UPDATE;", [$cid]); | |
| 111 | + | if (!$data) { | |
| 112 | + | $db->rollback(); | |
| 113 | + | die("0: Account is not converted to work with Znote AAC"); | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | $old_points = (int)$data['points']; | |
| 117 | + | if ($old_points < $buy['points']) { | |
| 118 | + | $db->rollback(); | |
| 119 | + | echo '<font color="red" size="4">You need more points, this offer cost '.$buy['points'].' points.</font>'; | |
| 120 | + | } else { | |
| 121 | + | $expense_points = (int)$buy['points']; | |
| 122 | + | $orderReady = true; | |
| 123 | + | ||
| 124 | + | if (!$db->execute( | |
| 125 | + | "UPDATE `znote_accounts` SET `points` = `points` - ? WHERE `account_id` = ? AND `points` >= ?;", | |
| 126 | + | [$expense_points, $cid, $expense_points] | |
| 127 | + | )) { | |
| 128 | + | $orderReady = false; | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | // Do the magic (insert into db, or change sex etc) | |
| 132 | + | // If type is 2 or 3 | |
| 133 | + | if ($orderReady && $buy['type'] == 2) { | |
| 134 | + | // Add premium days to account | |
| 135 | + | $orderReady = user_account_add_premdays($cid, $buy['count']); | |
| 136 | + | $successMessage = '<font color="green" size="4">You now have '.$buy['count'].' additional days of premium membership.</font>'; | |
| 137 | + | } else if ($orderReady) { | |
| 138 | + | $orderReady = $db->execute( | |
| 139 | + | "INSERT INTO `znote_shop_orders` (`account_id`, `type`, `itemid`, `count`, `time`) VALUES (?, ?, ?, ?, ?);", | |
| 140 | + | [$cid, (int)$buy['type'], (int)$buy['itemid'], (int)$buy['count'], $time] | |
| 141 | + | ); | |
| 142 | + | ||
| 143 | + | if ($buy['type'] == 3) { | |
| 144 | + | $successMessage = '<font color="green" size="4">'. t('shop.gender_unlocked') .'</font>'; | |
| 145 | + | } else if ($buy['type'] == 4) { | |
| 146 | + | $successMessage = '<font color="green" size="4">'. t('shop.name_unlocked') .'</font>'; | |
| 147 | + | } else { | |
| 148 | + | $successMessage = '<font color="green" size="4">Your order is ready to be delivered. Write this command in-game to get it: [!shop].<br>Make sure you are in depot and can carry it before executing the command!</font>'; | |
| 149 | + | } | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | if ($orderReady) { | |
| 153 | + | $orderReady = $db->execute( | |
| 154 | + | "INSERT INTO `znote_shop_logs` (`account_id`, `player_id`, `type`, `itemid`, `count`, `points`, `time`) VALUES (?, 0, ?, ?, ?, ?, ?);", | |
| 155 | + | [$cid, (int)$buy['type'], (int)$buy['itemid'], (int)$buy['count'], (int)$buy['points'], $time] | |
| 156 | + | ); | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | if ($orderReady) { | |
| 160 | + | $db->commit(); | |
| 161 | + | $user_znote_data['points'] = $old_points - $expense_points; | |
| 162 | + | echo $successMessage; | |
| 163 | + | ||
| 164 | + | // Plugins can react to a purchase - a coupon ledger, a Discord message, | |
| 165 | + | // a loyalty counter. They cannot change what was bought; this is a | |
| 166 | + | // notification, fired after the points have already been taken. | |
| 167 | + | znote_hook('shop.purchased', array( | |
| 168 | + | 'account_id' => $cid, | |
| 169 | + | 'offer_id' => $post, | |
| 170 | + | 'type' => $buy['type'], | |
| 171 | + | 'itemid' => $buy['itemid'], | |
| 172 | + | 'count' => $buy['count'], | |
| 173 | + | 'points' => $buy['points'], | |
| 174 | + | )); | |
| 175 | + | $buy['points'] = 0; | |
| 176 | + | } else { | |
| 177 | + | $db->rollback(); | |
| 178 | + | echo '<font color="red" size="4">Shop purchase failed. Please try again or contact staff.</font>'; | |
| 179 | + | } | |
| 180 | + | } | |
| 181 | + | //var_dump($buy); | |
| 182 | + | //echo '<font color="red" size="4">'. $_POST['buy'] .'</font>'; | |
| 183 | + | } | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | view('shop'); | |
| 187 | + | ||
| 188 | + | theme_close(); |
| @@ -0,0 +1,3 @@ | |||
| 1 | + | order deny,allow | |
| 2 | + | deny from all | |
| 3 | + | allow from 127.0.0.1 localhost ::1 |
| @@ -0,0 +1,32 @@ | |||
| 1 | + | <?php | |
| 2 | + | require '../config.php'; | |
| 3 | + | require '../engine/database/connect.php'; | |
| 4 | + | ?> | |
| 5 | + | ||
| 6 | + | <h1>Gesior and Modern shop points to Znote AAC shop points</h1> | |
| 7 | + | <p>Convert donation/shop points from previous Gesior/Modern installation to Znote AAC:</p> | |
| 8 | + | <?php | |
| 9 | + | $accounts = db()->fetchAll("SELECT `id`, `premium_points` FROM `accounts` WHERE `premium_points` > 0;"); | |
| 10 | + | $accountids = array(); | |
| 11 | + | foreach ($accounts as $acc) $accountids[] = $acc['id']; | |
| 12 | + | ||
| 13 | + | if ($accounts !== false) echo "<p>Detected: ". count($accounts) ." accounts who have points in old system.</p>"; | |
| 14 | + | else die("<h1>All accounts already converted. :)</h1>"); | |
| 15 | + | ||
| 16 | + | $placeholders = implode(',', array_fill(0, count($accountids), '?')); | |
| 17 | + | $znote_accounts = db()->fetchAll("SELECT `account_id`, `points` FROM `znote_accounts` WHERE `account_id` IN ($placeholders);", $accountids); | |
| 18 | + | ||
| 19 | + | if (count($accounts) !== count($znote_accounts)) die("<h1><font color='red'>Failed to syncronize accounts. You need to convert all accounts to Znote AAC first!</font></h1>"); | |
| 20 | + | ||
| 21 | + | // Order old accounts by id. | |
| 22 | + | $idaccounts = array(); | |
| 23 | + | foreach ($accounts as $acc) { | |
| 24 | + | $idaccounts[$acc['id']] = $acc['premium_points']; | |
| 25 | + | } | |
| 26 | + | foreach ($znote_accounts as $acc) { | |
| 27 | + | db()->execute("UPDATE `znote_accounts` SET `points` = ? WHERE `account_id` = ? LIMIT 1;", [$acc['points'] + $idaccounts[$acc['account_id']], $acc['account_id']]); | |
| 28 | + | } | |
| 29 | + | db()->execute("UPDATE `accounts` SET `premium_points` = 0;"); | |
| 30 | + | ||
| 31 | + | echo "<h1><font color='green'>Successfully converted all points!</font></h1>"; | |
| 32 | + | ?> |
| @@ -0,0 +1,153 @@ | |||
| 1 | + | <?php | |
| 2 | + | require '../config.php'; | |
| 3 | + | require '../engine/database/connect.php'; | |
| 4 | + | require '../engine/function/general.php'; | |
| 5 | + | require '../engine/function/users.php'; | |
| 6 | + | ?> | |
| 7 | + | ||
| 8 | + | <h1>Old database to Znote AAC compatibility converter:</h1> | |
| 9 | + | <p>Converting accounts and characters to work with Znote AAC:</p> | |
| 10 | + | <?php | |
| 11 | + | // some variables | |
| 12 | + | $updated_acc = 0; | |
| 13 | + | // $updated_acc += 1; | |
| 14 | + | $updated_char = 0; | |
| 15 | + | // $updated_char += 1; | |
| 16 | + | $updated_pass = 0; | |
| 17 | + | ||
| 18 | + | // install functions | |
| 19 | + | function fetch_all_accounts() { | |
| 20 | + | $results = db()->fetchAll("SELECT `id` FROM `accounts`"); | |
| 21 | + | $accounts = array(); | |
| 22 | + | foreach ($results as $row) { | |
| 23 | + | $accounts[] = $row['id']; | |
| 24 | + | } | |
| 25 | + | return (count($accounts) > 0) ? $accounts : false; | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | function user_count_znote_accounts() { | |
| 29 | + | $data = db()->fetchOne("SELECT COUNT(`account_id`) AS `count` from `znote_accounts`;"); | |
| 30 | + | return ($data !== false) ? $data['count'] : 0; | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | function user_character_is_compatible($pid) { | |
| 34 | + | $data = db()->fetchOne("SELECT COUNT(`player_id`) AS `count` from `znote_players` WHERE `player_id` = ?;", [$pid]); | |
| 35 | + | return ($data !== false) ? $data['count'] : 0; | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | function fetch_znote_accounts() { | |
| 39 | + | $results = db()->fetchAll("SELECT `account_id` FROM `znote_accounts`"); | |
| 40 | + | $accounts = array(); | |
| 41 | + | foreach ($results as $row) { | |
| 42 | + | $accounts[] = $row['account_id']; | |
| 43 | + | } | |
| 44 | + | return (count($accounts) > 0) ? $accounts : false; | |
| 45 | + | } | |
| 46 | + | // end install functions | |
| 47 | + | ||
| 48 | + | // count all accounts, znote accounts, find out which accounts needs to be converted. | |
| 49 | + | $all_account = fetch_all_accounts(); | |
| 50 | + | $znote_account = fetch_znote_accounts(); | |
| 51 | + | if ($all_account !== false) { | |
| 52 | + | if ($znote_account !== false) { // If existing znote compatible account exists: | |
| 53 | + | foreach ($all_account as $all) { // Loop through every element in znote_account array | |
| 54 | + | if (!in_array($all, $znote_account)) { | |
| 55 | + | $old_accounts[] = $all; | |
| 56 | + | } | |
| 57 | + | } | |
| 58 | + | } else { | |
| 59 | + | foreach ($all_account as $all) { | |
| 60 | + | $old_accounts[] = $all; | |
| 61 | + | } | |
| 62 | + | } | |
| 63 | + | } | |
| 64 | + | // end ^ | |
| 65 | + | ||
| 66 | + | // Send count status | |
| 67 | + | if (isset($all_account) && $all_account !== false) { | |
| 68 | + | echo '<br>'; | |
| 69 | + | echo 'Total accounts detected: '. count($all_account) .'.'; | |
| 70 | + | ||
| 71 | + | if (isset($znote_account) && $znote_account !== false) { | |
| 72 | + | echo '<br>'; | |
| 73 | + | echo 'Znote compatible accounts detected: '. count($znote_account) .'.'; | |
| 74 | + | ||
| 75 | + | if (isset($old_accounts)) { | |
| 76 | + | echo '<br>'; | |
| 77 | + | echo 'Old accounts detected: '. count($old_accounts) .'.'; | |
| 78 | + | } | |
| 79 | + | } else { | |
| 80 | + | echo '<br>'; | |
| 81 | + | echo 'Znote compatible accounts detected: 0.'; | |
| 82 | + | } | |
| 83 | + | echo '<br>'; | |
| 84 | + | echo '<br>'; | |
| 85 | + | } else { | |
| 86 | + | echo '<br>'; | |
| 87 | + | echo 'Total accounts detected: 0.'; | |
| 88 | + | } | |
| 89 | + | // end count status | |
| 90 | + | ||
| 91 | + | // validate accounts | |
| 92 | + | if (isset($old_accounts) && $old_accounts !== false) { | |
| 93 | + | $time = time(); | |
| 94 | + | foreach ($old_accounts as $old) { | |
| 95 | + | ||
| 96 | + | // Make acc data compatible: | |
| 97 | + | db()->execute("INSERT INTO `znote_accounts` (`account_id`, `ip`, `created`, `flag`) VALUES (?, 0, ?, '')", [$old, $time]); | |
| 98 | + | $updated_acc += 1; | |
| 99 | + | ||
| 100 | + | // Fetch unsalted password | |
| 101 | + | if ($config['ServerEngine'] == 'TFS_03' && $config['salt'] === true) { | |
| 102 | + | $password = user_data($old, 'password', 'salt'); | |
| 103 | + | $p_pass = str_replace($password['salt'],"",$password['password']); | |
| 104 | + | } | |
| 105 | + | if ($config['ServerEngine'] == 'TFS_02' || $config['salt'] === false) { | |
| 106 | + | $password = user_data($old, 'password'); | |
| 107 | + | $p_pass = $password['password']; | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | // Verify lenght of password is less than 28 characters (most likely a plain password) | |
| 111 | + | if (strlen($p_pass) < 28 && $old > 1) { | |
| 112 | + | // encrypt it with sha1 | |
| 113 | + | if ($config['ServerEngine'] == 'TFS_02' || $config['salt'] === false) $p_pass = sha1($p_pass); | |
| 114 | + | if ($config['ServerEngine'] == 'TFS_03' && $config['salt'] === true) $p_pass = sha1($password['salt'].$p_pass); | |
| 115 | + | ||
| 116 | + | // Update their password so they are sha1 encrypted | |
| 117 | + | db()->execute("UPDATE `accounts` SET `password` = ? WHERE `id` = ?;", [$p_pass, $old]); | |
| 118 | + | $updated_pass += 1; | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | } | |
| 122 | + | } | |
| 123 | + | ||
| 124 | + | // validate players | |
| 125 | + | if ($all_account !== false) { | |
| 126 | + | $time = time(); | |
| 127 | + | foreach ($all_account as $all) { | |
| 128 | + | ||
| 129 | + | $chars = user_character_list_player_id($all); | |
| 130 | + | if ($chars !== false) { | |
| 131 | + | // since char list is not false, we found a character list | |
| 132 | + | ||
| 133 | + | // Lets loop through the character list | |
| 134 | + | foreach ($chars as $c) { | |
| 135 | + | // Is character not compatible yet? | |
| 136 | + | if (user_character_is_compatible($c['id']) == 0) { | |
| 137 | + | // Then lets make it compatible: | |
| 138 | + | $cid = $c['id']; | |
| 139 | + | db()->execute("INSERT INTO `znote_players` (`player_id`, `created`, `hide_char`, `comment`) VALUES (?, ?, 0, '')", [$cid, $time]); | |
| 140 | + | $updated_char += 1; | |
| 141 | + | ||
| 142 | + | } | |
| 143 | + | } | |
| 144 | + | } | |
| 145 | + | } | |
| 146 | + | } | |
| 147 | + | ||
| 148 | + | echo "<br><b><font color=\"green\">SUCCESS</font></b><br><br>"; | |
| 149 | + | echo 'Updated accounts: '. $updated_acc .'<br>'; | |
| 150 | + | echo 'Updated characters: : '. $updated_char .'<br>'; | |
| 151 | + | echo 'Detected:'. $updated_pass .' accounts with plain passwords. These passwords has been given sha1 encryption.<br>'; | |
| 152 | + | echo '<br>All accounts and characters are compatible with Znote AAC<br>'; | |
| 153 | + | ?> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | Milestone - What I wish to add to Znote AAC in the future. (Znote AAC TODO/wish list). | |
| 2 | + | - Character auction page for donation points. | |
| 3 | + | - Semi-live communication with OT. | |
| 4 | + | - Live ban, kick, broadcast message, open/close server and Custom commands. | |
| 5 | + | - Sub-page system. |
| @@ -0,0 +1,70 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | ||
| 3 | + | /* PLAYER SKILLS REPAIR SCRIPT IF YOU SOMEHOW DELETE PLAYER SKILLS | |
| 4 | + | --------------------------------------------------------------- | |
| 5 | + | Place in root web directory, login to admin account, | |
| 6 | + | and enter site.com/repairSkills.php (with big S). | |
| 7 | + | */ | |
| 8 | + | ||
| 9 | + | protect_page(); | |
| 10 | + | admin_only($user_data); | |
| 11 | + | znote_csrf_protect_public_post(); | |
| 12 | + | ||
| 13 | + | if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') { | |
| 14 | + | ?> | |
| 15 | + | <h1>Repair missing player skills</h1> | |
| 16 | + | <p>This operation scans every player and inserts default rows where skills are missing.</p> | |
| 17 | + | <form method="post" onsubmit="return confirm('Repair missing skills for every affected player?');"> | |
| 18 | + | <button type="submit">Run repair</button> | |
| 19 | + | </form> | |
| 20 | + | <?php | |
| 21 | + | exit; | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | $Splayers = 0; | |
| 25 | + | $Salready = 0; | |
| 26 | + | $Sfixed = 0; | |
| 27 | + | ||
| 28 | + | $players = db()->fetchAll("SELECT `id` FROM `players`;"); | |
| 29 | + | if ($players !== false) { | |
| 30 | + | $Splayers = count($players); | |
| 31 | + | foreach ($players as $char) { | |
| 32 | + | ||
| 33 | + | // Check if player have skills | |
| 34 | + | $skills = db()->fetchOne("SELECT `value` FROM `player_skills` WHERE `player_id` = ? AND `skillid` = 2 LIMIT 1;", [$char['id']]); | |
| 35 | + | ||
| 36 | + | // If he dont have any skills | |
| 37 | + | if ($skills === false) { | |
| 38 | + | $Sfixed++; | |
| 39 | + | ||
| 40 | + | // Loop through every skill id and give him default skills. | |
| 41 | + | $rows = array(); | |
| 42 | + | $params = array(); | |
| 43 | + | for ($i = 0; $i < 7; $i++) { | |
| 44 | + | $rows[] = '(?, ?, 10, 0)'; | |
| 45 | + | $params[] = $char['id']; | |
| 46 | + | $params[] = $i; | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | db()->execute( | |
| 50 | + | "INSERT INTO `player_skills` (`player_id`, `skillid`, `value`, `count`) VALUES " . implode(', ', $rows) . ";", | |
| 51 | + | $params | |
| 52 | + | ); | |
| 53 | + | } else $Salready++; | |
| 54 | + | } | |
| 55 | + | acp_log('maintenance.repair_skills', 'ALL', ['players' => $Splayers, 'repaired' => $Sfixed]); | |
| 56 | + | ?> | |
| 57 | + | <h1>Script run status:</h1> | |
| 58 | + | <p>Players detected: <?php echo $Splayers; ?></p> | |
| 59 | + | <p>Players already fixed: <?php echo $Salready; ?></p> | |
| 60 | + | <p><b>Repaired player accounts: <?php echo $Sfixed; ?></b></p> | |
| 61 | + | <?php | |
| 62 | + | } else { | |
| 63 | + | ?> | |
| 64 | + | <h1>No players detected.</h1> | |
| 65 | + | <p>Something went wrong.</p> | |
| 66 | + | <?php | |
| 67 | + | } | |
| 68 | + | ?> | |
| 69 | + | ||
| 70 | + | <h1>Script run completed.</h1> |
| @@ -0,0 +1,25 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; theme_open(); | |
| 2 | + | ||
| 3 | + | /** | |
| 4 | + | * Spell list. | |
| 5 | + | * | |
| 6 | + | * spells.xml is uploaded and parsed in the admin panel, under Server Info. This | |
| 7 | + | * page only reads the result: | |
| 8 | + | * $spells the spell list, or false when nothing was published | |
| 9 | + | * $showSpellsForm, $spellsUpdated, $spellsFailed kept at false for older themes | |
| 10 | + | */ | |
| 11 | + | ||
| 12 | + | $showSpellsForm = false; | |
| 13 | + | $spellsUpdated = false; | |
| 14 | + | $spellsFailed = false; | |
| 15 | + | ||
| 16 | + | $spells = serverdata_load('spells'); | |
| 17 | + | ||
| 18 | + | if ($spells === false && is_file(serverdata_file('spells.xml'))) { | |
| 19 | + | serverdata_rebuild('spells'); | |
| 20 | + | $spells = serverdata_load('spells'); | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | view('spells'); | |
| 24 | + | ||
| 25 | + | theme_close(); |
| @@ -0,0 +1,28 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.0 - admin action log | |
| 3 | + | -- | |
| 4 | + | -- Only needed for databases created BEFORE this feature. A fresh import of | |
| 5 | + | -- SQL/znote_schema.sql already contains the table. | |
| 6 | + | -- | |
| 7 | + | -- Records mutating actions taken from the admin panel (bans, skill edits, | |
| 8 | + | -- points, settings changes, plugin lifecycle, etc). Written by acp_log() in | |
| 9 | + | -- engine/function/adminlog.php, read from Admin Panel > Admin Log. | |
| 10 | + | -- | |
| 11 | + | -- Run once: | |
| 12 | + | -- mysql -u <user> -p <database> < SQL/migrations/2.0.0_admin_log.sql | |
| 13 | + | -- --------------------------------------------------------------------------- | |
| 14 | + | ||
| 15 | + | CREATE TABLE IF NOT EXISTS `znote_admin_log` ( | |
| 16 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 17 | + | `admin_id` int NOT NULL DEFAULT '0', | |
| 18 | + | `admin_name` varchar(50) NOT NULL DEFAULT '', | |
| 19 | + | `action` varchar(64) NOT NULL, | |
| 20 | + | `target` varchar(191) NOT NULL DEFAULT '', | |
| 21 | + | `details` text NOT NULL, | |
| 22 | + | `ip` varchar(45) NOT NULL DEFAULT '', | |
| 23 | + | `created` int NOT NULL, | |
| 24 | + | PRIMARY KEY (`id`), | |
| 25 | + | KEY `admin_created` (`admin_id`, `created`), | |
| 26 | + | KEY `action_created` (`action`, `created`), | |
| 27 | + | KEY `created` (`created`) | |
| 28 | + | ) ENGINE=InnoDB; |
| @@ -0,0 +1,24 @@ | |||
| 1 | + | -- Merge duplicate forum boards. | |
| 2 | + | -- | |
| 3 | + | -- Older schema seeds (and the OT -> ZnoteX converter) could INSERT the default | |
| 4 | + | -- boards more than once, leaving two "Discussion", two "Staff Board", etc. This | |
| 5 | + | -- keeps the lowest id for each (name, guild_id), moves every thread onto it and | |
| 6 | + | -- deletes the emptied duplicates. Safe to run more than once. | |
| 7 | + | ||
| 8 | + | UPDATE `znote_forum_threads` `t` | |
| 9 | + | JOIN `znote_forum` `dup` ON `dup`.`id` = `t`.`forum_id` | |
| 10 | + | JOIN ( | |
| 11 | + | SELECT `name`, `guild_id`, MIN(`id`) AS `keep_id` | |
| 12 | + | FROM `znote_forum` | |
| 13 | + | GROUP BY `name`, `guild_id` | |
| 14 | + | ) `k` ON `k`.`name` = `dup`.`name` AND `k`.`guild_id` = `dup`.`guild_id` | |
| 15 | + | SET `t`.`forum_id` = `k`.`keep_id` | |
| 16 | + | WHERE `t`.`forum_id` <> `k`.`keep_id`; | |
| 17 | + | ||
| 18 | + | DELETE `f` FROM `znote_forum` `f` | |
| 19 | + | JOIN ( | |
| 20 | + | SELECT `name`, `guild_id`, MIN(`id`) AS `keep_id` | |
| 21 | + | FROM `znote_forum` | |
| 22 | + | GROUP BY `name`, `guild_id` | |
| 23 | + | ) `k` ON `k`.`name` = `f`.`name` AND `k`.`guild_id` = `f`.`guild_id` | |
| 24 | + | WHERE `f`.`id` <> `k`.`keep_id`; |
| @@ -0,0 +1,4 @@ | |||
| 1 | + | -- Allows imported MyAAC/Gesior gallery URLs to fit without truncation. | |
| 2 | + | ||
| 3 | + | ALTER TABLE `znote_images` | |
| 4 | + | MODIFY `image` varchar(255) NOT NULL; |
| @@ -0,0 +1,96 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.0 - navigation menus | |
| 3 | + | -- | |
| 4 | + | -- Only needed for databases created BEFORE this feature. A fresh import of | |
| 5 | + | -- SQL/znote_schema.sql already contains the table and its default entries. | |
| 6 | + | -- | |
| 7 | + | -- Menu links used to be hardcoded in every theme, which meant editing PHP to | |
| 8 | + | -- add a link and duplicating the whole menu in each theme. They live here now | |
| 9 | + | -- and are edited from Admin Panel > Menus. | |
| 10 | + | -- | |
| 11 | + | -- Run once: | |
| 12 | + | -- mysql -u <user> -p <database> < SQL/migrations/2.0.0_menus.sql | |
| 13 | + | -- --------------------------------------------------------------------------- | |
| 14 | + | ||
| 15 | + | -- Navigation entries, managed from Admin Panel > Menus. | |
| 16 | + | -- A theme declares the locations it renders (see layouts/README.md); entries | |
| 17 | + | -- are grouped by that location and ordered by sort_order. | |
| 18 | + | CREATE TABLE IF NOT EXISTS `znote_menu` ( | |
| 19 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 20 | + | `location` varchar(32) NOT NULL COMMENT 'Theme-declared slot: main, sidebar, footer...', | |
| 21 | + | `parent_id` int NOT NULL DEFAULT '0' COMMENT '0 = top level, else the id of the parent entry', | |
| 22 | + | `label` varchar(64) NOT NULL, | |
| 23 | + | `url` varchar(255) NOT NULL, | |
| 24 | + | `icon` varchar(48) NOT NULL DEFAULT '' COMMENT 'Optional Font Awesome class', | |
| 25 | + | `target` varchar(10) NOT NULL DEFAULT '', | |
| 26 | + | `visibility` varchar(10) NOT NULL DEFAULT 'all' COMMENT 'all, guest, user or admin', | |
| 27 | + | `sort_order` int NOT NULL DEFAULT '0', | |
| 28 | + | `active` tinyint NOT NULL DEFAULT '1', | |
| 29 | + | PRIMARY KEY (`id`), | |
| 30 | + | KEY `loc_sort` (`location`, `active`, `sort_order`, `id`) | |
| 31 | + | ) ENGINE=InnoDB; | |
| 32 | + | ||
| 33 | + | INSERT INTO `znote_menu` (`location`, `parent_id`, `label`, `url`, `icon`, `visibility`, `sort_order`) | |
| 34 | + | SELECT `seed`.`location`, `seed`.`parent_id`, `seed`.`label`, `seed`.`url`, `seed`.`icon`, `seed`.`visibility`, `seed`.`sort_order` | |
| 35 | + | FROM ( | |
| 36 | + | SELECT 'main' AS `location`, 0 AS `parent_id`, 'Home' AS `label`, 'index.php' AS `url`, 'fa-home' AS `icon`, 'all' AS `visibility`, 10 AS `sort_order` | |
| 37 | + | UNION ALL SELECT 'main', 0, 'Changelog', 'changelog.php', '', 'all', 20 | |
| 38 | + | UNION ALL SELECT 'main', 0, 'Account', 'myaccount.php', 'fa-user-circle', 'user', 30 | |
| 39 | + | UNION ALL SELECT 'main', 0, 'Login', 'login.php', 'fa-user-circle', 'guest', 30 | |
| 40 | + | UNION ALL SELECT 'main', 0, 'Register', 'register.php', 'fa-key', 'guest', 40 | |
| 41 | + | UNION ALL SELECT 'main', 0, 'Downloads', 'downloads.php', '', 'all', 50 | |
| 42 | + | UNION ALL SELECT 'main', 0, 'Community', 'onlinelist.php', 'fa-users', 'all', 60 | |
| 43 | + | UNION ALL SELECT 'main', 0, 'Highscores', 'highscores.php', '', 'all', 70 | |
| 44 | + | UNION ALL SELECT 'main', 0, 'Guilds', 'guilds.php', '', 'all', 80 | |
| 45 | + | UNION ALL SELECT 'main', 0, 'Forum', 'forum.php', '', 'all', 90 | |
| 46 | + | UNION ALL SELECT 'main', 0, 'Houses', 'houses.php', '', 'all', 100 | |
| 47 | + | UNION ALL SELECT 'main', 0, 'Latest deaths', 'deaths.php', '', 'all', 110 | |
| 48 | + | UNION ALL SELECT 'main', 0, 'Kill statistics', 'killers.php', '', 'all', 120 | |
| 49 | + | UNION ALL SELECT 'main', 0, 'Bans', 'bans.php', '', 'all', 125 | |
| 50 | + | UNION ALL SELECT 'main', 0, 'Creatures', 'creatures.php', '', 'all', 135 | |
| 51 | + | UNION ALL SELECT 'main', 0, 'Library', 'serverinfo.php', 'fa-book', 'all', 130 | |
| 52 | + | UNION ALL SELECT 'main', 0, 'Spells', 'spells.php', '', 'all', 140 | |
| 53 | + | UNION ALL SELECT 'main', 0, 'Support', 'support.php', 'fa-info-circle', 'all', 150 | |
| 54 | + | UNION ALL SELECT 'main', 0, 'Helpdesk', 'helpdesk.php', '', 'all', 160 | |
| 55 | + | UNION ALL SELECT 'main', 0, 'Shop', 'shop.php', 'fa-shopping-cart', 'all', 170 | |
| 56 | + | UNION ALL SELECT 'main', 0, 'Buy points', 'buypoints.php', '', 'all', 180 | |
| 57 | + | UNION ALL SELECT 'main', 0, 'Admin Panel', 'admin/index.php', 'fa-sliders', 'admin', 190 | |
| 58 | + | ) `seed` | |
| 59 | + | LEFT JOIN `znote_menu` `existing` | |
| 60 | + | ON `existing`.`location` = `seed`.`location` | |
| 61 | + | AND `existing`.`parent_id` = 0 | |
| 62 | + | AND `existing`.`label` = `seed`.`label` | |
| 63 | + | WHERE `existing`.`id` IS NULL; | |
| 64 | + | ||
| 65 | + | -- Nest the sub-entries under their section. Done as a second pass because the | |
| 66 | + | -- parent ids are only known once the rows above exist. | |
| 67 | + | UPDATE `znote_menu` `c` | |
| 68 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Home' | |
| 69 | + | SET `c`.`parent_id` = `p`.`id` | |
| 70 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Changelog'); | |
| 71 | + | ||
| 72 | + | UPDATE `znote_menu` `c` | |
| 73 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Account' | |
| 74 | + | SET `c`.`parent_id` = `p`.`id` | |
| 75 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Downloads'); | |
| 76 | + | ||
| 77 | + | UPDATE `znote_menu` `c` | |
| 78 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Community' | |
| 79 | + | SET `c`.`parent_id` = `p`.`id` | |
| 80 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Highscores','Guilds','Forum','Houses','Latest deaths','Kill statistics','Bans'); | |
| 81 | + | ||
| 82 | + | UPDATE `znote_menu` `c` | |
| 83 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Library' | |
| 84 | + | SET `c`.`parent_id` = `p`.`id` | |
| 85 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Spells','Creatures'); | |
| 86 | + | ||
| 87 | + | UPDATE `znote_menu` `c` | |
| 88 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Support' | |
| 89 | + | SET `c`.`parent_id` = `p`.`id` | |
| 90 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Helpdesk'); | |
| 91 | + | ||
| 92 | + | UPDATE `znote_menu` `c` | |
| 93 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Shop' | |
| 94 | + | SET `c`.`parent_id` = `p`.`id` | |
| 95 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Buy points'); | |
| 96 | + |
| @@ -0,0 +1,66 @@ | |||
| 1 | + | -- Adds database-backed pages and legacy conversion mapping tables. | |
| 2 | + | -- Safe to run more than once. | |
| 3 | + | ||
| 4 | + | CREATE TABLE IF NOT EXISTS `znote_pages` ( | |
| 5 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 6 | + | `slug` varchar(64) NOT NULL, | |
| 7 | + | `title` varchar(100) NOT NULL, | |
| 8 | + | `body` mediumtext NOT NULL, | |
| 9 | + | `created` int NOT NULL DEFAULT '0', | |
| 10 | + | `updated` int NOT NULL DEFAULT '0', | |
| 11 | + | `player_id` int NOT NULL DEFAULT '0', | |
| 12 | + | `access` tinyint NOT NULL DEFAULT '0', | |
| 13 | + | `active` tinyint NOT NULL DEFAULT '1', | |
| 14 | + | PRIMARY KEY (`id`), | |
| 15 | + | UNIQUE KEY `slug` (`slug`) | |
| 16 | + | ) ENGINE=InnoDB; | |
| 17 | + | ||
| 18 | + | CREATE TABLE IF NOT EXISTS `znote_convert_map` ( | |
| 19 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 20 | + | `source` varchar(32) NOT NULL, | |
| 21 | + | `source_table` varchar(64) NOT NULL, | |
| 22 | + | `source_id` varchar(64) NOT NULL, | |
| 23 | + | `target_table` varchar(64) NOT NULL, | |
| 24 | + | `target_id` int NOT NULL, | |
| 25 | + | `created` int NOT NULL, | |
| 26 | + | PRIMARY KEY (`id`), | |
| 27 | + | UNIQUE KEY `source_row` (`source`, `source_table`, `source_id`, `target_table`) | |
| 28 | + | ) ENGINE=InnoDB; | |
| 29 | + | ||
| 30 | + | CREATE TABLE IF NOT EXISTS `znote_legacy_tables` ( | |
| 31 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 32 | + | `source` varchar(32) NOT NULL, | |
| 33 | + | `table_name` varchar(64) NOT NULL, | |
| 34 | + | `schema_sql` longtext NOT NULL, | |
| 35 | + | `row_count` int NOT NULL DEFAULT '0', | |
| 36 | + | `captured` int NOT NULL, | |
| 37 | + | PRIMARY KEY (`id`), | |
| 38 | + | UNIQUE KEY `source_table` (`source`, `table_name`) | |
| 39 | + | ) ENGINE=InnoDB; | |
| 40 | + | ||
| 41 | + | SET @znote_add_schema_sql := IF( | |
| 42 | + | ( | |
| 43 | + | SELECT COUNT(*) | |
| 44 | + | FROM INFORMATION_SCHEMA.COLUMNS | |
| 45 | + | WHERE TABLE_SCHEMA = DATABASE() | |
| 46 | + | AND TABLE_NAME = 'znote_legacy_tables' | |
| 47 | + | AND COLUMN_NAME = 'schema_sql' | |
| 48 | + | ) = 0, | |
| 49 | + | 'ALTER TABLE `znote_legacy_tables` ADD `schema_sql` longtext NULL AFTER `table_name`', | |
| 50 | + | 'SELECT 1' | |
| 51 | + | ); | |
| 52 | + | PREPARE znote_add_schema_sql_stmt FROM @znote_add_schema_sql; | |
| 53 | + | EXECUTE znote_add_schema_sql_stmt; | |
| 54 | + | DEALLOCATE PREPARE znote_add_schema_sql_stmt; | |
| 55 | + | ||
| 56 | + | CREATE TABLE IF NOT EXISTS `znote_legacy_rows` ( | |
| 57 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 58 | + | `source` varchar(32) NOT NULL, | |
| 59 | + | `table_name` varchar(64) NOT NULL, | |
| 60 | + | `source_pk` varchar(128) NOT NULL DEFAULT '', | |
| 61 | + | `row_json` longtext NOT NULL, | |
| 62 | + | `captured` int NOT NULL, | |
| 63 | + | PRIMARY KEY (`id`), | |
| 64 | + | KEY `source_table` (`source`, `table_name`), | |
| 65 | + | KEY `source_pk` (`source`, `table_name`, `source_pk`) | |
| 66 | + | ) ENGINE=InnoDB; |
| @@ -0,0 +1,38 @@ | |||
| 1 | + | -- Stripe / Mercado Pago transaction ledger. | |
| 2 | + | -- Safe to run more than once. | |
| 3 | + | ||
| 4 | + | CREATE TABLE IF NOT EXISTS `znote_payment_transactions` ( | |
| 5 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 6 | + | `provider` varchar(32) NOT NULL, | |
| 7 | + | `reference` varchar(128) NOT NULL, | |
| 8 | + | `provider_reference` varchar(128) DEFAULT NULL, | |
| 9 | + | `account_id` int NOT NULL, | |
| 10 | + | `price` decimal(11,2) NOT NULL, | |
| 11 | + | `currency` varchar(8) NOT NULL, | |
| 12 | + | `points` int NOT NULL, | |
| 13 | + | `status` varchar(32) NOT NULL DEFAULT 'pending', | |
| 14 | + | `credited` tinyint NOT NULL DEFAULT '0', | |
| 15 | + | `test_mode` tinyint NOT NULL DEFAULT '0', | |
| 16 | + | `created_at` int NOT NULL, | |
| 17 | + | `updated_at` int NOT NULL, | |
| 18 | + | `credited_at` int DEFAULT NULL, | |
| 19 | + | `payload` longtext, | |
| 20 | + | PRIMARY KEY (`id`), | |
| 21 | + | UNIQUE KEY `provider_reference_internal` (`provider`, `reference`), | |
| 22 | + | KEY `provider_reference_external` (`provider`, `provider_reference`), | |
| 23 | + | KEY `account_status` (`account_id`, `status`, `created_at`) | |
| 24 | + | ) ENGINE=InnoDB; | |
| 25 | + | ||
| 26 | + | CREATE TABLE IF NOT EXISTS `znote_payment_events` ( | |
| 27 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 28 | + | `provider` varchar(32) NOT NULL, | |
| 29 | + | `event_id` varchar(128) NOT NULL, | |
| 30 | + | `provider_reference` varchar(128) DEFAULT NULL, | |
| 31 | + | `payment_reference` varchar(128) DEFAULT NULL, | |
| 32 | + | `status` varchar(32) NOT NULL DEFAULT 'received', | |
| 33 | + | `payload` longtext, | |
| 34 | + | `received_at` int NOT NULL, | |
| 35 | + | PRIMARY KEY (`id`), | |
| 36 | + | UNIQUE KEY `provider_event` (`provider`, `event_id`), | |
| 37 | + | KEY `payment_reference` (`provider`, `payment_reference`) | |
| 38 | + | ) ENGINE=InnoDB; |
| @@ -0,0 +1,25 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.0 - settings table (needed by the layout system) | |
| 3 | + | -- | |
| 4 | + | -- Only needed for databases created BEFORE 2.0.0. A fresh import of | |
| 5 | + | -- SQL/znote_schema.sql already contains this table. | |
| 6 | + | -- | |
| 7 | + | -- Key/value settings written from the admin panel. The active layout is the | |
| 8 | + | -- first user of it; anything else the panel should be able to change without | |
| 9 | + | -- editing config.php belongs here too. | |
| 10 | + | -- | |
| 11 | + | -- Run once: | |
| 12 | + | -- mysql -u <user> -p <database> < SQL/migrations/2.0.0_znote_config.sql | |
| 13 | + | -- --------------------------------------------------------------------------- | |
| 14 | + | ||
| 15 | + | CREATE TABLE IF NOT EXISTS `znote_config` ( | |
| 16 | + | `key` varchar(64) NOT NULL, | |
| 17 | + | `value` text NOT NULL, | |
| 18 | + | PRIMARY KEY (`key`) | |
| 19 | + | ) ENGINE=InnoDB; | |
| 20 | + | ||
| 21 | + | -- Existing sites keep the look they have: 'default' is the current Snavy | |
| 22 | + | -- layout, moved from layout/ to layouts/default/. | |
| 23 | + | INSERT INTO `znote_config` (`key`, `value`) VALUES | |
| 24 | + | ('layout', 'default') | |
| 25 | + | ON DUPLICATE KEY UPDATE `key` = `key`; |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | -- Website-only password modernization: accounts.password (SHA1, read by the | |
| 2 | + | -- game server) is never touched. This column holds a modern password_hash() | |
| 3 | + | -- value used only to verify website logins; it is filled in lazily as each | |
| 4 | + | -- account logs in or changes its password. | |
| 5 | + | -- | |
| 6 | + | -- Only needed for databases created BEFORE 2.0.1. A fresh import of | |
| 7 | + | -- SQL/znote_schema.sql already contains this column. | |
| 8 | + | ||
| 9 | + | ALTER TABLE `znote_accounts` | |
| 10 | + | ADD COLUMN `password_hash` varchar(255) DEFAULT NULL AFTER `secret`; |
| @@ -0,0 +1,39 @@ | |||
| 1 | + | -- ZnoteX 2.0.1 - convert ZnoteX-owned tables to full UTF-8. | |
| 2 | + | ||
| 3 | + | SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; | |
| 4 | + | ||
| 5 | + | ALTER TABLE `znote` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 6 | + | ALTER TABLE `znote_accounts` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 7 | + | ALTER TABLE `znote_news` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 8 | + | ALTER TABLE `znote_images` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 9 | + | ALTER TABLE `znote_paypal` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 10 | + | ALTER TABLE `znote_paygol` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 11 | + | ALTER TABLE `znote_pagseguro` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 12 | + | ALTER TABLE `znote_pagseguro_notifications` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 13 | + | ALTER TABLE `znote_payment_transactions` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 14 | + | ALTER TABLE `znote_payment_events` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 15 | + | ALTER TABLE `znote_players` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 16 | + | ALTER TABLE `znote_player_reports` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 17 | + | ALTER TABLE `znote_changelog` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 18 | + | ALTER TABLE `znote_shop` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 19 | + | ALTER TABLE `znote_shop_offers` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 20 | + | ALTER TABLE `znote_shop_logs` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 21 | + | ALTER TABLE `znote_shop_orders` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 22 | + | ALTER TABLE `znote_admin_log` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 23 | + | ALTER TABLE `znote_config` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 24 | + | ALTER TABLE `znote_menu` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 25 | + | ALTER TABLE `znote_pages` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 26 | + | ALTER TABLE `znote_convert_map` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 27 | + | ALTER TABLE `znote_legacy_tables` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 28 | + | ALTER TABLE `znote_legacy_rows` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 29 | + | ALTER TABLE `znote_visitors` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 30 | + | ALTER TABLE `znote_visitors_details` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 31 | + | ALTER TABLE `znote_forum` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 32 | + | ALTER TABLE `znote_forum_threads` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 33 | + | ALTER TABLE `znote_forum_posts` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 34 | + | ALTER TABLE `znote_deleted_characters` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 35 | + | ALTER TABLE `znote_guild_wars` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 36 | + | ALTER TABLE `znote_tickets` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 37 | + | ALTER TABLE `znote_tickets_replies` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 38 | + | ALTER TABLE `znote_global_storage` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; | |
| 39 | + | ALTER TABLE `znote_auction_player` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; |
| @@ -0,0 +1,40 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.2 - Website 2FA v2 | |
| 3 | + | -- | |
| 4 | + | -- Independent of the game engine: nothing here touches `accounts`, so it works | |
| 5 | + | -- the same on TFS, Canary, otHire or BlackTek. The old TFS-tied system | |
| 6 | + | -- (accounts.secret, znote_accounts.secret) is untouched and keeps working. | |
| 7 | + | -- --------------------------------------------------------------------------- | |
| 8 | + | ||
| 9 | + | CREATE TABLE IF NOT EXISTS `znote_2fa` ( | |
| 10 | + | `account_id` int NOT NULL, | |
| 11 | + | `totp_secret` varchar(64) DEFAULT NULL, | |
| 12 | + | `totp_enabled` tinyint(1) NOT NULL DEFAULT '0', | |
| 13 | + | `email_otp_enabled` tinyint(1) NOT NULL DEFAULT '0', | |
| 14 | + | `recovery_codes` text, | |
| 15 | + | `session_version` int NOT NULL DEFAULT '1', | |
| 16 | + | `updated_at` int NOT NULL DEFAULT '0', | |
| 17 | + | PRIMARY KEY (`account_id`) | |
| 18 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 19 | + | ||
| 20 | + | CREATE TABLE IF NOT EXISTS `znote_2fa_email_codes` ( | |
| 21 | + | `account_id` int NOT NULL, | |
| 22 | + | `code_hash` varchar(64) NOT NULL, | |
| 23 | + | `expires_at` int NOT NULL, | |
| 24 | + | `attempts` int NOT NULL DEFAULT '0', | |
| 25 | + | PRIMARY KEY (`account_id`) | |
| 26 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 27 | + | ||
| 28 | + | CREATE TABLE IF NOT EXISTS `znote_2fa_trusted_devices` ( | |
| 29 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 30 | + | `account_id` int NOT NULL, | |
| 31 | + | `token_hash` varchar(64) NOT NULL, | |
| 32 | + | `label` varchar(255) NOT NULL DEFAULT '', | |
| 33 | + | `ip` varchar(45) NOT NULL DEFAULT '', | |
| 34 | + | `created_at` int NOT NULL, | |
| 35 | + | `expires_at` int NOT NULL, | |
| 36 | + | `last_used_at` int NOT NULL DEFAULT '0', | |
| 37 | + | PRIMARY KEY (`id`), | |
| 38 | + | UNIQUE KEY `token_hash` (`token_hash`), | |
| 39 | + | KEY `account_id` (`account_id`) | |
| 40 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; |
| @@ -0,0 +1,18 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.3 - Login attempt tracking / IP lockout | |
| 3 | + | -- | |
| 4 | + | -- Every login attempt (success or failure) is logged by IP. Admin Panel > | |
| 5 | + | -- Security > Login Protection reads this to lock an IP out after too many | |
| 6 | + | -- failures in a short window. Rows are pruned automatically as new ones are | |
| 7 | + | -- written, so this table never grows without bound. | |
| 8 | + | -- --------------------------------------------------------------------------- | |
| 9 | + | ||
| 10 | + | CREATE TABLE IF NOT EXISTS `znote_login_attempts` ( | |
| 11 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 12 | + | `ip` varchar(45) NOT NULL, | |
| 13 | + | `username` varchar(32) NOT NULL DEFAULT '', | |
| 14 | + | `success` tinyint(1) NOT NULL DEFAULT '0', | |
| 15 | + | `created_at` int NOT NULL, | |
| 16 | + | PRIMARY KEY (`id`), | |
| 17 | + | KEY `ip_time` (`ip`, `created_at`) | |
| 18 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; |
| @@ -0,0 +1,22 @@ | |||
| 1 | + | -- --------------------------------------------------------------------------- | |
| 2 | + | -- ZnoteX 2.0.4 - Server data overrides (config / items / creatures editors) | |
| 3 | + | -- | |
| 4 | + | -- admin/modules/config_editor.php, items_editor.php and creatures_editor.php | |
| 5 | + | -- let admins edit or add single records on top of the bulk-uploaded | |
| 6 | + | -- config.lua / items.xml / monster files handled by admin/modules/serverinfo.php. | |
| 7 | + | -- Overrides are stored here and merged over the parsed cache at read time | |
| 8 | + | -- (serverdata_apply_overrides(), called from serverdata_load()), so a later | |
| 9 | + | -- re-upload of the source file never discards a manual edit. | |
| 10 | + | -- --------------------------------------------------------------------------- | |
| 11 | + | ||
| 12 | + | CREATE TABLE IF NOT EXISTS `znote_serverdata_overrides` ( | |
| 13 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 14 | + | `source` varchar(16) NOT NULL, | |
| 15 | + | `record_key` varchar(191) NOT NULL, | |
| 16 | + | `data` text NOT NULL, | |
| 17 | + | `deleted` tinyint(1) NOT NULL DEFAULT '0', | |
| 18 | + | `updated_by` varchar(64) DEFAULT NULL, | |
| 19 | + | `updated_at` int NOT NULL, | |
| 20 | + | PRIMARY KEY (`id`), | |
| 21 | + | UNIQUE KEY `source_key` (`source`, `record_key`) | |
| 22 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; |
| @@ -0,0 +1,559 @@ | |||
| 1 | + | -- Start of Znote AAC database schema | |
| 2 | + | ||
| 3 | + | SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; | |
| 4 | + | ||
| 5 | + | SET @znote_version = '2.0.0'; | |
| 6 | + | ||
| 7 | + | CREATE TABLE IF NOT EXISTS `znote` ( | |
| 8 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 9 | + | `version` varchar(30) NOT NULL COMMENT 'Znote AAC version', | |
| 10 | + | `installed` int NOT NULL, | |
| 11 | + | `cached` int DEFAULT NULL, | |
| 12 | + | PRIMARY KEY (`id`) | |
| 13 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 14 | + | ||
| 15 | + | CREATE TABLE IF NOT EXISTS `znote_accounts` ( | |
| 16 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 17 | + | `account_id` int NOT NULL, | |
| 18 | + | `ip` bigint UNSIGNED NOT NULL, | |
| 19 | + | `created` int NOT NULL, | |
| 20 | + | `points` int DEFAULT 0, | |
| 21 | + | `cooldown` int DEFAULT 0, | |
| 22 | + | `active` tinyint NOT NULL DEFAULT '0', | |
| 23 | + | `active_email` tinyint NOT NULL DEFAULT '0', | |
| 24 | + | `activekey` int NOT NULL DEFAULT '0', | |
| 25 | + | `flag` varchar(20) NOT NULL, | |
| 26 | + | `secret` char(16) DEFAULT NULL, | |
| 27 | + | `password_hash` varchar(255) DEFAULT NULL, | |
| 28 | + | PRIMARY KEY (`id`) | |
| 29 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 30 | + | ||
| 31 | + | CREATE TABLE IF NOT EXISTS `znote_news` ( | |
| 32 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 33 | + | `title` varchar(30) NOT NULL, | |
| 34 | + | `text` text NOT NULL, | |
| 35 | + | `date` int NOT NULL, | |
| 36 | + | `pid` int NOT NULL, | |
| 37 | + | PRIMARY KEY (`id`) | |
| 38 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 39 | + | ||
| 40 | + | CREATE TABLE IF NOT EXISTS `znote_images` ( | |
| 41 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 42 | + | `title` varchar(30) NOT NULL, | |
| 43 | + | `desc` text NOT NULL, | |
| 44 | + | `date` int NOT NULL, | |
| 45 | + | `status` int NOT NULL, | |
| 46 | + | `image` varchar(255) NOT NULL, | |
| 47 | + | `delhash` varchar(30) NOT NULL, | |
| 48 | + | `account_id` int NOT NULL, | |
| 49 | + | PRIMARY KEY (`id`) | |
| 50 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 51 | + | ||
| 52 | + | CREATE TABLE IF NOT EXISTS `znote_paypal` ( | |
| 53 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 54 | + | `txn_id` varchar(30) NOT NULL, | |
| 55 | + | `email` varchar(255) NOT NULL, | |
| 56 | + | `accid` int NOT NULL, | |
| 57 | + | `price` int NOT NULL, | |
| 58 | + | `points` int NOT NULL, | |
| 59 | + | PRIMARY KEY (`id`) | |
| 60 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 61 | + | ||
| 62 | + | CREATE TABLE IF NOT EXISTS `znote_paygol` ( | |
| 63 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 64 | + | `account_id` int NOT NULL, | |
| 65 | + | `price` int NOT NULL, | |
| 66 | + | `points` int NOT NULL, | |
| 67 | + | `message_id` varchar(255) NOT NULL, | |
| 68 | + | `service_id` varchar(255) NOT NULL, | |
| 69 | + | `shortcode` varchar(255) NOT NULL, | |
| 70 | + | `keyword` varchar(255) NOT NULL, | |
| 71 | + | `message` varchar(255) NOT NULL, | |
| 72 | + | `sender` varchar(255) NOT NULL, | |
| 73 | + | `operator` varchar(255) NOT NULL, | |
| 74 | + | `country` varchar(255) NOT NULL, | |
| 75 | + | `currency` varchar(255) NOT NULL, | |
| 76 | + | PRIMARY KEY (`id`) | |
| 77 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 78 | + | ||
| 79 | + | CREATE TABLE IF NOT EXISTS `znote_pagseguro` ( | |
| 80 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 81 | + | `transaction` varchar(36) NOT NULL, | |
| 82 | + | `account` int NOT NULL, | |
| 83 | + | `price` decimal(11,2) NOT NULL, | |
| 84 | + | `points` int NOT NULL, | |
| 85 | + | `payment_status` tinyint NOT NULL, | |
| 86 | + | `completed` tinyint NOT NULL, | |
| 87 | + | PRIMARY KEY (`id`), | |
| 88 | + | KEY `transaction` (`transaction`) | |
| 89 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 90 | + | ||
| 91 | + | CREATE TABLE IF NOT EXISTS `znote_pagseguro_notifications` ( | |
| 92 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 93 | + | `notification_code` varchar(40) NOT NULL, | |
| 94 | + | `details` text NOT NULL, | |
| 95 | + | `receive_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, | |
| 96 | + | PRIMARY KEY (`id`) | |
| 97 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 98 | + | ||
| 99 | + | CREATE TABLE IF NOT EXISTS `znote_payment_transactions` ( | |
| 100 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 101 | + | `provider` varchar(32) NOT NULL, | |
| 102 | + | `reference` varchar(128) NOT NULL, | |
| 103 | + | `provider_reference` varchar(128) DEFAULT NULL, | |
| 104 | + | `account_id` int NOT NULL, | |
| 105 | + | `price` decimal(11,2) NOT NULL, | |
| 106 | + | `currency` varchar(8) NOT NULL, | |
| 107 | + | `points` int NOT NULL, | |
| 108 | + | `status` varchar(32) NOT NULL DEFAULT 'pending', | |
| 109 | + | `credited` tinyint NOT NULL DEFAULT '0', | |
| 110 | + | `test_mode` tinyint NOT NULL DEFAULT '0', | |
| 111 | + | `created_at` int NOT NULL, | |
| 112 | + | `updated_at` int NOT NULL, | |
| 113 | + | `credited_at` int DEFAULT NULL, | |
| 114 | + | `payload` longtext, | |
| 115 | + | PRIMARY KEY (`id`), | |
| 116 | + | UNIQUE KEY `provider_reference_internal` (`provider`, `reference`), | |
| 117 | + | KEY `provider_reference_external` (`provider`, `provider_reference`), | |
| 118 | + | KEY `account_status` (`account_id`, `status`, `created_at`) | |
| 119 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 120 | + | ||
| 121 | + | CREATE TABLE IF NOT EXISTS `znote_payment_events` ( | |
| 122 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 123 | + | `provider` varchar(32) NOT NULL, | |
| 124 | + | `event_id` varchar(128) NOT NULL, | |
| 125 | + | `provider_reference` varchar(128) DEFAULT NULL, | |
| 126 | + | `payment_reference` varchar(128) DEFAULT NULL, | |
| 127 | + | `status` varchar(32) NOT NULL DEFAULT 'received', | |
| 128 | + | `payload` longtext, | |
| 129 | + | `received_at` int NOT NULL, | |
| 130 | + | PRIMARY KEY (`id`), | |
| 131 | + | UNIQUE KEY `provider_event` (`provider`, `event_id`), | |
| 132 | + | KEY `payment_reference` (`provider`, `payment_reference`) | |
| 133 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 134 | + | ||
| 135 | + | CREATE TABLE IF NOT EXISTS `znote_players` ( | |
| 136 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 137 | + | `player_id` int NOT NULL, | |
| 138 | + | `created` int NOT NULL, | |
| 139 | + | `hide_char` tinyint NOT NULL, | |
| 140 | + | `comment` varchar(255) NOT NULL, | |
| 141 | + | PRIMARY KEY (`id`) | |
| 142 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 143 | + | ||
| 144 | + | CREATE TABLE IF NOT EXISTS `znote_player_reports` ( | |
| 145 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 146 | + | `name` varchar(50) NOT NULL, | |
| 147 | + | `posx` int NOT NULL, | |
| 148 | + | `posy` int NOT NULL, | |
| 149 | + | `posz` int NOT NULL, | |
| 150 | + | `report_description` varchar(255) NOT NULL, | |
| 151 | + | `date` int NOT NULL, | |
| 152 | + | `status` tinyint NOT NULL DEFAULT '0', | |
| 153 | + | PRIMARY KEY (`id`) | |
| 154 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 155 | + | ||
| 156 | + | CREATE TABLE IF NOT EXISTS `znote_changelog` ( | |
| 157 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 158 | + | `text` varchar(255) NOT NULL, | |
| 159 | + | `time` int NOT NULL, | |
| 160 | + | `report_id` int NOT NULL, | |
| 161 | + | `status` tinyint NOT NULL DEFAULT '0', | |
| 162 | + | PRIMARY KEY (`id`) | |
| 163 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 164 | + | ||
| 165 | + | CREATE TABLE IF NOT EXISTS `znote_shop` ( | |
| 166 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 167 | + | `type` int NOT NULL, | |
| 168 | + | `itemid` int DEFAULT NULL, | |
| 169 | + | `count` int NOT NULL DEFAULT '1', | |
| 170 | + | `description` varchar(255) NOT NULL, | |
| 171 | + | `points` int NOT NULL DEFAULT '10', | |
| 172 | + | PRIMARY KEY (`id`) | |
| 173 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 174 | + | ||
| 175 | + | CREATE TABLE IF NOT EXISTS `znote_shop_offers` ( | |
| 176 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 177 | + | `type` int NOT NULL, | |
| 178 | + | `itemid` int DEFAULT NULL, | |
| 179 | + | `count` int NOT NULL DEFAULT '1', | |
| 180 | + | `description` varchar(255) NOT NULL, | |
| 181 | + | `points` int NOT NULL DEFAULT '10', | |
| 182 | + | `active` tinyint NOT NULL DEFAULT '1', | |
| 183 | + | `sort_order` int NOT NULL DEFAULT '0', | |
| 184 | + | `created_by` int DEFAULT NULL, | |
| 185 | + | `created_at` int DEFAULT NULL, | |
| 186 | + | `updated_at` int DEFAULT NULL, | |
| 187 | + | PRIMARY KEY (`id`), | |
| 188 | + | KEY `active_sort` (`active`, `sort_order`, `id`) | |
| 189 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 190 | + | ||
| 191 | + | CREATE TABLE IF NOT EXISTS `znote_shop_logs` ( | |
| 192 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 193 | + | `account_id` int NOT NULL, | |
| 194 | + | `player_id` int NOT NULL, | |
| 195 | + | `type` int NOT NULL, | |
| 196 | + | `itemid` int NOT NULL, | |
| 197 | + | `count` int NOT NULL, | |
| 198 | + | `points` int NOT NULL, | |
| 199 | + | `time` int NOT NULL, | |
| 200 | + | PRIMARY KEY (`id`) | |
| 201 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 202 | + | ||
| 203 | + | CREATE TABLE IF NOT EXISTS `znote_shop_orders` ( | |
| 204 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 205 | + | `account_id` int NOT NULL, | |
| 206 | + | `type` int NOT NULL, | |
| 207 | + | `itemid` int NOT NULL, | |
| 208 | + | `count` int NOT NULL, | |
| 209 | + | `time` int NOT NULL DEFAULT '0', | |
| 210 | + | PRIMARY KEY (`id`) | |
| 211 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 212 | + | ||
| 213 | + | -- Audit trail of mutating actions taken from the admin panel: bans, skill | |
| 214 | + | -- edits, points, settings changes, plugin lifecycle, etc. Written by | |
| 215 | + | -- acp_log() in engine/function/adminlog.php, read by Admin Panel > Admin Log. | |
| 216 | + | CREATE TABLE IF NOT EXISTS `znote_admin_log` ( | |
| 217 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 218 | + | `admin_id` int NOT NULL DEFAULT '0', | |
| 219 | + | `admin_name` varchar(50) NOT NULL DEFAULT '', | |
| 220 | + | `action` varchar(64) NOT NULL, | |
| 221 | + | `target` varchar(191) NOT NULL DEFAULT '', | |
| 222 | + | `details` text NOT NULL, | |
| 223 | + | `ip` varchar(45) NOT NULL DEFAULT '', | |
| 224 | + | `created` int NOT NULL, | |
| 225 | + | PRIMARY KEY (`id`), | |
| 226 | + | KEY `admin_created` (`admin_id`, `created`), | |
| 227 | + | KEY `action_created` (`action`, `created`), | |
| 228 | + | KEY `created` (`created`) | |
| 229 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 230 | + | ||
| 231 | + | -- Key/value settings written from the admin panel (active layout, etc). | |
| 232 | + | CREATE TABLE IF NOT EXISTS `znote_config` ( | |
| 233 | + | `key` varchar(64) NOT NULL, | |
| 234 | + | `value` text NOT NULL, | |
| 235 | + | PRIMARY KEY (`key`) | |
| 236 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 237 | + | ||
| 238 | + | -- Navigation entries, managed from Admin Panel > Menus. | |
| 239 | + | -- A theme declares the locations it renders (see layouts/README.md); entries | |
| 240 | + | -- are grouped by that location and ordered by sort_order. | |
| 241 | + | CREATE TABLE IF NOT EXISTS `znote_menu` ( | |
| 242 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 243 | + | `location` varchar(32) NOT NULL COMMENT 'Theme-declared slot: main, sidebar, footer...', | |
| 244 | + | `parent_id` int NOT NULL DEFAULT '0' COMMENT '0 = top level, else the id of the parent entry', | |
| 245 | + | `label` varchar(64) NOT NULL, | |
| 246 | + | `url` varchar(255) NOT NULL, | |
| 247 | + | `icon` varchar(48) NOT NULL DEFAULT '' COMMENT 'Optional Font Awesome class', | |
| 248 | + | `target` varchar(10) NOT NULL DEFAULT '', | |
| 249 | + | `visibility` varchar(10) NOT NULL DEFAULT 'all' COMMENT 'all, guest, user or admin', | |
| 250 | + | `sort_order` int NOT NULL DEFAULT '0', | |
| 251 | + | `active` tinyint NOT NULL DEFAULT '1', | |
| 252 | + | PRIMARY KEY (`id`), | |
| 253 | + | KEY `loc_sort` (`location`, `active`, `sort_order`, `id`) | |
| 254 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 255 | + | ||
| 256 | + | -- Database-backed pages, used by importers and editable site content. | |
| 257 | + | CREATE TABLE IF NOT EXISTS `znote_pages` ( | |
| 258 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 259 | + | `slug` varchar(64) NOT NULL, | |
| 260 | + | `title` varchar(100) NOT NULL, | |
| 261 | + | `body` mediumtext NOT NULL, | |
| 262 | + | `created` int NOT NULL DEFAULT '0', | |
| 263 | + | `updated` int NOT NULL DEFAULT '0', | |
| 264 | + | `player_id` int NOT NULL DEFAULT '0', | |
| 265 | + | `access` tinyint NOT NULL DEFAULT '0', | |
| 266 | + | `active` tinyint NOT NULL DEFAULT '1', | |
| 267 | + | PRIMARY KEY (`id`), | |
| 268 | + | UNIQUE KEY `slug` (`slug`) | |
| 269 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 270 | + | ||
| 271 | + | -- Keeps legacy imports idempotent and stores source ids for follow-up imports. | |
| 272 | + | CREATE TABLE IF NOT EXISTS `znote_convert_map` ( | |
| 273 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 274 | + | `source` varchar(32) NOT NULL, | |
| 275 | + | `source_table` varchar(64) NOT NULL, | |
| 276 | + | `source_id` varchar(64) NOT NULL, | |
| 277 | + | `target_table` varchar(64) NOT NULL, | |
| 278 | + | `target_id` int NOT NULL, | |
| 279 | + | `created` int NOT NULL, | |
| 280 | + | PRIMARY KEY (`id`), | |
| 281 | + | UNIQUE KEY `source_row` (`source`, `source_table`, `source_id`, `target_table`) | |
| 282 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 283 | + | ||
| 284 | + | -- Raw archive of legacy AAC tables, including custom columns ZnoteX does not | |
| 285 | + | -- understand yet. This keeps migrations lossless. | |
| 286 | + | CREATE TABLE IF NOT EXISTS `znote_legacy_tables` ( | |
| 287 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 288 | + | `source` varchar(32) NOT NULL, | |
| 289 | + | `table_name` varchar(64) NOT NULL, | |
| 290 | + | `schema_sql` longtext NOT NULL, | |
| 291 | + | `row_count` int NOT NULL DEFAULT '0', | |
| 292 | + | `captured` int NOT NULL, | |
| 293 | + | PRIMARY KEY (`id`), | |
| 294 | + | UNIQUE KEY `source_table` (`source`, `table_name`) | |
| 295 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 296 | + | ||
| 297 | + | CREATE TABLE IF NOT EXISTS `znote_legacy_rows` ( | |
| 298 | + | `id` bigint NOT NULL AUTO_INCREMENT, | |
| 299 | + | `source` varchar(32) NOT NULL, | |
| 300 | + | `table_name` varchar(64) NOT NULL, | |
| 301 | + | `source_pk` varchar(128) NOT NULL DEFAULT '', | |
| 302 | + | `row_json` longtext NOT NULL, | |
| 303 | + | `captured` int NOT NULL, | |
| 304 | + | PRIMARY KEY (`id`), | |
| 305 | + | KEY `source_table` (`source`, `table_name`), | |
| 306 | + | KEY `source_pk` (`source`, `table_name`, `source_pk`) | |
| 307 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 308 | + | ||
| 309 | + | CREATE TABLE IF NOT EXISTS `znote_visitors` ( | |
| 310 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 311 | + | `ip` bigint NOT NULL, | |
| 312 | + | `value` int NOT NULL, | |
| 313 | + | PRIMARY KEY (`id`) | |
| 314 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 315 | + | ||
| 316 | + | CREATE TABLE IF NOT EXISTS `znote_visitors_details` ( | |
| 317 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 318 | + | `ip` bigint NOT NULL, | |
| 319 | + | `time` int NOT NULL, | |
| 320 | + | `type` tinyint NOT NULL, | |
| 321 | + | `account_id` int NOT NULL, | |
| 322 | + | PRIMARY KEY (`id`) | |
| 323 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 324 | + | ||
| 325 | + | -- Forum 1/3 (boards) | |
| 326 | + | CREATE TABLE IF NOT EXISTS `znote_forum` ( | |
| 327 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 328 | + | `name` varchar(50) NOT NULL, | |
| 329 | + | `access` tinyint NOT NULL, | |
| 330 | + | `closed` tinyint NOT NULL, | |
| 331 | + | `hidden` tinyint NOT NULL, | |
| 332 | + | `guild_id` int NOT NULL, | |
| 333 | + | PRIMARY KEY (`id`) | |
| 334 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 335 | + | ||
| 336 | + | -- Forum 2/3 (threads) | |
| 337 | + | CREATE TABLE IF NOT EXISTS `znote_forum_threads` ( | |
| 338 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 339 | + | `forum_id` int NOT NULL, | |
| 340 | + | `player_id` int NOT NULL, | |
| 341 | + | `player_name` varchar(50) NOT NULL, | |
| 342 | + | `title` varchar(50) NOT NULL, | |
| 343 | + | `text` text NOT NULL, | |
| 344 | + | `created` int NOT NULL, | |
| 345 | + | `updated` int NOT NULL, | |
| 346 | + | `sticky` tinyint NOT NULL, | |
| 347 | + | `hidden` tinyint NOT NULL, | |
| 348 | + | `closed` tinyint NOT NULL, | |
| 349 | + | PRIMARY KEY (`id`) | |
| 350 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 351 | + | ||
| 352 | + | -- Forum 3/3 (posts) | |
| 353 | + | CREATE TABLE IF NOT EXISTS `znote_forum_posts` ( | |
| 354 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 355 | + | `thread_id` int NOT NULL, | |
| 356 | + | `player_id` int NOT NULL, | |
| 357 | + | `player_name` varchar(50) NOT NULL, | |
| 358 | + | `text` text NOT NULL, | |
| 359 | + | `created` int NOT NULL, | |
| 360 | + | `updated` int NOT NULL, | |
| 361 | + | PRIMARY KEY (`id`) | |
| 362 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 363 | + | ||
| 364 | + | -- Pending characters for deletion | |
| 365 | + | CREATE TABLE IF NOT EXISTS `znote_deleted_characters` ( | |
| 366 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 367 | + | `original_account_id` int NOT NULL, | |
| 368 | + | `character_name` varchar(255) NOT NULL, | |
| 369 | + | `time` datetime NOT NULL, | |
| 370 | + | `done` tinyint NOT NULL, | |
| 371 | + | PRIMARY KEY (`id`) | |
| 372 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 373 | + | ||
| 374 | + | CREATE TABLE IF NOT EXISTS `znote_guild_wars` ( | |
| 375 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 376 | + | `limit` int NOT NULL DEFAULT '0', | |
| 377 | + | PRIMARY KEY (`id`) | |
| 378 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 379 | + | ||
| 380 | + | -- Helpdesk system | |
| 381 | + | CREATE TABLE IF NOT EXISTS `znote_tickets` ( | |
| 382 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 383 | + | `owner` int NOT NULL, | |
| 384 | + | `username` varchar(32) NOT NULL, | |
| 385 | + | `subject` text NOT NULL, | |
| 386 | + | `message` text NOT NULL, | |
| 387 | + | `ip` bigint NOT NULL, | |
| 388 | + | `creation` int NOT NULL, | |
| 389 | + | `status` varchar(20) NOT NULL, | |
| 390 | + | PRIMARY KEY (`id`) | |
| 391 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 392 | + | ||
| 393 | + | CREATE TABLE IF NOT EXISTS `znote_tickets_replies` ( | |
| 394 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 395 | + | `tid` int NOT NULL, | |
| 396 | + | `username` varchar(32) NOT NULL, | |
| 397 | + | `message` text NOT NULL, | |
| 398 | + | `created` int NOT NULL, | |
| 399 | + | PRIMARY KEY (`id`) | |
| 400 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 401 | + | ||
| 402 | + | CREATE TABLE IF NOT EXISTS `znote_global_storage` ( | |
| 403 | + | `key` varchar(32) NOT NULL, | |
| 404 | + | `value` TEXT NOT NULL, | |
| 405 | + | UNIQUE (`key`) | |
| 406 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 407 | + | ||
| 408 | + | -- Character auction system | |
| 409 | + | CREATE TABLE IF NOT EXISTS `znote_auction_player` ( | |
| 410 | + | `id` int NOT NULL AUTO_INCREMENT, | |
| 411 | + | `player_id` int NOT NULL, | |
| 412 | + | `original_account_id` int NOT NULL, | |
| 413 | + | `bidder_account_id` int NOT NULL, | |
| 414 | + | `time_begin` int NOT NULL, | |
| 415 | + | `time_end` int NOT NULL, | |
| 416 | + | `price` int NOT NULL, | |
| 417 | + | `bid` int NOT NULL, | |
| 418 | + | `deposit` int NOT NULL, | |
| 419 | + | `sold` tinyint NOT NULL, | |
| 420 | + | `claimed` tinyint NOT NULL, | |
| 421 | + | PRIMARY KEY (`id`) | |
| 422 | + | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; | |
| 423 | + | ||
| 424 | + | -- Populate basic info | |
| 425 | + | INSERT INTO `znote` (`version`, `installed`) VALUES | |
| 426 | + | (@znote_version, UNIX_TIMESTAMP(CURDATE())); | |
| 427 | + | ||
| 428 | + | -- Default settings | |
| 429 | + | INSERT INTO `znote_config` (`key`, `value`) VALUES | |
| 430 | + | ('layout', 'default'); | |
| 431 | + | ||
| 432 | + | INSERT INTO `znote_menu` (`location`, `parent_id`, `label`, `url`, `icon`, `visibility`, `sort_order`) | |
| 433 | + | SELECT `seed`.`location`, `seed`.`parent_id`, `seed`.`label`, `seed`.`url`, `seed`.`icon`, `seed`.`visibility`, `seed`.`sort_order` | |
| 434 | + | FROM ( | |
| 435 | + | SELECT 'main' AS `location`, 0 AS `parent_id`, 'Home' AS `label`, 'index.php' AS `url`, 'fa-home' AS `icon`, 'all' AS `visibility`, 10 AS `sort_order` | |
| 436 | + | UNION ALL SELECT 'main', 0, 'Changelog', 'changelog.php', '', 'all', 20 | |
| 437 | + | UNION ALL SELECT 'main', 0, 'Account', 'myaccount.php', 'fa-user-circle', 'user', 30 | |
| 438 | + | UNION ALL SELECT 'main', 0, 'Login', 'login.php', 'fa-user-circle', 'guest', 30 | |
| 439 | + | UNION ALL SELECT 'main', 0, 'Register', 'register.php', 'fa-key', 'guest', 40 | |
| 440 | + | UNION ALL SELECT 'main', 0, 'Downloads', 'downloads.php', '', 'all', 50 | |
| 441 | + | UNION ALL SELECT 'main', 0, 'Community', 'onlinelist.php', 'fa-users', 'all', 60 | |
| 442 | + | UNION ALL SELECT 'main', 0, 'Highscores', 'highscores.php', '', 'all', 70 | |
| 443 | + | UNION ALL SELECT 'main', 0, 'Guilds', 'guilds.php', '', 'all', 80 | |
| 444 | + | UNION ALL SELECT 'main', 0, 'Forum', 'forum.php', '', 'all', 90 | |
| 445 | + | UNION ALL SELECT 'main', 0, 'Houses', 'houses.php', '', 'all', 100 | |
| 446 | + | UNION ALL SELECT 'main', 0, 'Latest deaths', 'deaths.php', '', 'all', 110 | |
| 447 | + | UNION ALL SELECT 'main', 0, 'Kill statistics', 'killers.php', '', 'all', 120 | |
| 448 | + | UNION ALL SELECT 'main', 0, 'Bans', 'bans.php', '', 'all', 125 | |
| 449 | + | UNION ALL SELECT 'main', 0, 'Creatures', 'creatures.php', '', 'all', 135 | |
| 450 | + | UNION ALL SELECT 'main', 0, 'Library', 'serverinfo.php', 'fa-book', 'all', 130 | |
| 451 | + | UNION ALL SELECT 'main', 0, 'Spells', 'spells.php', '', 'all', 140 | |
| 452 | + | UNION ALL SELECT 'main', 0, 'Support', 'support.php', 'fa-info-circle', 'all', 150 | |
| 453 | + | UNION ALL SELECT 'main', 0, 'Helpdesk', 'helpdesk.php', '', 'all', 160 | |
| 454 | + | UNION ALL SELECT 'main', 0, 'Shop', 'shop.php', 'fa-shopping-cart', 'all', 170 | |
| 455 | + | UNION ALL SELECT 'main', 0, 'Buy points', 'buypoints.php', '', 'all', 180 | |
| 456 | + | UNION ALL SELECT 'main', 0, 'Admin Panel', 'admin/index.php', 'fa-sliders', 'admin', 190 | |
| 457 | + | ) `seed` | |
| 458 | + | LEFT JOIN `znote_menu` `existing` | |
| 459 | + | ON `existing`.`location` = `seed`.`location` | |
| 460 | + | AND `existing`.`label` = `seed`.`label` | |
| 461 | + | AND `existing`.`url` = `seed`.`url` | |
| 462 | + | WHERE `existing`.`id` IS NULL; | |
| 463 | + | ||
| 464 | + | ||
| 465 | + | -- Nest the sub-entries under their section. Done as a second pass because the | |
| 466 | + | -- parent ids are only known once the rows above exist. | |
| 467 | + | UPDATE `znote_menu` `c` | |
| 468 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Home' | |
| 469 | + | SET `c`.`parent_id` = `p`.`id` | |
| 470 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Changelog'); | |
| 471 | + | ||
| 472 | + | UPDATE `znote_menu` `c` | |
| 473 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Account' | |
| 474 | + | SET `c`.`parent_id` = `p`.`id` | |
| 475 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Downloads'); | |
| 476 | + | ||
| 477 | + | UPDATE `znote_menu` `c` | |
| 478 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Community' | |
| 479 | + | SET `c`.`parent_id` = `p`.`id` | |
| 480 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Highscores','Guilds','Forum','Houses','Latest deaths','Kill statistics','Bans'); | |
| 481 | + | ||
| 482 | + | UPDATE `znote_menu` `c` | |
| 483 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Library' | |
| 484 | + | SET `c`.`parent_id` = `p`.`id` | |
| 485 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Spells','Creatures'); | |
| 486 | + | ||
| 487 | + | UPDATE `znote_menu` `c` | |
| 488 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Support' | |
| 489 | + | SET `c`.`parent_id` = `p`.`id` | |
| 490 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Helpdesk'); | |
| 491 | + | ||
| 492 | + | UPDATE `znote_menu` `c` | |
| 493 | + | JOIN `znote_menu` `p` ON `p`.`location` = 'main' AND `p`.`parent_id` = 0 AND `p`.`label` = 'Shop' | |
| 494 | + | SET `c`.`parent_id` = `p`.`id` | |
| 495 | + | WHERE `c`.`location` = 'main' AND `c`.`label` IN ('Buy points'); | |
| 496 | + | ||
| 497 | + | -- Add default forum boards | |
| 498 | + | INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`) | |
| 499 | + | SELECT 'Staff Board', '4', '0', '0', '0' FROM DUAL | |
| 500 | + | WHERE NOT EXISTS (SELECT 1 FROM `znote_forum` WHERE `name` = 'Staff Board' AND `guild_id` = '0'); | |
| 501 | + | INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`) | |
| 502 | + | SELECT 'Tutors Board', '2', '0', '0', '0' FROM DUAL | |
| 503 | + | WHERE NOT EXISTS (SELECT 1 FROM `znote_forum` WHERE `name` = 'Tutors Board' AND `guild_id` = '0'); | |
| 504 | + | INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`) | |
| 505 | + | SELECT 'Discussion', '1', '0', '0', '0' FROM DUAL | |
| 506 | + | WHERE NOT EXISTS (SELECT 1 FROM `znote_forum` WHERE `name` = 'Discussion' AND `guild_id` = '0'); | |
| 507 | + | INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`) | |
| 508 | + | SELECT 'Feedback', '1', '0', '1', '0' FROM DUAL | |
| 509 | + | WHERE NOT EXISTS (SELECT 1 FROM `znote_forum` WHERE `name` = 'Feedback' AND `guild_id` = '0'); | |
| 510 | + | ||
| 511 | + | -- Convert existing accounts in database to be Znote AAC compatible | |
| 512 | + | INSERT INTO `znote_accounts` (`account_id`, `ip`, `created`, `flag`) | |
| 513 | + | SELECT | |
| 514 | + | `a`.`id` AS `account_id`, | |
| 515 | + | 0 AS `ip`, | |
| 516 | + | UNIX_TIMESTAMP(CURDATE()) AS `created`, | |
| 517 | + | '' AS `flag` | |
| 518 | + | FROM `accounts` AS `a` | |
| 519 | + | LEFT JOIN `znote_accounts` AS `z` | |
| 520 | + | ON `a`.`id` = `z`.`account_id` | |
| 521 | + | WHERE `z`.`created` IS NULL; | |
| 522 | + | ||
| 523 | + | -- Convert existing players in database to be Znote AAC compatible | |
| 524 | + | INSERT INTO `znote_players` (`player_id`, `created`, `hide_char`, `comment`) | |
| 525 | + | SELECT | |
| 526 | + | `p`.`id` AS `player_id`, | |
| 527 | + | UNIX_TIMESTAMP(CURDATE()) AS `created`, | |
| 528 | + | 0 AS `hide_char`, | |
| 529 | + | '' AS `comment` | |
| 530 | + | FROM `players` AS `p` | |
| 531 | + | LEFT JOIN `znote_players` AS `z` | |
| 532 | + | ON `p`.`id` = `z`.`player_id` | |
| 533 | + | WHERE `z`.`created` IS NULL; | |
| 534 | + | ||
| 535 | + | -- Delete duplicate account records | |
| 536 | + | DELETE `d` FROM `znote_accounts` AS `d` | |
| 537 | + | INNER JOIN ( | |
| 538 | + | SELECT `i`.`account_id`, | |
| 539 | + | MAX(`i`.`id`) AS `retain` | |
| 540 | + | FROM `znote_accounts` AS `i` | |
| 541 | + | GROUP BY `i`.`account_id` | |
| 542 | + | HAVING COUNT(`i`.`id`) > 1 | |
| 543 | + | ) AS `x` | |
| 544 | + | ON `d`.`account_id` = `x`.`account_id` | |
| 545 | + | AND `d`.`id` != `x`.`retain`; | |
| 546 | + | ||
| 547 | + | -- Delete duplicate player records | |
| 548 | + | DELETE `d` FROM `znote_players` AS `d` | |
| 549 | + | INNER JOIN ( | |
| 550 | + | SELECT `i`.`player_id`, | |
| 551 | + | MAX(`i`.`id`) AS `retain` | |
| 552 | + | FROM `znote_players` AS `i` | |
| 553 | + | GROUP BY `i`.`player_id` | |
| 554 | + | HAVING COUNT(`i`.`id`) > 1 | |
| 555 | + | ) AS `x` | |
| 556 | + | ON `d`.`player_id` = `x`.`player_id` | |
| 557 | + | AND `d`.`id` != `x`.`retain`; | |
| 558 | + | ||
| 559 | + | -- End of Znote AAC database schema |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; theme_open(); | |
| 2 | + | if ($config['allowSubPages']) { | |
| 3 | + | $page = (isset($_GET['page']) && !empty($_GET['page'])) ? getValue($_GET['page'] ?? null) : ''; | |
| 4 | + | if (isset($subpages[$page]['file'])) { $f = theme_file('sub/'.$subpages[$page]['file']); if ($f !== null) require_once $f; } | |
| 5 | + | else { | |
| 6 | + | if (isset($subpages)) echo '<h2>'. t('sub.unknown_title') .'</h2><p>'. t('sub.unknown_text') .'</p>'; | |
| 7 | + | } | |
| 8 | + | } | |
| 9 | + | else echo '<h2>'. t('sub.disabled_title') .'</h2><p>'. t('sub.disabled_text') .'</p>'; | |
| 10 | + | theme_close(); ?> |