| @@ -0,0 +1,1245 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | znote_csrf_protect_public_post(); | |
| 3 | + | if ($config['require_login']['guilds']) protect_page(); | |
| 4 | + | ||
| 5 | + | // These functions are page-specific and only used in this file. | |
| 6 | + | function guild_list($TFSVersion) { | |
| 7 | + | $cache = new Cache('engine/cache/guildlist'); | |
| 8 | + | if ($cache->hasExpired()) { | |
| 9 | + | $guilds = db()->fetchAll(" | |
| 10 | + | SELECT | |
| 11 | + | `id`, | |
| 12 | + | `name`, | |
| 13 | + | `creationdata`, | |
| 14 | + | `motd`, | |
| 15 | + | ( SELECT | |
| 16 | + | COUNT('guild_id') | |
| 17 | + | FROM `guild_membership` | |
| 18 | + | WHERE `guild_id`=`id` | |
| 19 | + | ) AS `total` | |
| 20 | + | FROM `guilds` | |
| 21 | + | ORDER BY `name`; | |
| 22 | + | "); | |
| 23 | + | ||
| 24 | + | // Add level data info to guilds | |
| 25 | + | if ($guilds !== false) | |
| 26 | + | for ($i = 0; $i < count($guilds); $i++) | |
| 27 | + | $guilds[$i]['level'] = get_guild_level_data($guilds[$i]['id']); | |
| 28 | + | ||
| 29 | + | $cache->setContent($guilds); | |
| 30 | + | $cache->save(); | |
| 31 | + | } else { | |
| 32 | + | $guilds = $cache->load(); | |
| 33 | + | } | |
| 34 | + | return $guilds; | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | function get_guild_position($rid) { | |
| 38 | + | $data = db()->fetchOne("SELECT `level` FROM `guild_ranks` WHERE `id` = ?;", [(int)$rid]); | |
| 39 | + | return ($data !== false) ? $data['level'] : false; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | // Check guild logo | |
| 43 | + | function logo_exists($guild) { | |
| 44 | + | if (function_exists('theme_guild_logo')) { | |
| 45 | + | $themed = theme_guild_logo($guild); | |
| 46 | + | if ($themed !== null) { | |
| 47 | + | echo $themed; | |
| 48 | + | return; | |
| 49 | + | } | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | $guild = sanitize($guild); | |
| 53 | + | echo (file_exists('engine/guildimg/'.$guild.'.gif')) ? 'engine/guildimg/'.$guild.'.gif' : 'engine/guildimg/default@logo.gif'; | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // Remove cid invitation from guild (gid) | |
| 57 | + | function guild_remove_invitation($cid, $gid) { | |
| 58 | + | db()->execute(" | |
| 59 | + | DELETE FROM `guild_invites` | |
| 60 | + | WHERE `player_id` = ? | |
| 61 | + | AND `guild_id` = ? | |
| 62 | + | ;", [(int)$cid, (int)$gid]); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | function update_player_guildnick_10($cid, $nick) { | |
| 66 | + | $nick = sanitize($nick); | |
| 67 | + | db()->execute("UPDATE `guild_membership` SET `nick` = ? WHERE `player_id` = ?", [$nick, (int)$cid]); | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | function guild_player_join($cid, $gid) { | |
| 71 | + | $cid = (int)$cid; | |
| 72 | + | $gid = (int)$gid; | |
| 73 | + | ||
| 74 | + | // Find rank id for regular member in this guild | |
| 75 | + | $guildrank = db()->fetchOne(" | |
| 76 | + | SELECT `id` | |
| 77 | + | FROM `guild_ranks` | |
| 78 | + | WHERE `guild_id` = ? | |
| 79 | + | AND `level` = 1 LIMIT 1; | |
| 80 | + | ", [$gid]); | |
| 81 | + | ||
| 82 | + | if ($guildrank !== false) { | |
| 83 | + | $rid = $guildrank['id']; | |
| 84 | + | // Remove invite | |
| 85 | + | db()->execute("DELETE FROM `guild_invites` WHERE `player_id` = ?;", [$cid]); | |
| 86 | + | // Add to guild | |
| 87 | + | db()->execute(" | |
| 88 | + | INSERT INTO `guild_membership` (`player_id`, `guild_id`, `rank_id`, `nick`) | |
| 89 | + | VALUES (?, ?, ?, '') | |
| 90 | + | ;", [$cid, $gid, $rid]); | |
| 91 | + | // Return success | |
| 92 | + | return true; | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | return false; | |
| 96 | + | } | |
| 97 | + | ||
| 98 | + | function guild_player_leave_10($cid) { | |
| 99 | + | db()->execute("DELETE FROM `guild_membership` WHERE `player_id` = ? LIMIT 1;", [(int)$cid]); | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | function update_player_guild_position_10($cid, $rid) { | |
| 103 | + | db()->execute("UPDATE `guild_membership` SET `rank_id` = ? WHERE `player_id` = ?", [(int)$rid, (int)$cid]); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | function guild_invite_player($cid, $gid) { | |
| 107 | + | db()->execute("INSERT INTO `guild_invites` (`player_id`, `guild_id`) VALUES (?, ?)", [(int)$cid, (int)$gid]); | |
| 108 | + | } | |
| 109 | + | ||
| 110 | + | function guild_remove_invites($gid) { | |
| 111 | + | db()->execute("DELETE FROM `guild_invites` WHERE `guild_id` = ?;", [(int)$gid]); | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | function guild_delete($gid) { | |
| 115 | + | db()->execute("DELETE FROM `guilds` WHERE `id` = ?;", [(int)$gid]); | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | // Get guild data, using guild id. | |
| 119 | + | function get_guild_rank_data($gid) { | |
| 120 | + | return db()->fetchAll("SELECT `id`, `guild_id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = ? ORDER BY `id` DESC LIMIT 0, 30", [(int)$gid]); | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // Changes leadership of a guild to player_id | |
| 124 | + | function guild_new_leader($new_leader, $gid) { | |
| 125 | + | db()->execute("UPDATE `guilds` SET `ownerid` = ? WHERE `id` = ?", [(int)$new_leader, (int)$gid]); | |
| 126 | + | } | |
| 127 | + | ||
| 128 | + | // Change guild leader (parameters: cid, new and old leader). | |
| 129 | + | function guild_change_leader($nCid, $oCid) { | |
| 130 | + | $nCid = (int)$nCid; | |
| 131 | + | $oCid = (int)$oCid; | |
| 132 | + | $gid = guild_leader_gid($oCid); | |
| 133 | + | $ranks = get_guild_rank_data($gid); | |
| 134 | + | $leader_rid = 0; | |
| 135 | + | $vice_rid = 0; | |
| 136 | + | ||
| 137 | + | // Get rank id for leader and vice leader. | |
| 138 | + | foreach ($ranks as $rank) { | |
| 139 | + | if ($rank['level'] == 3) $leader_rid = $rank['id']; | |
| 140 | + | if ($rank['level'] == 2) $vice_rid = $rank['id']; | |
| 141 | + | } | |
| 142 | + | ||
| 143 | + | $status = ($leader_rid > 0 && $vice_rid > 0) ? true : false; | |
| 144 | + | ||
| 145 | + | if ($status) { | |
| 146 | + | // Update players and set their new rank id | |
| 147 | + | db()->execute("UPDATE `guild_membership` SET `rank_id` = ? WHERE `player_id` = ? LIMIT 1;", [$leader_rid, $nCid]); | |
| 148 | + | db()->execute("UPDATE `guild_membership` SET `rank_id` = ? WHERE `player_id` = ? LIMIT 1;", [$vice_rid, $oCid]); | |
| 149 | + | ||
| 150 | + | // Update guilds set new ownerid | |
| 151 | + | guild_new_leader($nCid, $gid); | |
| 152 | + | } | |
| 153 | + | ||
| 154 | + | return $status; | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | // Creates a guild, where cid is the owner of the guild, and name is the name of guild. | |
| 158 | + | function create_guild($cid, $name) { | |
| 159 | + | $cid = (int)$cid; | |
| 160 | + | $name = trim(preg_replace('/\s\s+/', ' ', str_replace("\n", " ", sanitize($name)))); | |
| 161 | + | $time = time(); | |
| 162 | + | ||
| 163 | + | // Create the guild | |
| 164 | + | db()->execute("INSERT INTO `guilds` (`name`, `ownerid`, `creationdata`, `motd`) VALUES (?, ?, ?, '');", [$name, $cid, $time]); | |
| 165 | + | ||
| 166 | + | // Get guild id | |
| 167 | + | $gid = get_guild_id($name); | |
| 168 | + | ||
| 169 | + | // Get rank id for guild leader | |
| 170 | + | $data = db()->fetchOne("SELECT `id` FROM `guild_ranks` WHERE `guild_id` = ? AND `level` = 3 LIMIT 1;", [$gid]); | |
| 171 | + | $rid = ($data !== false) ? $data['id'] : false; | |
| 172 | + | ||
| 173 | + | // Give player rank id for leader of his guild | |
| 174 | + | db()->execute("INSERT INTO `guild_membership` (`player_id`, `guild_id`, `rank_id`, `nick`) VALUES (?, ?, ?, '');", [$cid, $gid, $rid]); | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | // Returns total members in a guild (integer) | |
| 178 | + | function count_guild_members($gid) { | |
| 179 | + | $data = db()->fetchOne("SELECT COUNT('guild_id') AS `total` FROM `guild_membership` WHERE `guild_id` = ?;", [(int)$gid]); | |
| 180 | + | return ($data !== false) ? $data['total'] : false; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | // Guildwar functions | |
| 184 | + | function guild_war_invitation($cid, $gid) { | |
| 185 | + | $cid = (int)$cid; | |
| 186 | + | $gid = (int)$gid; | |
| 187 | + | $gname = get_guild_name($cid); | |
| 188 | + | $ename = get_guild_name($gid); | |
| 189 | + | $time = time(); | |
| 190 | + | db()->execute(" | |
| 191 | + | INSERT INTO `guild_wars` (`guild1`, `guild2`, `name1`, `name2`, `status`, `started`, `ended`) | |
| 192 | + | VALUES (?, ?, ?, ?, 0, ?, 0) | |
| 193 | + | ;", [$cid, $gid, $gname, $ename, $time]); | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | function accept_war_invitation($cid, $gid) { | |
| 197 | + | db()->execute("UPDATE `guild_wars` SET `status` = 1 WHERE `guild1` = ? AND `guild2` = ? AND `status` = 0;", [(int)$cid, (int)$gid]); | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | function reject_war_invitation($cid, $gid) { | |
| 201 | + | db()->execute("UPDATE `guild_wars` SET `status` = 2, `ended` = ? WHERE `guild1` = ? AND `guild2` = ?;", [time(), (int)$cid, (int)$gid]); | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | function cancel_war_invitation($cid, $gid) { | |
| 205 | + | db()->execute("UPDATE `guild_wars` SET `status` = 3, `ended` = ? WHERE `guild2` = ? AND `guild1` = ?;", [time(), (int)$cid, (int)$gid]); | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | theme_open(); | |
| 209 | + | ||
| 210 | + | if (user_logged_in() === true) { | |
| 211 | + | // fetch data | |
| 212 | + | $char_count = user_character_list_count($session_user_id); | |
| 213 | + | $char_array = user_character_list($user_data['id']); | |
| 214 | + | $characters = array(); | |
| 215 | + | $charactersId = array(); | |
| 216 | + | $charactersRank = array(); | |
| 217 | + | if ($char_array !== false) { | |
| 218 | + | foreach ($char_array as $value) { | |
| 219 | + | $characters[] = $value['name']; | |
| 220 | + | $charactersId[] = $value['id']; | |
| 221 | + | $charactersRank[] = $value['rank_id']; | |
| 222 | + | } | |
| 223 | + | } | |
| 224 | + | } else { | |
| 225 | + | $char_count = 0; | |
| 226 | + | } | |
| 227 | + | ||
| 228 | + | // Display the guild list | |
| 229 | + | if (empty($_GET['name'])) { | |
| 230 | + | //data_dump($guild, false, "guild data"); | |
| 231 | + | $guilds = guild_list(znote_server_adapter()->normalizedEngine()); | |
| 232 | + | ||
| 233 | + | if (isset($guilds) && !empty($guilds) && $guilds !== false) { | |
| 234 | + | //data_dump($guilds, false, "Guilds"); | |
| 235 | + | if (theme_file('views/guilds.php') !== null) { | |
| 236 | + | view('guilds', array('guilds' => $guilds)); | |
| 237 | + | } else { | |
| 238 | + | ?> | |
| 239 | + | <style type="text/css"> | |
| 240 | + | #guildsTable tr { | |
| 241 | + | cursor: pointer; | |
| 242 | + | } | |
| 243 | + | </style> | |
| 244 | + | <table id="guildsTable" class="table table-striped table-hover"> | |
| 245 | + | <tr class="yellow"> | |
| 246 | + | <th><?= t('guild.logo_label') ?></th> | |
| 247 | + | <th><?= t('common.description') ?></th> | |
| 248 | + | <th><?= t('guild.data') ?></th> | |
| 249 | + | </tr> | |
| 250 | + | <?php | |
| 251 | + | foreach ($guilds as $guild) { | |
| 252 | + | if ($guild['total'] >= 1) { | |
| 253 | + | $url = url("guilds.php?name=". $guild['name']); | |
| 254 | + | ?> | |
| 255 | + | <tr class="special" onclick="javascript:window.location.href='<?php echo $url; ?>'"> | |
| 256 | + | <td style="width: 100px;"> | |
| 257 | + | <img style="max-height: 100px; margin: auto; display: block;" | |
| 258 | + | src="<?php logo_exists($guild['name']); ?>"> | |
| 259 | + | </td> | |
| 260 | + | <td> | |
| 261 | + | <b><?php echo $guild['name']; ?></b> | |
| 262 | + | <?php if (strlen($guild['motd']) > 0) echo '<br>'.$guild['motd']; ?> | |
| 263 | + | </td> | |
| 264 | + | <td> | |
| 265 | + | <?php echo "Total members: ".$guild['level']['players']; ?> | |
| 266 | + | <br><?php echo "Average level: ".$guild['level']['avg'].""; ?> | |
| 267 | + | <br><?php echo "Guild level: ".$guild['level']['total']; ?> | |
| 268 | + | </td> | |
| 269 | + | </tr> | |
| 270 | + | <?php | |
| 271 | + | //echo '<td>'. getClock($guild['creationdata'], true) .'</td>'; | |
| 272 | + | } | |
| 273 | + | } | |
| 274 | + | ?> | |
| 275 | + | </table> | |
| 276 | + | <?php | |
| 277 | + | } | |
| 278 | + | } else echo '<p>'. t('guild.empty_list'). '</p>';?> | |
| 279 | + | ||
| 280 | + | <!-- POST action: create guild --> | |
| 281 | + | <?php | |
| 282 | + | ob_start(); | |
| 283 | + | if (user_logged_in() === true) { | |
| 284 | + | // post verifications | |
| 285 | + | // CREATE GUILD | |
| 286 | + | if (!empty($_POST['selected_char']) && !empty($_POST['guild_name'])) { | |
| 287 | + | data_dump($_POST, false, "debug"); | |
| 288 | + | if ((int)user_character_account_id($_POST['selected_char']) === $session_user_id) { | |
| 289 | + | data_dump($_POST, false, "debug2"); | |
| 290 | + | //code here | |
| 291 | + | $name = sanitize($_POST['selected_char']); | |
| 292 | + | $user_id = user_character_id($name); | |
| 293 | + | ||
| 294 | + | $char_data = user_character_data($user_id, 'level'); | |
| 295 | + | $char_data['online'] = (user_is_online_10($user_id)) ? 1 : 0; | |
| 296 | + | ||
| 297 | + | // If character level is high enough | |
| 298 | + | if ($char_data['level'] >= $config['create_guild_level']) { | |
| 299 | + | ||
| 300 | + | // If character is offline | |
| 301 | + | if ($char_data['online'] == 0) { | |
| 302 | + | ||
| 303 | + | // If character is premium | |
| 304 | + | if ($config['guild_require_premium'] == false || $user_data['premdays'] > 0) { | |
| 305 | + | ||
| 306 | + | if (get_character_guild_rank($user_id) < 1) { | |
| 307 | + | ||
| 308 | + | if (preg_match("/^[a-zA-Z_ ]+$/", $_POST['guild_name'])) { | |
| 309 | + | // Only allow normal symbols as guild name | |
| 310 | + | if (strlen($_POST['guild_name']) < 31) { | |
| 311 | + | ||
| 312 | + | $guildname = sanitize($_POST['guild_name']); | |
| 313 | + | ||
| 314 | + | $gid = get_guild_id($guildname); | |
| 315 | + | if ($gid === false) { | |
| 316 | + | create_guild($user_id, $guildname); | |
| 317 | + | // Re-cache the guild list | |
| 318 | + | $guilds = guild_list(znote_server_adapter()->normalizedEngine()); | |
| 319 | + | header('Location: success.php'); | |
| 320 | + | exit(); | |
| 321 | + | } else echo 'A guild with that name already exist.'; | |
| 322 | + | } else echo t('guild.name_too_long2'); | |
| 323 | + | } else echo t('guild.name_letters'); | |
| 324 | + | } else echo t('guild.already_in'); | |
| 325 | + | } else echo t('guild.need_premium'); | |
| 326 | + | } else echo t('guild.must_offline'); | |
| 327 | + | } else echo $name .' is level '. $char_data['level'] .'. But you need level '. $config['create_guild_level'] .'+ to create your own guild!'; | |
| 328 | + | } | |
| 329 | + | } | |
| 330 | + | // end | |
| 331 | + | ?> | |
| 332 | + | ||
| 333 | + | ||
| 334 | + | <!-- FORMS TO CREATE GUILD--> | |
| 335 | + | <form action="" method="post"> | |
| 336 | + | <ul> | |
| 337 | + | <li> | |
| 338 | + | <?= t('guild.create_btn') ?>:<br> | |
| 339 | + | <select name="selected_char"> | |
| 340 | + | <?php | |
| 341 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 342 | + | echo '<option value="'. $characters[$i] .'">'. $characters[$i] .'</option>'; | |
| 343 | + | } | |
| 344 | + | ?> | |
| 345 | + | </select> | |
| 346 | + | <input type="text" name="guild_name"> | |
| 347 | + | <input type="submit" value="<?= t('guild.create_btn') ?>"> | |
| 348 | + | </li> | |
| 349 | + | </ul> | |
| 350 | + | </form> | |
| 351 | + | ||
| 352 | + | <?php | |
| 353 | + | } else echo t('guild.need_login'); | |
| 354 | + | $guildCreateFormHtml = ob_get_clean(); | |
| 355 | + | view('guild_create_form', ['guildCreateFormHtml' => $guildCreateFormHtml]); | |
| 356 | + | ?> | |
| 357 | + | <!-- end user--> | |
| 358 | + | ||
| 359 | + | <?php | |
| 360 | + | } else { // GUILD OVERVIEW | |
| 361 | + | $guild = get_guild_data($_GET['name'] ?? ''); | |
| 362 | + | if (!is_array($guild) || empty($guild['id'])) { | |
| 363 | + | header('Location: guilds.php'); | |
| 364 | + | exit(); | |
| 365 | + | } | |
| 366 | + | $gid = (int)$guild['id']; | |
| 367 | + | $gcount = count_guild_members($gid); | |
| 368 | + | if ($gcount < 1) { | |
| 369 | + | header('Location: guilds.php'); | |
| 370 | + | exit(); | |
| 371 | + | } | |
| 372 | + | $inv_data = guild_invite_list($gid); | |
| 373 | + | $players = get_guild_players($gid); | |
| 374 | + | $inv_count = 0; | |
| 375 | + | ||
| 376 | + | // Calculate invite count | |
| 377 | + | if ($inv_data !== false) { | |
| 378 | + | foreach ($inv_data as $inv) { | |
| 379 | + | ++$inv_count; | |
| 380 | + | } | |
| 381 | + | } | |
| 382 | + | ||
| 383 | + | // calculate visitor access | |
| 384 | + | $highest_access = 0; | |
| 385 | + | if (user_logged_in() === true) { | |
| 386 | + | // Get visitor access in this guild | |
| 387 | + | foreach ($players as $player) { | |
| 388 | + | $rid = $player['rank_id']; | |
| 389 | + | ||
| 390 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 391 | + | ||
| 392 | + | $playerRank = $charactersRank[$i]; | |
| 393 | + | ||
| 394 | + | if ($playerRank == $rid) { | |
| 395 | + | // Get a player guild position ID, using his rank_id | |
| 396 | + | $access = get_guild_position($playerRank); | |
| 397 | + | if ($access == 2 || $access == 3) { //If player got access level vice leader or leader | |
| 398 | + | if ($access > $highest_access) $highest_access = $access; | |
| 399 | + | } | |
| 400 | + | } | |
| 401 | + | ||
| 402 | + | } | |
| 403 | + | } | |
| 404 | + | } | |
| 405 | + | // Display the specific guild page | |
| 406 | + | ob_start(); | |
| 407 | + | ?> | |
| 408 | + | ||
| 409 | + | <!-- Guild information above table --> | |
| 410 | + | <div id="guildTitleDiv"> | |
| 411 | + | <?php | |
| 412 | + | if (isset($_GET['error'])) | |
| 413 | + | echo "<font size='5' color='red'>".sanitize($_GET['error'])."</font><br><br>"; | |
| 414 | + | ||
| 415 | + | if ($config['use_guild_logos']): ?> | |
| 416 | + | <div id="guildImageDiv" style="float: left; margin-right: 10px;"> | |
| 417 | + | <img style="max-width: 100px; max-height: 100px;" src="<?php logo_exists(sanitize($_GET['name'])); ?>"> | |
| 418 | + | </div> | |
| 419 | + | <?php | |
| 420 | + | endif; ?> | |
| 421 | + | <div id="guildDescription"> | |
| 422 | + | <h1><?= t('guild.title_char', ['name' => sanitize($_GET['name'])]) ?></h1> | |
| 423 | + | <p><?php echo $guild['motd']; ?></p> | |
| 424 | + | </div> | |
| 425 | + | </div> | |
| 426 | + | ||
| 427 | + | <!-- Guild information --> | |
| 428 | + | <table id="guildViewTable" class="table table-striped"> | |
| 429 | + | <tr class="yellow"> | |
| 430 | + | <th><?= t('common.rank_label') ?></th> | |
| 431 | + | <th><?= t('common.name_label') ?></th> | |
| 432 | + | <th><?= t('common.level_label') ?></th> | |
| 433 | + | <th><?= t('common.vocation_label') ?></th> | |
| 434 | + | <th><?= t('guild.status') ?></th> | |
| 435 | + | </tr> | |
| 436 | + | <?php | |
| 437 | + | $onlinelist = array(); | |
| 438 | + | $gplayers = array(); | |
| 439 | + | foreach ($players as $player) { | |
| 440 | + | $gplayers[] = $player['id']; | |
| 441 | + | } | |
| 442 | + | $gplayerPlaceholders = implode(',', array_fill(0, count($gplayers), '?')); | |
| 443 | + | $onlinequery = db()->fetchAll(" | |
| 444 | + | SELECT `player_id` | |
| 445 | + | FROM `players_online` | |
| 446 | + | WHERE `player_id` IN({$gplayerPlaceholders}); | |
| 447 | + | ", $gplayers); | |
| 448 | + | if ($onlinequery !== false) foreach ($onlinequery as $online) { | |
| 449 | + | $onlinelist[] = $online['player_id']; | |
| 450 | + | } | |
| 451 | + | //data_dump($players, false, "Data"); | |
| 452 | + | $rankName = ''; | |
| 453 | + | foreach ($players as $player) { | |
| 454 | + | $chardata['online'] = (in_array($player['id'], $onlinelist)) ? 1 : 0; | |
| 455 | + | echo '<tr>'; | |
| 456 | + | echo '<td>' . ($rankName !== $player['rank_name'] ? $player['rank_name'] : '') . '</td>'; | |
| 457 | + | $rankName = $player['rank_name']; | |
| 458 | + | echo '<td><a href="characterprofile.php?name='. $player['name'] .'">'. $player['name'] .'</a>'; | |
| 459 | + | if (!empty($player['guildnick'])) { | |
| 460 | + | echo ' ('. $player['guildnick'] .')'; | |
| 461 | + | } | |
| 462 | + | echo '</td>'; | |
| 463 | + | echo '<td>'. $player['level'] .'</td>'; | |
| 464 | + | echo '<td>'. $config['vocations'][$player['vocation']]['name'] .'</td>'; | |
| 465 | + | if ($chardata['online'] == 1) echo '<td> <b><font color="green"> Online </font></b></td>'; | |
| 466 | + | else echo '<td> Offline </td>'; | |
| 467 | + | echo '</tr>'; | |
| 468 | + | } | |
| 469 | + | ?> | |
| 470 | + | </table> | |
| 471 | + | ||
| 472 | + | <?php // Invited characters | |
| 473 | + | if ($inv_count > 0): ?> | |
| 474 | + | <h3><?= t('guild.invited_chars') ?></h3> | |
| 475 | + | <table> | |
| 476 | + | <tr class="yellow"> | |
| 477 | + | <td>Name:</td> | |
| 478 | + | <?php | |
| 479 | + | if ($highest_access == 2 || $highest_access == 3) { | |
| 480 | + | echo '<td>'. t('guild.remove'). '</td>'; | |
| 481 | + | } | |
| 482 | + | // Shuffle through visitor characters | |
| 483 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 484 | + | $exist = false; | |
| 485 | + | // Shuffle through invited character, see if they match your character. | |
| 486 | + | if ($inv_data !== false) foreach ($inv_data as $inv) { | |
| 487 | + | if ($charactersId[$i] == $inv['player_id']) { | |
| 488 | + | $exist = true; | |
| 489 | + | } | |
| 490 | + | } | |
| 491 | + | if ($exist) echo '<td>'. t('guild.join2'). '</td><td>'. t('guild.reject2'). '</td>'; | |
| 492 | + | } | |
| 493 | + | ?> | |
| 494 | + | </tr> | |
| 495 | + | <?php | |
| 496 | + | $bool = false; | |
| 497 | + | if ($inv_data !== false) foreach ($inv_data as $inv) { | |
| 498 | + | echo '<tr>'; | |
| 499 | + | echo '<td>'. $inv['name'] .'</td>'; | |
| 500 | + | // Remove invitation | |
| 501 | + | if ($highest_access == 2 || $highest_access == 3) { | |
| 502 | + | ?> <form action="" method="post"> <?php | |
| 503 | + | echo '<td>'; | |
| 504 | + | echo '<input type="hidden" name="uninvite" value="' . $inv['player_id'] . '" />'; | |
| 505 | + | echo '<input type="submit" value="'. t('guild.remove_invite') .'">'; | |
| 506 | + | echo '</td>'; | |
| 507 | + | ?> </form> <?php | |
| 508 | + | } | |
| 509 | + | // Join Guild | |
| 510 | + | ?> <form action="" method="post"> <?php | |
| 511 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 512 | + | if ($charactersId[$i] == $inv['player_id']) { | |
| 513 | + | echo '<td>'; | |
| 514 | + | echo '<input type="hidden" name="joinguild" value="' . $inv['player_id'] . '" />'; | |
| 515 | + | echo '<input type="submit" value="'. t('guild.join_btn') .'">'; | |
| 516 | + | echo '</td>'; | |
| 517 | + | $bool = true; | |
| 518 | + | } | |
| 519 | + | } | |
| 520 | + | if (isset($bool, $exist) && !$bool && $exist) { | |
| 521 | + | echo '<td></td>'; | |
| 522 | + | $bool = false; | |
| 523 | + | } | |
| 524 | + | ?> </form> <?php | |
| 525 | + | // Reject invitation | |
| 526 | + | ?> <form action="" method="post"> <?php | |
| 527 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 528 | + | if ($charactersId[$i] == $inv['player_id']) { | |
| 529 | + | echo '<td>'; | |
| 530 | + | echo '<input type="hidden" name="uninvite" value="' . $inv['player_id'] . '" />'; | |
| 531 | + | echo '<input type="submit" value="'. t('guild.reject_btn') .'">'; | |
| 532 | + | echo '</td>'; | |
| 533 | + | $bool = true; | |
| 534 | + | } | |
| 535 | + | } | |
| 536 | + | if (isset($bool, $exist) && !$bool && $exist) { | |
| 537 | + | echo '<td></td>'; | |
| 538 | + | $bool = false; | |
| 539 | + | } | |
| 540 | + | ?> </form> <?php | |
| 541 | + | echo '</tr>'; | |
| 542 | + | } | |
| 543 | + | ?> | |
| 544 | + | </table> | |
| 545 | + | <?php endif; ?> | |
| 546 | + | ||
| 547 | + | <!-- <?= t('guild.leader') ?> stuff --> | |
| 548 | + | <?php | |
| 549 | + | if (user_logged_in() === true) { | |
| 550 | + | ||
| 551 | + | // Uninvite and joinguild is also used for visitors who reject their invitation. | |
| 552 | + | if (!empty($_POST['uninvite'])) { | |
| 553 | + | // Is this action being triggered by a vice leader+, or the invited player? | |
| 554 | + | $u_player = (int)$_POST['uninvite']; | |
| 555 | + | $u_player = user_character_name($u_player); | |
| 556 | + | $u_player = (int)user_character_account_id($u_player); | |
| 557 | + | if (in_array($highest_access, array(2,3)) || $u_player === $session_user_id) { | |
| 558 | + | guild_remove_invitation($_POST['uninvite'], $gid); | |
| 559 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 560 | + | exit(); | |
| 561 | + | } | |
| 562 | + | } | |
| 563 | + | ||
| 564 | + | // Join guild | |
| 565 | + | if (!empty($_POST['joinguild'])) { | |
| 566 | + | $joining_player_id = (int)$_POST['joinguild']; | |
| 567 | + | $join_account = (int)user_character_account_id(user_character_name($joining_player_id)); | |
| 568 | + | ||
| 569 | + | if ($join_account !== $session_user_id) { | |
| 570 | + | echo '<font color="red" size="4">'. t('guild.wrong_acc_join2'). '</font>'; | |
| 571 | + | theme_close(); | |
| 572 | + | exit(); | |
| 573 | + | } | |
| 574 | + | ||
| 575 | + | // Join a guild | |
| 576 | + | if ($inv_data !== false) foreach ($inv_data as $inv) { | |
| 577 | + | if ((int)$inv['player_id'] == $joining_player_id) { | |
| 578 | + | $chardata['online'] = (user_is_online_10($joining_player_id)) ? 1 : 0; | |
| 579 | + | if ($chardata['online'] == 0) { | |
| 580 | + | ||
| 581 | + | // Ensure player is not already a member of another guild | |
| 582 | + | if (get_character_guild_rank($joining_player_id) === false) { | |
| 583 | + | if (guild_player_join($joining_player_id, (int)$gid)) { | |
| 584 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 585 | + | exit(); | |
| 586 | + | } else echo '<font color="red" size="4">'. t('guild.no_position2'). '</font>'; | |
| 587 | + | ||
| 588 | + | } else { | |
| 589 | + | $already_guild = get_player_guild_data($joining_player_id); | |
| 590 | + | $already_guild_name = get_guild_name($already_guild['guild_id']); | |
| 591 | + | ||
| 592 | + | echo '<font color="red" size="4">' . t('guild.you_are_already') . ' <strong>' . htmlspecialchars((string)$already_guild['rank_name'], ENT_QUOTES, 'UTF-8') . '</strong> ' . t('guild.of_another_guild') . ': <strong><a href="guilds.php?name=' . htmlspecialchars((string)$already_guild_name, ENT_QUOTES, 'UTF-8') . '">' . htmlspecialchars((string)$already_guild_name, ENT_QUOTES, 'UTF-8') . '</a></strong>.<br>' . t('guild.leave_first') . '</font>'; | |
| 593 | + | } | |
| 594 | + | ||
| 595 | + | } else echo '<font color="red" size="4">'. t('guild.offline_join2'). '</font>'; | |
| 596 | + | } | |
| 597 | + | } | |
| 598 | + | } | |
| 599 | + | ||
| 600 | + | // leave guild | |
| 601 | + | if (!empty($_POST['leave_guild'])) { | |
| 602 | + | $name = sanitize($_POST['leave_guild']); | |
| 603 | + | $cidd = user_character_id($name); | |
| 604 | + | ||
| 605 | + | $leave_account = (int)user_character_account_id($name); | |
| 606 | + | if ($leave_account !== $session_user_id) { | |
| 607 | + | echo '<font color="red" size="4">'. t('guild.wrong_acc_leave2'). '</font>'; | |
| 608 | + | theme_close(); | |
| 609 | + | exit(); | |
| 610 | + | } | |
| 611 | + | ||
| 612 | + | // If character is offline | |
| 613 | + | $chardata['online'] = (user_is_online_10($cidd)) ? 1 : 0; | |
| 614 | + | if ($chardata['online'] == 0) { | |
| 615 | + | guild_player_leave_10($cidd); | |
| 616 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 617 | + | exit(); | |
| 618 | + | } else echo '<font color="red" size="4">'. t('guild.offline_first2'). '</font>'; | |
| 619 | + | } | |
| 620 | + | ||
| 621 | + | // vice leader (and guild leader) actions | |
| 622 | + | if ($highest_access >= 2) { | |
| 623 | + | // Vice-leaders and leaders | |
| 624 | + | // Change Guild Nick | |
| 625 | + | if (!empty($_POST['player_guildnick'])) { | |
| 626 | + | if ($config['guild_allow_nicknames']) { | |
| 627 | + | $p_cid = user_character_id($_POST['player_guildnick']); | |
| 628 | + | $p_guild = get_player_guild_data($p_cid); | |
| 629 | + | ||
| 630 | + | if (preg_match("/^[a-zA-Z_ ]+$/", $_POST['guildnick']) || empty($_POST['guildnick'])) { | |
| 631 | + | // Only allow normal symbols as guild nick | |
| 632 | + | $p_nick = sanitize($_POST['guildnick']); | |
| 633 | + | ||
| 634 | + | if ($p_guild['guild_id'] == $gid) { | |
| 635 | + | $chardata['online'] = (user_is_online_10($p_cid)) ? 1 : 0; | |
| 636 | + | ||
| 637 | + | if ($chardata['online'] == 0) { | |
| 638 | + | update_player_guildnick_10($p_cid, $p_nick); | |
| 639 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 640 | + | exit(); | |
| 641 | + | } else echo '<font color="red" size="4">'. t('guild.not_offline'). '</font>'; | |
| 642 | + | } | |
| 643 | + | ||
| 644 | + | } else echo '<font color="red" size="4">Character guild nick may only contain a-z, A-Z and spaces.</font>'; | |
| 645 | + | } else echo '<font color="red" size="4">'. t('guild.nick_disabled2'). '</font>'; | |
| 646 | + | } | |
| 647 | + | ||
| 648 | + | // Invite character to guild | |
| 649 | + | if (!empty($_POST['invite'])) { | |
| 650 | + | if (user_character_exist($_POST['invite'])) { | |
| 651 | + | // Make sure they are not in another guild | |
| 652 | + | $charid = user_character_id($_POST['invite']); | |
| 653 | + | $membership = db()->fetchOne("SELECT `rank_id` FROM `guild_membership` WHERE `player_id` = ? LIMIT 1;", [$charid]); | |
| 654 | + | if (!$membership) { | |
| 655 | + | $status = false; | |
| 656 | + | if ($inv_data !== false) { | |
| 657 | + | foreach ($inv_data as $inv) { | |
| 658 | + | if ($inv['player_id'] == user_character_id($_POST['invite'])) $status = true; | |
| 659 | + | } | |
| 660 | + | } | |
| 661 | + | foreach ($players as $player) { | |
| 662 | + | if ($player['name'] == $_POST['invite']) $status = true; | |
| 663 | + | } | |
| 664 | + | ||
| 665 | + | if ($status == false) { | |
| 666 | + | guild_invite_player($charid, $gid); | |
| 667 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 668 | + | exit(); | |
| 669 | + | } else echo '<font color="red" size="4">That character is already invited(or a member) on this guild.</font>'; | |
| 670 | + | } else echo '<font color="red" size="4">'. t('guild.char_in_guild'). '</font>'; | |
| 671 | + | ||
| 672 | + | } else echo '<font color="red" size="4">'. t('guild.char_not_found'). '</font>'; | |
| 673 | + | } | |
| 674 | + | ||
| 675 | + | // Guild Message (motd) | |
| 676 | + | if (!empty($_POST['motd'])) { | |
| 677 | + | $motd = sanitize($_POST['motd']); | |
| 678 | + | db()->execute("UPDATE `guilds` SET `motd` = ? WHERE `id` = ? LIMIT 1;", [$motd, $gid]); | |
| 679 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 680 | + | exit(); | |
| 681 | + | } | |
| 682 | + | ||
| 683 | + | // Remove guild member | |
| 684 | + | if (!empty($_POST['remove_member'])) { | |
| 685 | + | $name = sanitize($_POST['remove_member']); | |
| 686 | + | $cid = user_character_id($name); | |
| 687 | + | ||
| 688 | + | $p_guild = get_player_guild_data($cid); | |
| 689 | + | if ($p_guild['guild_id'] == $gid) { | |
| 690 | + | guild_remove_member_10($cid); | |
| 691 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 692 | + | exit(); | |
| 693 | + | } | |
| 694 | + | } | |
| 695 | + | ||
| 696 | + | // Create forum guild board | |
| 697 | + | if (!empty($_POST['forumGuildId'])) { | |
| 698 | + | ||
| 699 | + | if ($config['forum']['guildboard'] === true) { | |
| 700 | + | $forumExist = db()->fetchOne("SELECT `id` FROM `znote_forum` WHERE `guild_id` = ? LIMIT 1;", [$gid]); | |
| 701 | + | ||
| 702 | + | if ($forumExist === false) { | |
| 703 | + | // Insert data | |
| 704 | + | db()->execute(" | |
| 705 | + | INSERT INTO `znote_forum` | |
| 706 | + | (`name`, `access`, `closed`, `hidden`, `guild_id`) | |
| 707 | + | VALUES ('Guild', 1, 0, 0, ?) | |
| 708 | + | ;", [$gid]); | |
| 709 | + | echo '<h1>'. t('guild.board_created2'). '</h1>'; | |
| 710 | + | } else echo '<h1>'. t('guild.board_exists2'). '</h1>'; | |
| 711 | + | ||
| 712 | + | } else { | |
| 713 | + | echo '<h1>'. t('guild.board_disabled2'). '</h1>'; | |
| 714 | + | } | |
| 715 | + | } | |
| 716 | + | ||
| 717 | + | // Guildwar actions | |
| 718 | + | if ($config['guildwar_enabled'] === true) { | |
| 719 | + | ||
| 720 | + | // Invite another guild to war | |
| 721 | + | if (!empty($_POST['warinvite'])) { | |
| 722 | + | $targetGuild = get_guild_id($_POST['warinvite']); | |
| 723 | + | if ($targetGuild) { | |
| 724 | + | $status = false; | |
| 725 | + | ||
| 726 | + | $war_invite = db()->fetchOne("SELECT `id` FROM `guilds` WHERE `id` = ?;", [$gid]); | |
| 727 | + | if ($war_invite !== false) { | |
| 728 | + | foreach ($war_invite as $inv) { | |
| 729 | + | if ($inv['id'] == $targetGuild) $status = true; | |
| 730 | + | } | |
| 731 | + | } | |
| 732 | + | ||
| 733 | + | $check_guild = get_guild_name($gid); | |
| 734 | + | foreach ($check_guild as $guild) { | |
| 735 | + | if ($guild['name'] == $_POST['warinvite']) $status = true; | |
| 736 | + | } | |
| 737 | + | ||
| 738 | + | if ((int)$gid === (int)$targetGuild) $status = true; | |
| 739 | + | ||
| 740 | + | $wars = db()->fetchAll(" | |
| 741 | + | SELECT | |
| 742 | + | `id`, `guild1`, `guild2`, `status` | |
| 743 | + | FROM `guild_wars` | |
| 744 | + | WHERE (`guild1` = ? OR `guild1` = ?) | |
| 745 | + | AND (`guild2` = ? OR `guild2` = ?) | |
| 746 | + | AND `status` IN (0, 1) | |
| 747 | + | ;", [$gid, $targetGuild, $gid, $targetGuild]); | |
| 748 | + | ||
| 749 | + | if ($status == false && $wars == false) { | |
| 750 | + | guild_war_invitation($gid, $targetGuild); | |
| 751 | + | $limit = (empty($_POST['limit'])) ? 100 : (int)$_POST['limit']; | |
| 752 | + | db()->execute("INSERT INTO `znote_guild_wars` (`limit`) VALUES (?);", [$limit]); | |
| 753 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 754 | + | exit(); | |
| 755 | + | } else echo '<font color="red" size="4">This guild has already been invited to war(or you\'re trying to invite your own).</FONT>'; | |
| 756 | + | ||
| 757 | + | } else echo '<font color="red" size="4">'. t('guild.not_found'). '</font>'; | |
| 758 | + | } | |
| 759 | + | ||
| 760 | + | if (!empty($_POST['cancel_war_invite'])) { | |
| 761 | + | cancel_war_invitation($_POST['cancel_war_invite'], $gid); | |
| 762 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 763 | + | exit(); | |
| 764 | + | } | |
| 765 | + | ||
| 766 | + | if (!empty($_POST['reject_war_invite'])) { | |
| 767 | + | reject_war_invitation($_POST['reject_war_invite'], $gid); | |
| 768 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 769 | + | exit(); | |
| 770 | + | } | |
| 771 | + | ||
| 772 | + | if (!empty($_POST['accept_war_invite'])) { | |
| 773 | + | accept_war_invitation($_POST['accept_war_invite'], $gid); | |
| 774 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 775 | + | exit(); | |
| 776 | + | } | |
| 777 | + | } | |
| 778 | + | ||
| 779 | + | $members = count_guild_members($gid); | |
| 780 | + | $ranks = get_guild_rank_data($gid); | |
| 781 | + | ||
| 782 | + | // Form to create guild board | |
| 783 | + | if ($config['forum']['guildboard'] === true && $config['forum']['enabled'] === true) { | |
| 784 | + | $forumExist = db()->fetchOne("SELECT `id` FROM `znote_forum` WHERE `guild_id` = ? LIMIT 1;", [$gid]); | |
| 785 | + | if ($forumExist === false) { | |
| 786 | + | ?> | |
| 787 | + | <form action="" method="post"> | |
| 788 | + | <ul> | |
| 789 | + | <li><?= t('guild.create_board2') ?><br> | |
| 790 | + | <input type="hidden" name="forumGuildId" value="<?php echo $gid; ?>"> | |
| 791 | + | <input type="submit" value="<?= t('guild.create_board_btn') ?>"> | |
| 792 | + | </ul> | |
| 793 | + | </form> | |
| 794 | + | <?php | |
| 795 | + | } | |
| 796 | + | } | |
| 797 | + | ||
| 798 | + | // Form to upload guild logo | |
| 799 | + | if ($config['use_guild_logos']) { | |
| 800 | + | ?> | |
| 801 | + | <form action="" method="post" enctype="multipart/form-data"> | |
| 802 | + | <ul> | |
| 803 | + | <li>Upload guild logo [.gif images only, 100x100px size]:<br> | |
| 804 | + | <input type="file" name="file" id="file" accept="image/gif"> | |
| 805 | + | <input type="submit" name="submit" value="<?= t('guild.upload_logo') ?>"> | |
| 806 | + | </li> | |
| 807 | + | </ul> | |
| 808 | + | </form> | |
| 809 | + | <?php | |
| 810 | + | if (!empty($_FILES['file'])) { | |
| 811 | + | check_image($_FILES['file']); | |
| 812 | + | echo '<br><br>'; | |
| 813 | + | } | |
| 814 | + | } | |
| 815 | + | ?> | |
| 816 | + | ||
| 817 | + | <!-- forms to invite character --> | |
| 818 | + | <form action="" method="post"> | |
| 819 | + | <ul> | |
| 820 | + | <li><?= t('guild.invite_btn') ?> to guild:<br> | |
| 821 | + | <input type="text" name="invite" placeholder="<?= t('guild.char_name_placeholder') ?>"> | |
| 822 | + | <input type="submit" value="<?= t('guild.invite_btn') ?>"> | |
| 823 | + | </li> | |
| 824 | + | </ul> | |
| 825 | + | </form> | |
| 826 | + | ||
| 827 | + | <!-- Guild message of the day motd --> | |
| 828 | + | <form action="" method="post"> | |
| 829 | + | <ul> | |
| 830 | + | <li><?= t('guild.change_motd') ?></li> | |
| 831 | + | <li> | |
| 832 | + | <textarea name="motd" placeholder="<?= t('guild.message_placeholder') ?>" cols="50" rows="3"><?php echo $guild['motd']; ?></textarea><br> | |
| 833 | + | <input type="submit" value="<?= t('guild.update_motd') ?>"> | |
| 834 | + | </li> | |
| 835 | + | </ul> | |
| 836 | + | </form> | |
| 837 | + | ||
| 838 | + | <?php if ($config['guild_allow_nicknames']): ?> | |
| 839 | + | <!-- FORMS TO CHANGE GUILD NICK --> | |
| 840 | + | <form action="" method="post"> | |
| 841 | + | <ul> | |
| 842 | + | <li> | |
| 843 | + | Change Guild Nick:<br> | |
| 844 | + | <select name="player_guildnick"> | |
| 845 | + | <?php | |
| 846 | + | //$gid = get_guild_id($_GET['name']); | |
| 847 | + | //$players = get_guild_players($gid); | |
| 848 | + | foreach ($players as $player) { | |
| 849 | + | if ($player['rank_level'] != 3) { | |
| 850 | + | echo '<option value="'. $player['name'] .'">'. $player['name'] .'</option>'; | |
| 851 | + | } else { | |
| 852 | + | if ($highest_access == 3) { | |
| 853 | + | echo '<option value="'. $player['name'] .'">'. $player['name'] .'</option>'; | |
| 854 | + | } | |
| 855 | + | } | |
| 856 | + | } | |
| 857 | + | ?> | |
| 858 | + | </select> | |
| 859 | + | <input type="text" name="guildnick" maxlength="15" placeholder="<?= t('guild.leave_blank_placeholder') ?>"> | |
| 860 | + | <input type="submit" value="<?= t('guild.change_nick_btn') ?>"> | |
| 861 | + | </li> | |
| 862 | + | </ul> | |
| 863 | + | </form> | |
| 864 | + | <?php endif; ?> | |
| 865 | + | ||
| 866 | + | <?php if ($members > 1): ?> | |
| 867 | + | <!-- FORMS TO PROMOTE CHARACTER--> | |
| 868 | + | <form action="" method="post"> | |
| 869 | + | <ul> | |
| 870 | + | <li> | |
| 871 | + | Promote Character:<br> | |
| 872 | + | <select name="promote_character"> | |
| 873 | + | <?php | |
| 874 | + | //$gid = get_guild_id($_GET['name']); | |
| 875 | + | //$players = get_guild_players($gid); | |
| 876 | + | foreach ($players as $player) { | |
| 877 | + | if ($player['rank_level'] != 3) { | |
| 878 | + | echo '<option value="'. $player['name'] .'">'. $player['name'] .'</option>'; | |
| 879 | + | } | |
| 880 | + | } | |
| 881 | + | ?> | |
| 882 | + | </select> | |
| 883 | + | <select name="promote_position"> | |
| 884 | + | <?php | |
| 885 | + | foreach ($ranks as $rank) { | |
| 886 | + | if ($rank['level'] != 3) { | |
| 887 | + | if ($rank['level'] != 2) { | |
| 888 | + | echo '<option value="'. $rank['id'] .'">'. $rank['name'] .'</option>'; | |
| 889 | + | } else { | |
| 890 | + | if ($highest_access == 3) { | |
| 891 | + | echo '<option value="'. $rank['id'] .'">'. $rank['name'] .'</option>'; | |
| 892 | + | } | |
| 893 | + | } | |
| 894 | + | } | |
| 895 | + | } | |
| 896 | + | ?> | |
| 897 | + | </select> | |
| 898 | + | <input type="submit" value="<?= t('guild.promote') ?>"> | |
| 899 | + | </li> | |
| 900 | + | </ul> | |
| 901 | + | </form> | |
| 902 | + | ||
| 903 | + | <!-- Remove member from guild --> | |
| 904 | + | <form action="" method="post"> | |
| 905 | + | <ul> | |
| 906 | + | <li> | |
| 907 | + | Kick member from guild:<br> | |
| 908 | + | <select name="remove_member"> | |
| 909 | + | <?php | |
| 910 | + | //$gid = get_guild_id($_GET['name']); | |
| 911 | + | //$players = get_guild_players($gid); | |
| 912 | + | foreach ($players as $player) { | |
| 913 | + | if ($player['rank_level'] != 3) { | |
| 914 | + | if ($player['rank_level'] != 2) { | |
| 915 | + | echo '<option value="'. $player['name'] .'">'. $player['name'] .'</option>'; | |
| 916 | + | } else { | |
| 917 | + | if ($highest_access == 3) { | |
| 918 | + | echo '<option value="'. $player['name'] .'">'. $player['name'] .'</option>'; | |
| 919 | + | } | |
| 920 | + | } | |
| 921 | + | } | |
| 922 | + | } | |
| 923 | + | ?> | |
| 924 | + | </select> | |
| 925 | + | <input type="submit" value="<?= t('guild.remove_member') ?>"> | |
| 926 | + | </li> | |
| 927 | + | </ul> | |
| 928 | + | </form> | |
| 929 | + | <?php endif; ?> | |
| 930 | + | <br><br> | |
| 931 | + | <?php | |
| 932 | + | } // end vice leader+ | |
| 933 | + | ||
| 934 | + | // Guild leaders only | |
| 935 | + | if ($highest_access == 3) { | |
| 936 | + | // Promote character to guild position | |
| 937 | + | if (!empty($_POST['promote_character']) && !empty($_POST['promote_position'])) { | |
| 938 | + | // Verify that promoted character is from this guild. | |
| 939 | + | $p_rid = $_POST['promote_position']; | |
| 940 | + | $p_cid = user_character_id($_POST['promote_character']); | |
| 941 | + | $p_guild = get_player_guild_data($p_cid); | |
| 942 | + | ||
| 943 | + | if ($p_guild['guild_id'] == $gid) { | |
| 944 | + | $chardata['online'] = (user_is_online_10($p_cid)) ? 1 : 0; | |
| 945 | + | if ($chardata['online'] == 0) { | |
| 946 | + | update_player_guild_position_10($p_cid, $p_rid); | |
| 947 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 948 | + | exit(); | |
| 949 | + | } else echo '<font color="red" size="4">'. t('guild.not_offline'). '</font>'; | |
| 950 | + | } | |
| 951 | + | } | |
| 952 | + | ||
| 953 | + | // Disband guild | |
| 954 | + | if (!empty($_POST['disband'])) { | |
| 955 | + | //$gidd = (int)$_POST['disband']; | |
| 956 | + | $members = get_guild_players($gid); | |
| 957 | + | $online = false; | |
| 958 | + | ||
| 959 | + | // First figure out if anyone are online. | |
| 960 | + | foreach ($members as $member) { | |
| 961 | + | $chardata['online'] = (user_is_online_10(user_character_id($member['name']))) ? 1 : 0; | |
| 962 | + | if ($chardata['online'] == 1) { | |
| 963 | + | $online = true; | |
| 964 | + | } | |
| 965 | + | } | |
| 966 | + | ||
| 967 | + | if (!$online) { | |
| 968 | + | // Then remove guild rank from every player. | |
| 969 | + | foreach ($members as $member) | |
| 970 | + | guild_player_leave_10(user_character_id($member['name'])); | |
| 971 | + | ||
| 972 | + | // Remove all guild invitations to this guild | |
| 973 | + | if ($inv_count > 0) | |
| 974 | + | guild_remove_invites($gid); | |
| 975 | + | ||
| 976 | + | // Then remove the guild itself. | |
| 977 | + | guild_delete($gid); | |
| 978 | + | header('Location: success.php'); | |
| 979 | + | exit(); | |
| 980 | + | } else echo '<font color="red" size="4">'. t('guild.disband_offline'). '</font>'; | |
| 981 | + | } | |
| 982 | + | ||
| 983 | + | // Change guild leader | |
| 984 | + | if (!empty($_POST['new_leader'])) { | |
| 985 | + | $new_leader = (int)$_POST['new_leader']; | |
| 986 | + | $old_leader = guild_leader($gid); | |
| 987 | + | ||
| 988 | + | $online = false; | |
| 989 | + | $newData['online'] = (user_is_online_10($new_leader)) ? 1 : 0; | |
| 990 | + | $oldData['online'] = (user_is_online_10($old_leader)) ? 1 : 0; | |
| 991 | + | ||
| 992 | + | if ($newData['online'] == 1 || $oldData['online'] == 1) $online = true; | |
| 993 | + | ||
| 994 | + | if ($online == false) { | |
| 995 | + | if (guild_change_leader($new_leader, $old_leader)) { | |
| 996 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 997 | + | exit(); | |
| 998 | + | } else echo '<font color="red" size="4">Something went wrong when attempting to change leadership.</font>'; | |
| 999 | + | } else echo '<font color="red" size="4">The new and old leader must be offline to change leadership.</font>'; | |
| 1000 | + | } | |
| 1001 | + | ||
| 1002 | + | // Change guild ranks | |
| 1003 | + | if (!empty($_POST['change_ranks'])) { | |
| 1004 | + | //$c_gid = (int)$_POST['change_ranks']; | |
| 1005 | + | $c_ranks = get_guild_rank_data($gid); | |
| 1006 | + | $rank_data = array(); | |
| 1007 | + | $rank_ids = array(); | |
| 1008 | + | ||
| 1009 | + | // Feed new rank data | |
| 1010 | + | foreach ($c_ranks as $rank) { | |
| 1011 | + | $tmp = 'rank_name!'. $rank['level']; | |
| 1012 | + | if (!empty($_POST[$tmp])) { | |
| 1013 | + | $rank_data[$rank['level']] = sanitize($_POST[$tmp]); | |
| 1014 | + | $rank_ids[$rank['level']] = $rank['id']; | |
| 1015 | + | } | |
| 1016 | + | } | |
| 1017 | + | ||
| 1018 | + | // Change guild rank name. | |
| 1019 | + | foreach ($rank_data as $level => $name) { | |
| 1020 | + | $rid = (int)$rank_ids[$level]; | |
| 1021 | + | $name = sanitize($name); | |
| 1022 | + | db()->execute("UPDATE `guild_ranks` SET `name` = ? WHERE `id` = ?", [$name, $rid]); | |
| 1023 | + | } | |
| 1024 | + | ||
| 1025 | + | header('Location: guilds.php?name='. $_GET['name']); | |
| 1026 | + | exit(); | |
| 1027 | + | } | |
| 1028 | + | ?> | |
| 1029 | + | ||
| 1030 | + | <!-- forms to change rank titles --> | |
| 1031 | + | <form action="" method="post"> | |
| 1032 | + | <ul> | |
| 1033 | + | <li><b><?= t('guild.change_ranks') ?></b><br> | |
| 1034 | + | <?php | |
| 1035 | + | $rank_count = 1; | |
| 1036 | + | foreach ($ranks as $rank) { | |
| 1037 | + | echo '<input type="text" name="rank_name!'. $rank['level'] .'" value="'. $rank['name'] .'">'; | |
| 1038 | + | } | |
| 1039 | + | echo '<input type="hidden" name="change_ranks" value="' . $gid . '" />'; | |
| 1040 | + | ?> | |
| 1041 | + | <input type="submit" value="<?= t('guild.update_ranks') ?>"> | |
| 1042 | + | </li> | |
| 1043 | + | </ul> | |
| 1044 | + | </form> | |
| 1045 | + | ||
| 1046 | + | <!-- forms to disband guild --> | |
| 1047 | + | <form action="" method="post"> | |
| 1048 | + | <ul> | |
| 1049 | + | <li><b>DELETE GUILD (All members must be offline):</b><br> | |
| 1050 | + | <?php echo '<input type="hidden" name="disband" value="' . $gid . '" />'; ?> | |
| 1051 | + | <input type="submit" value="<?= t('guild.disband_btn') ?>"> | |
| 1052 | + | </li> | |
| 1053 | + | </ul> | |
| 1054 | + | </form> | |
| 1055 | + | ||
| 1056 | + | <!-- forms to change leadership--> | |
| 1057 | + | <?php if ($members > 1): ?> | |
| 1058 | + | <form action="" method="post"> | |
| 1059 | + | <ul> | |
| 1060 | + | <li><b><?= t('guild.change_leader') ?></b><br> | |
| 1061 | + | <select name="new_leader"> | |
| 1062 | + | <?php | |
| 1063 | + | //$gid = get_guild_id($_GET['name']); | |
| 1064 | + | //$players = get_guild_players($gid); | |
| 1065 | + | foreach ($players as $player) { | |
| 1066 | + | if ($player['rank_level'] != 3) { | |
| 1067 | + | echo '<option value="'. $player['id'] .'">'. $player['name'] .'</option>'; | |
| 1068 | + | } | |
| 1069 | + | } | |
| 1070 | + | ?> | |
| 1071 | + | </select> | |
| 1072 | + | <input type="submit" value="<?= t('guild.change_leader_btn') ?>"> | |
| 1073 | + | </li> | |
| 1074 | + | </ul> | |
| 1075 | + | </form> | |
| 1076 | + | <?php endif; ?> | |
| 1077 | + | ||
| 1078 | + | <!-- Guild war management --> | |
| 1079 | + | <?php if ($config['guildwar_enabled'] === true) { ?> | |
| 1080 | + | <h2><?= t('guild.war_management2') ?></h2> | |
| 1081 | + | <form action="" method="post"> | |
| 1082 | + | <ul> | |
| 1083 | + | <li><?= t('guild.invite_war') ?><br> | |
| 1084 | + | <input type="text" name="warinvite" placeholder="<?= t('guild.name_placeholder') ?>"> | |
| 1085 | + | <input type="number" min="10" max="999" name="limit"> | |
| 1086 | + | <input type="submit" value="<?= t('guild.invite_guild_btn') ?>"> | |
| 1087 | + | </li> | |
| 1088 | + | </ul> | |
| 1089 | + | </form> | |
| 1090 | + | ||
| 1091 | + | <style type="text/css"> | |
| 1092 | + | form {display: inline;} | |
| 1093 | + | #btnspace{margin-left:100px;} | |
| 1094 | + | </style> | |
| 1095 | + | ||
| 1096 | + | <table id="guildsTable" class="table table-striped table-hover"> | |
| 1097 | + | <tr class="yellow"> | |
| 1098 | + | <th><?= t('guild.aggressor') ?></th> | |
| 1099 | + | <th><?= t('guild.information') ?></th> | |
| 1100 | + | <th><?= t('guild.enemy') ?></th> | |
| 1101 | + | </tr> | |
| 1102 | + | <?php | |
| 1103 | + | $i = 0; | |
| 1104 | + | $wars = db()->fetchAll(" | |
| 1105 | + | SELECT | |
| 1106 | + | `guild1`, `guild2`, `name1`, `name2`, `started`, | |
| 1107 | + | ( SELECT `limit` | |
| 1108 | + | FROM `znote_guild_wars` | |
| 1109 | + | WHERE `znote_guild_wars`.`id` = `guild_wars`.`id` | |
| 1110 | + | ) AS `limit` | |
| 1111 | + | FROM `guild_wars` | |
| 1112 | + | WHERE (`guild1` = ? OR `guild2` = ?) | |
| 1113 | + | AND `status` = 0 | |
| 1114 | + | ORDER BY `started` DESC | |
| 1115 | + | ", [$gid, $gid]); | |
| 1116 | + | if (!empty($wars) || $wars !== false) { | |
| 1117 | + | foreach($wars as $war): | |
| 1118 | + | $i++; | |
| 1119 | + | $row = "<tr>"; | |
| 1120 | + | $row .= "<td><a href='guilds.php?name={$war['name1']}'>{$war['name1']}</a></td>"; | |
| 1121 | + | ||
| 1122 | + | $row .= '<td><center>'; | |
| 1123 | + | ||
| 1124 | + | $row .= '<b>'. t('guild.pending_invite'). '</b>'; | |
| 1125 | + | $row .= '<br />'. t('guild.invited_on'). ' '. getClock($war['started'], true) .'.'; | |
| 1126 | + | $row .= "<br />The frag limit is set to {$war['limit']} frags.<br />"; | |
| 1127 | + | ||
| 1128 | + | if ($war['guild1'] == $gid) { | |
| 1129 | + | $row .= '<br><form action="" method="post">'; | |
| 1130 | + | $row .= "<input type='hidden' name='cancel_war_invite' value='{$war['guild2']}' />"; | |
| 1131 | + | $row .= '<input type="submit" value="'. t('guild.cancel_btn') .'">'; | |
| 1132 | + | $row .= '</form>'; | |
| 1133 | + | } else if ($war['guild2'] == $gid) { | |
| 1134 | + | $row .= '<br><form action="" method="post">'; | |
| 1135 | + | $row .= "<input type='hidden' name='accept_war_invite' value='{$war['guild1']}' />"; | |
| 1136 | + | $row .= '<input type="submit" value="'. t('guild.accept_btn') .'">'; | |
| 1137 | + | $row .= '</form>'; | |
| 1138 | + | ||
| 1139 | + | $row .= '<form action="" method="post">'; | |
| 1140 | + | $row .= "<input type='hidden' name='reject_war_invite' value='{$war['guild1']}' />"; | |
| 1141 | + | $row .= '<input type="submit" ID="btnspace" value="'. t('guild.reject_btn') .'">'; | |
| 1142 | + | $row .= '</form>'; | |
| 1143 | + | } | |
| 1144 | + | $row .= '</center></td>'; | |
| 1145 | + | $row .= "<td><a href='guilds.php?name={$war['name2']}'>{$war['name2']}</a></td>"; | |
| 1146 | + | $row .= '</tr>'; | |
| 1147 | + | echo $row; | |
| 1148 | + | endforeach; | |
| 1149 | + | } | |
| 1150 | + | ||
| 1151 | + | if ($i == 0) echo '<tr><td colspan="3"><center>'. t('guild.no_invites2'). '</center></td></tr>'; | |
| 1152 | + | ?> | |
| 1153 | + | </table> | |
| 1154 | + | <?php | |
| 1155 | + | } | |
| 1156 | + | } // end leader+ | |
| 1157 | + | } // end user_logged_in() === true | |
| 1158 | + | ?> | |
| 1159 | + | <!-- end leader and vice leader --> | |
| 1160 | + | ||
| 1161 | + | <?php | |
| 1162 | + | if ($config['guildwar_enabled'] === true) { | |
| 1163 | + | $wardata = get_guild_wars(); | |
| 1164 | + | $war_exist = false; | |
| 1165 | + | if ($wardata !== false) { | |
| 1166 | + | foreach ($wardata as $wars) { | |
| 1167 | + | if ($wars['guild1'] == $gid || $wars['guild2'] == $gid) $war_exist = true; | |
| 1168 | + | } | |
| 1169 | + | } | |
| 1170 | + | ||
| 1171 | + | if ($war_exist) { | |
| 1172 | + | ?> | |
| 1173 | + | <h2><?= t('guild.war_overview') ?></h2> | |
| 1174 | + | <table> | |
| 1175 | + | <tr class="yellow"> | |
| 1176 | + | <td><?= t('guild.attacker') ?></td> | |
| 1177 | + | <td><?= t('guild.defender') ?></td> | |
| 1178 | + | <td>status:</td> | |
| 1179 | + | <td>started:</td> | |
| 1180 | + | </tr> | |
| 1181 | + | <?php | |
| 1182 | + | foreach ($wardata as $wars): | |
| 1183 | + | if ($wars['guild1'] == $gid || $wars['guild2'] == $gid): | |
| 1184 | + | $url = url("guildwar.php?warid=". $wars['id']); | |
| 1185 | + | ?> | |
| 1186 | + | <tr class="special" onclick="javascript:window.location.href='<?php echo $url; ?>'"> | |
| 1187 | + | <td><?php echo $wars['name1']; ?></td> | |
| 1188 | + | <td><?php echo $wars['name2']; ?></td> | |
| 1189 | + | <td><?php echo $config['war_status'][$wars['status']]; ?></td> | |
| 1190 | + | <td><?php echo getClock($wars['started'], true); ?></td> | |
| 1191 | + | </tr> | |
| 1192 | + | <?php | |
| 1193 | + | endif; | |
| 1194 | + | endforeach; | |
| 1195 | + | ?> | |
| 1196 | + | </table> | |
| 1197 | + | <?php | |
| 1198 | + | } | |
| 1199 | + | } | |
| 1200 | + | ?> | |
| 1201 | + | ||
| 1202 | + | <!-- leave guild with character --> | |
| 1203 | + | <?php | |
| 1204 | + | $bool = false; | |
| 1205 | + | if (user_logged_in() === true) { | |
| 1206 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 1207 | + | foreach ($players as $player) { | |
| 1208 | + | if ($player['name'] == $characters[$i]) $bool = true; | |
| 1209 | + | } | |
| 1210 | + | } | |
| 1211 | + | if ($bool) { | |
| 1212 | + | $forumExist = db()->fetchOne("SELECT `id` FROM `znote_forum` WHERE `guild_id` = ? LIMIT 1;", [$gid]); | |
| 1213 | + | if ($forumExist !== false) { | |
| 1214 | + | ?> - <font size="4"><a href="forum.php?cat=<?php echo $forumExist['id']; ?>"><?= t('guild.visit_board') ?></a></font><br><br><br><?php | |
| 1215 | + | } | |
| 1216 | + | ?> | |
| 1217 | + | <form action="" method="post"> | |
| 1218 | + | <ul> | |
| 1219 | + | <li> | |
| 1220 | + | <?= t('guild.leave_btn') ?>:<br> | |
| 1221 | + | <select name="leave_guild"> | |
| 1222 | + | <option disabled><?= t('guild.with_placeholder') ?></option> | |
| 1223 | + | <?php | |
| 1224 | + | for ($i = 0; $i < $char_count; $i++) { | |
| 1225 | + | foreach ($players as $player) { | |
| 1226 | + | if ($player['name'] == $characters[$i]) { | |
| 1227 | + | $data = get_player_guild_data($player['id']); | |
| 1228 | + | if ($data['rank_level'] != 3) echo '<option value="'. $characters[$i] .'">'. $characters[$i] .'</option>'; | |
| 1229 | + | else echo '<option disabled>'. $characters[$i] .' [disabled:'. t('guild.leader'). ']</option>'; | |
| 1230 | + | } | |
| 1231 | + | } | |
| 1232 | + | } | |
| 1233 | + | ?> | |
| 1234 | + | </select> | |
| 1235 | + | <input type="submit" value="<?= t('guild.leave_btn') ?>"> | |
| 1236 | + | </li> | |
| 1237 | + | </ul> | |
| 1238 | + | </form> | |
| 1239 | + | <?php | |
| 1240 | + | } // display form if user has a character in guild | |
| 1241 | + | } // user logged in | |
| 1242 | + | $guildOverviewHtml = ob_get_clean(); | |
| 1243 | + | view('guild_overview', ['guildOverviewHtml' => $guildOverviewHtml, 'guild' => $guild]); | |
| 1244 | + | } // end view specific guild | |
| 1245 | + | theme_close(); ?> |
| @@ -0,0 +1,13 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | if ($config['require_login']['guildwars']) protect_page(); | |
| 3 | + | if ($config['log_ip']) znote_visitor_insert_detailed_data(3); | |
| 4 | + | if ($config['guildwar_enabled'] === false) { | |
| 5 | + | header('Location: guilds.php'); | |
| 6 | + | exit(); | |
| 7 | + | } | |
| 8 | + | $isOtx = ($config['CustomVersion'] == 'OTX') ? true : false; | |
| 9 | + | theme_open(); | |
| 10 | + | ||
| 11 | + | view('guildwar'); | |
| 12 | + | ||
| 13 | + | theme_close(); |
| @@ -0,0 +1,93 @@ | |||
| 1 | + | <?php | |
| 2 | + | require_once 'engine/init.php'; | |
| 3 | + | protect_page(); | |
| 4 | + | theme_open(); | |
| 5 | + | ||
| 6 | + | $view = (isset($_GET['view']) && (int)$_GET['view'] > 0) ? (int)$_GET['view'] : false; | |
| 7 | + | if ($view !== false) { | |
| 8 | + | if (!empty($_POST['reply_text'])) { | |
| 9 | + | ||
| 10 | + | // Save ticket reply on database | |
| 11 | + | $query = array( | |
| 12 | + | 'tid' => $view, | |
| 13 | + | 'username'=> getValue($_POST['username'] ?? null), | |
| 14 | + | 'message' => getValue($_POST['reply_text'] ?? null), | |
| 15 | + | 'created' => time(), | |
| 16 | + | ); | |
| 17 | + | $fields = '`'. implode('`, `', array_keys($query)) .'`'; | |
| 18 | + | $placeholders = implode(', ', array_fill(0, count($query), '?')); | |
| 19 | + | db()->execute("INSERT INTO `znote_tickets_replies` ($fields) VALUES ($placeholders)", array_values($query)); | |
| 20 | + | db()->execute("UPDATE `znote_tickets` SET `status` = 'Player-Reply' WHERE `id` = ? LIMIT 1;", [$view]); | |
| 21 | + | } | |
| 22 | + | $ticketData = db()->fetchOne("SELECT * FROM znote_tickets WHERE id = ? LIMIT 1;", [$view]); | |
| 23 | + | ||
| 24 | + | if(!$ticketData || $ticketData['owner'] != $session_user_id) { | |
| 25 | + | echo t('helpdesk.no_access'); | |
| 26 | + | theme_close(); | |
| 27 | + | die; | |
| 28 | + | } | |
| 29 | + | $replies = db()->fetchAll("SELECT * FROM znote_tickets_replies WHERE tid = ? ORDER BY `created`;", [$view]); | |
| 30 | + | view('helpdesk_ticket'); | |
| 31 | + | } else { | |
| 32 | + | ||
| 33 | + | $account = db()->fetchOne("SELECT name,email FROM accounts WHERE id = ?", [$session_user_id]); | |
| 34 | + | if (!is_array($account)) $account = array(); | |
| 35 | + | $account += array('name' => '', 'email' => ''); | |
| 36 | + | if (!empty($_POST)) { | |
| 37 | + | $required_fields = array('username', 'email', 'subject', 'message'); | |
| 38 | + | foreach($_POST as $key=>$value) { | |
| 39 | + | if (empty($value) && in_array($key, $required_fields) === true) { | |
| 40 | + | $errors[] = t('reg.fill_all'); | |
| 41 | + | break 1; | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // check errors (= user exist, pass long enough | |
| 46 | + | if (empty($errors) === true) { | |
| 47 | + | /* Token used for cross site scripting security */ | |
| 48 | + | if (!Token::isValid($_POST['token'])) { | |
| 49 | + | $errors[] = t('login.token_invalid'); | |
| 50 | + | } | |
| 51 | + | if ($config['use_captcha']) { | |
| 52 | + | if(!verifyGoogleReCaptcha($_POST['g-recaptcha-response'])) { | |
| 53 | + | $errors[] = t('reg.captcha'); | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | // Reversed this if, so: first check if you need to validate, then validate. | |
| 57 | + | if ($config['validate_IP'] === true && validate_ip(getIP()) === false) { | |
| 58 | + | $errors[] = t('reg.bad_ip'); | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | } | |
| 62 | + | $tickets = db()->fetchAll("SELECT id,subject,creation,status FROM znote_tickets WHERE owner = ? ORDER BY creation DESC", [$session_user_id]); | |
| 63 | + | ||
| 64 | + | $helpdeskCreated = isset($_GET['success']) && empty($_GET['success']); | |
| 65 | + | ||
| 66 | + | if (!$helpdeskCreated && empty($_POST) === false && empty($errors) === true) { | |
| 67 | + | if ($config['log_ip']) { | |
| 68 | + | znote_visitor_insert_detailed_data(1); | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | //Save ticket on database | |
| 72 | + | $query = array( | |
| 73 | + | 'owner' => $session_user_id, | |
| 74 | + | 'username'=> getValue($_POST['username'] ?? null), | |
| 75 | + | 'subject' => getValue($_POST['subject'] ?? null), | |
| 76 | + | 'message' => getValue($_POST['message'] ?? null), | |
| 77 | + | 'ip' => getIPLong(), | |
| 78 | + | 'creation' => time(), | |
| 79 | + | 'status' => 'Open' | |
| 80 | + | ); | |
| 81 | + | ||
| 82 | + | $fields = '`'. implode('`, `', array_keys($query)) .'`'; | |
| 83 | + | $placeholders = implode(', ', array_fill(0, count($query), '?')); | |
| 84 | + | db()->execute("INSERT INTO `znote_tickets` ($fields) VALUES ($placeholders)", array_values($query)); | |
| 85 | + | ||
| 86 | + | header('Location: helpdesk.php?success'); | |
| 87 | + | exit(); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | view('helpdesk_list', ['helpdeskCreated' => $helpdeskCreated]); | |
| 91 | + | } | |
| 92 | + | theme_close(); | |
| 93 | + | ?> |
| @@ -0,0 +1,67 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; theme_open(); | |
| 2 | + | ||
| 3 | + | if ($config['log_ip']) { | |
| 4 | + | znote_visitor_insert_detailed_data(3); | |
| 5 | + | } | |
| 6 | + | ||
| 7 | + | // Fetch highscore type | |
| 8 | + | $type = (isset($_GET['type'])) ? (int)getValue($_GET['type'] ?? null) : 7; | |
| 9 | + | if ($type > 9) $type = 7; | |
| 10 | + | ||
| 11 | + | // Fetch highscore vocation | |
| 12 | + | $configVocations = $config['vocations']; | |
| 13 | + | //$debug['configVocations'] = $configVocations; | |
| 14 | + | ||
| 15 | + | $vocationIds = array_keys($configVocations); | |
| 16 | + | ||
| 17 | + | $vocation = 'all'; | |
| 18 | + | if (isset($_GET['vocation']) && is_numeric($_GET['vocation'])) { | |
| 19 | + | $vocation = (int)$_GET['vocation']; | |
| 20 | + | if (!in_array($vocation, $vocationIds)) { | |
| 21 | + | $vocation = "all"; | |
| 22 | + | } | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // Fetch highscore page | |
| 26 | + | $page = getValue($_GET['page'] ?? null); | |
| 27 | + | if (!$page || $page == 0) $page = 1; | |
| 28 | + | else $page = (int)$page; | |
| 29 | + | ||
| 30 | + | $highscore = $config['highscore']; | |
| 31 | + | $loadFlags = ($config['country_flags']['enabled'] && $config['country_flags']['highscores']) ? true : false; | |
| 32 | + | $loadOutfits = ($config['show_outfits']['highscores']) ? true : false; | |
| 33 | + | ||
| 34 | + | $rows = $highscore['rows']; | |
| 35 | + | $rowsPerPage = $highscore['rowsPerPage']; | |
| 36 | + | ||
| 37 | + | function skillName($type) { | |
| 38 | + | $types = array( | |
| 39 | + | 1 => "Club", | |
| 40 | + | 2 => "Sword", | |
| 41 | + | 3 => "Axe", | |
| 42 | + | 4 => "Distance", | |
| 43 | + | 5 => "Shield", | |
| 44 | + | 6 => "Fish", | |
| 45 | + | 7 => "Experience", // Hardcoded | |
| 46 | + | 8 => "Magic Level", // Hardcoded | |
| 47 | + | 9 => "Fist", // Since 0 returns false I will make 9 = 0. :) | |
| 48 | + | ); | |
| 49 | + | return $types[(int)$type]; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | function pageCheck($index, $page, $rowPerPage) { | |
| 53 | + | return ($index < ($page * $rowPerPage) && $index >= ($page * $rowPerPage) - $rowPerPage) ? true : false; | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | $cache = new Cache('engine/cache/highscores'); | |
| 57 | + | if ($cache->hasExpired()) { | |
| 58 | + | $vocGroups = fetchAllScores($rows, znote_server_adapter()->normalizedEngine(), $highscore['ignoreGroupId'], $configVocations, $vocation, $loadFlags, $loadOutfits); | |
| 59 | + | $cache->setContent($vocGroups); | |
| 60 | + | $cache->save(); | |
| 61 | + | } else { | |
| 62 | + | $vocGroups = $cache->load(); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | view('highscores'); | |
| 66 | + | ||
| 67 | + | theme_close(); |
| @@ -0,0 +1,369 @@ | |||
| 1 | + | <?php require_once 'engine/init.php'; | |
| 2 | + | znote_csrf_protect_public_post(); | |
| 3 | + | theme_open(); | |
| 4 | + | if ($config['log_ip']) { | |
| 5 | + | znote_visitor_insert_detailed_data(3); | |
| 6 | + | } | |
| 7 | + | ||
| 8 | + | $house = (isset($_GET['id']) && (int)$_GET['id'] > 0) ? (int)$_GET['id'] : false; | |
| 9 | + | $house_SQL = ""; | |
| 10 | + | $house_SQL_params = []; | |
| 11 | + | if ($house !== false) { | |
| 12 | + | $house_SQL = " | |
| 13 | + | SELECT | |
| 14 | + | `h`.`id`, `h`.`owner`, `h`.`paid`, `h`.`name`, `h`.`rent`, `h`.`town_id`, | |
| 15 | + | `h`.`size`, `h`.`beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder'), 'h') . ", | |
| 16 | + | `p`.`name` AS `ownername` | |
| 17 | + | FROM `houses` AS `h` | |
| 18 | + | LEFT JOIN `players` AS `p` | |
| 19 | + | ON `h`.`owner` > 0 | |
| 20 | + | AND `p`.`id` = `h`.`owner` | |
| 21 | + | WHERE `h`.`id` = ?; | |
| 22 | + | "; | |
| 23 | + | $house_SQL_params = [$house]; | |
| 24 | + | $house = db()->fetchOne($house_SQL, $house_SQL_params); | |
| 25 | + | if (!is_array($house)) { | |
| 26 | + | ?> | |
| 27 | + | <h1><?= t('house.not_found') ?></h1> | |
| 28 | + | <p><?= t('house.go_back') ?> <a href="houses.php">house list</a> and select a house for further details.</p> | |
| 29 | + | <?php | |
| 30 | + | theme_close(); | |
| 31 | + | exit; | |
| 32 | + | } | |
| 33 | + | $minbid = $config['houseConfig']['minimumBidSQM'] * $house['size']; | |
| 34 | + | if ($house['owner'] == 0) unset($house['ownername']); | |
| 35 | + | ||
| 36 | + | if ($config['houseConfig']['shopPoints']['enabled']) { | |
| 37 | + | $house['points'] = $house['size']; | |
| 38 | + | ||
| 39 | + | foreach ($config['houseConfig']['shopPoints']['cost'] AS $cost_sqm => $cost_points) { | |
| 40 | + | if ($cost_sqm < $house['size']) $house['points'] = $cost_points; | |
| 41 | + | } | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | //data_dump($house, false, "House data"); | |
| 45 | + | ||
| 46 | + | ////////////////////// | |
| 47 | + | // Bid on house logic | |
| 48 | + | $bid_char = &$_POST['char']; | |
| 49 | + | $bid_amount = &$_POST['amount']; | |
| 50 | + | if ($bid_amount && $bid_char) { | |
| 51 | + | $bid_char = (int)$bid_char; | |
| 52 | + | $bid_amount = (int)$bid_amount; | |
| 53 | + | ||
| 54 | + | $player = db()->fetchOne(" | |
| 55 | + | SELECT `id`, `account_id`, `name`, `level`, `balance` | |
| 56 | + | FROM `players` | |
| 57 | + | WHERE `id` = ? LIMIT 1; | |
| 58 | + | ", [$bid_char]); | |
| 59 | + | ||
| 60 | + | if (user_logged_in() === true && is_array($player) && $player['account_id'] == $session_user_id) { | |
| 61 | + | // Does player have or need premium? | |
| 62 | + | $premstatus = ($config['houseConfig']['requirePremium'] && $user_data['premdays'] == 0) ? false : true; | |
| 63 | + | if ($premstatus) { | |
| 64 | + | ||
| 65 | + | // Can player have or bid on more houses? | |
| 66 | + | $pHouseCount = db()->fetchOne(" | |
| 67 | + | SELECT COUNT('id') AS `value` | |
| 68 | + | FROM `houses` | |
| 69 | + | WHERE ( | |
| 70 | + | (`" . houseCol('highest_bidder') . "` = ? AND `owner` = ?) | |
| 71 | + | OR (`" . houseCol('highest_bidder') . "` = ?) | |
| 72 | + | OR (`owner` = ?) | |
| 73 | + | ) | |
| 74 | + | AND `id` != ? LIMIT 1; | |
| 75 | + | ", [$bid_char, $bid_char, $bid_char, $bid_char, $house['id']]); | |
| 76 | + | ||
| 77 | + | if ($pHouseCount['value'] < $config['houseConfig']['housesPerPlayer']) { | |
| 78 | + | // Is character level high enough? | |
| 79 | + | if ($player['level'] >= $config['houseConfig']['levelToBuyHouse']) { | |
| 80 | + | // Can player afford this bid? | |
| 81 | + | if ($player['balance'] > $bid_amount) { | |
| 82 | + | ||
| 83 | + | // Is bid higher than previous bid? | |
| 84 | + | if ($bid_amount > $house['bid']) { | |
| 85 | + | // Is bid higher than lowest bid? | |
| 86 | + | if ($bid_amount > $minbid) { | |
| 87 | + | // Should only apply to external players, allowing a player to up his pledge without | |
| 88 | + | // being forced to pay his full previous bid. | |
| 89 | + | if ($house['highest_bidder'] != $player['id']) $lastbid = $house['bid'] + 1; | |
| 90 | + | else { | |
| 91 | + | $lastbid = $house['last_bid']; | |
| 92 | + | echo "<b><font color='green'>You have raised the house pledge to ".$bid_amount."gp!</font></b><br>"; | |
| 93 | + | } | |
| 94 | + | // Has bid already started? | |
| 95 | + | if ($house['bid_end'] > 0) { | |
| 96 | + | if ($house['bid_end'] > time()) { | |
| 97 | + | ||
| 98 | + | db()->execute(" | |
| 99 | + | UPDATE `houses` | |
| 100 | + | SET | |
| 101 | + | `" . houseCol('highest_bidder') . "` = ?, | |
| 102 | + | `" . houseCol('bid') . "` = ?, | |
| 103 | + | `" . houseCol('last_bid') . "` = ? | |
| 104 | + | WHERE `id` = ? LIMIT 1; | |
| 105 | + | ", [$player['id'], $bid_amount, $lastbid, $house['id']]); | |
| 106 | + | ||
| 107 | + | $house = db()->fetchOne(" | |
| 108 | + | SELECT | |
| 109 | + | `id`, `owner`, `paid`, `name`, `rent`, `town_id`, `size`, | |
| 110 | + | `beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder')) . " | |
| 111 | + | FROM `houses` | |
| 112 | + | WHERE `id` = ?; | |
| 113 | + | ", [$house['id']]); | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | } else { | |
| 117 | + | $lastbid = $minbid + 1; | |
| 118 | + | $bidend = time() + $config['houseConfig']['auctionPeriod']; | |
| 119 | + | ||
| 120 | + | db()->execute(" | |
| 121 | + | UPDATE `houses` | |
| 122 | + | SET | |
| 123 | + | `" . houseCol('highest_bidder') . "` = ?, | |
| 124 | + | `" . houseCol('bid') . "` = ?, | |
| 125 | + | `" . houseCol('last_bid') . "` = ?, | |
| 126 | + | `" . houseCol('bid_end') . "` = ? | |
| 127 | + | WHERE `id` = ? LIMIT 1; | |
| 128 | + | ", [$player['id'], $bid_amount, $lastbid, $bidend, $house['id']]); | |
| 129 | + | ||
| 130 | + | $house = db()->fetchOne(" | |
| 131 | + | SELECT | |
| 132 | + | `id`, `owner`, `paid`, `name`, `rent`, `town_id`, `size`, | |
| 133 | + | `beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder')) . " | |
| 134 | + | FROM `houses` | |
| 135 | + | WHERE `id` = ?; | |
| 136 | + | ", [$house['id']]); | |
| 137 | + | ||
| 138 | + | } | |
| 139 | + | echo "<b><font color='green'>". t('house.highest_bid') ."</font></b>"; | |
| 140 | + | } else echo "<b><font color='red'>You need to place a bid that is higher or equal to {$minbid}gp.</font></b>"; | |
| 141 | + | ||
| 142 | + | } else { | |
| 143 | + | // Check if current bid is higher than last_bid | |
| 144 | + | if ($bid_amount > $house['last_bid']) { | |
| 145 | + | // Should only apply to external players, allowing a player to up his pledge without | |
| 146 | + | // being forced to pay his full previous bid. | |
| 147 | + | if ($house['highest_bidder'] != $player['id']) { | |
| 148 | + | $lastbid = $bid_amount + 1; | |
| 149 | + | ||
| 150 | + | db()->execute(" | |
| 151 | + | UPDATE `houses` | |
| 152 | + | SET `" . houseCol('last_bid') . "` = ? | |
| 153 | + | WHERE `id` = ? LIMIT 1; | |
| 154 | + | ", [$lastbid, $house['id']]); | |
| 155 | + | ||
| 156 | + | $house = db()->fetchOne(" | |
| 157 | + | SELECT | |
| 158 | + | `id`, `owner`, `paid`, `name`, `rent`, `town_id`, `size`, | |
| 159 | + | `beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder')) . " | |
| 160 | + | FROM `houses` | |
| 161 | + | WHERE `id` = ?; | |
| 162 | + | ", [$house['id']]); | |
| 163 | + | ||
| 164 | + | echo "<b><font color='orange'>Unfortunately your bid was not higher than previous bidder.</font></b>"; | |
| 165 | + | } else { | |
| 166 | + | echo "<b><font color='orange'>". t('house.already_higher') ."</font></b>"; | |
| 167 | + | } | |
| 168 | + | } else { | |
| 169 | + | echo "<b><font color='red'>" . t('house.bid_too_low') . "</font></b>"; | |
| 170 | + | } | |
| 171 | + | } | |
| 172 | + | } else echo "<b><font color='red'>" . t('house.not_enough2') . "</font></b>"; | |
| 173 | + | } else echo "<b><font color='red'>" . t('house.level_too_low', ['level' => $config['houseConfig']['levelToBuyHouse'] - 1]) . "</font></b>"; | |
| 174 | + | } else echo "<b><font color='red'>". t('house.too_many') ."</font></b>"; | |
| 175 | + | } else echo "<b><font color='red'>" . t('house.need_premium2') . "</font></b>"; | |
| 176 | + | } else echo "<b><font color='red'>" . t('house.own_char_only') . "</font></b>"; | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | //////////////////////////////////////// | |
| 180 | + | // Instantly buy house with shop points | |
| 181 | + | if ($config['houseConfig']['shopPoints']['enabled'] | |
| 182 | + | && isset($_POST['instantbuy']) | |
| 183 | + | && $bid_char | |
| 184 | + | && $house['owner'] == 0 | |
| 185 | + | && isset($house['points'])) { | |
| 186 | + | ||
| 187 | + | $account_points = (int)$user_znote_data['points']; | |
| 188 | + | ||
| 189 | + | if ($account_points >= $house['points']) { | |
| 190 | + | ||
| 191 | + | $bid_char = (int)$bid_char; | |
| 192 | + | $player = db()->fetchOne(" | |
| 193 | + | SELECT `id`, `account_id`, `name`, `level` | |
| 194 | + | FROM `players` | |
| 195 | + | WHERE `id` = ? LIMIT 1; | |
| 196 | + | ", [$bid_char]); | |
| 197 | + | ||
| 198 | + | $pHouseCount = db()->fetchOne(" | |
| 199 | + | SELECT COUNT('id') AS `value` | |
| 200 | + | FROM `houses` | |
| 201 | + | WHERE ( | |
| 202 | + | (`" . houseCol('highest_bidder') . "` = ? AND `owner` = ?) | |
| 203 | + | OR (`" . houseCol('highest_bidder') . "` = ?) | |
| 204 | + | OR (`owner` = ?) | |
| 205 | + | ) | |
| 206 | + | AND `id` != ? LIMIT 1; | |
| 207 | + | ", [$bid_char, $bid_char, $bid_char, $bid_char, $house['id']]); | |
| 208 | + | ||
| 209 | + | if (user_logged_in() === true | |
| 210 | + | && $player['account_id'] == $session_user_id | |
| 211 | + | && $player['level'] >= $config['houseConfig']['levelToBuyHouse'] | |
| 212 | + | && $pHouseCount['value'] < $config['houseConfig']['housesPerPlayer']) { | |
| 213 | + | ||
| 214 | + | $house_points = (int)$house['points']; | |
| 215 | + | $house_id = $house['id']; | |
| 216 | + | $time = time(); | |
| 217 | + | ||
| 218 | + | // Lock the account balance and the house row together, so two | |
| 219 | + | // concurrent purchases (or a purchase racing a bid) cannot both | |
| 220 | + | // succeed or spend points that were already spent. | |
| 221 | + | $purchased = db()->transaction(function ($db) use ($session_user_id, $bid_char, $house_id, $house_points, $time) { | |
| 222 | + | $account = $db->fetchOne("SELECT `points` FROM `znote_accounts` WHERE `account_id` = ? LIMIT 1 FOR UPDATE;", [$session_user_id]); | |
| 223 | + | if (!is_array($account) || (int)$account['points'] < $house_points) { | |
| 224 | + | return false; | |
| 225 | + | } | |
| 226 | + | ||
| 227 | + | $houseRow = $db->fetchOne("SELECT `owner` FROM `houses` WHERE `id` = ? LIMIT 1 FOR UPDATE;", [$house_id]); | |
| 228 | + | if (!is_array($houseRow) || (int)$houseRow['owner'] !== 0) { | |
| 229 | + | return false; | |
| 230 | + | } | |
| 231 | + | ||
| 232 | + | $db->execute("UPDATE `znote_accounts` SET `points` = `points` - ? WHERE `account_id` = ? LIMIT 1;", [$house_points, $session_user_id]); | |
| 233 | + | $db->execute("UPDATE `houses` SET `owner` = ? WHERE `id` = ? LIMIT 1;", [$bid_char, $house_id]); | |
| 234 | + | $db->execute(" | |
| 235 | + | INSERT INTO `znote_shop_logs` | |
| 236 | + | (`account_id`, `player_id`, `type`, `itemid`, `count`, `points`, `time`) VALUES | |
| 237 | + | (?, ?, 7, ?, 1, ?, ?) | |
| 238 | + | ", [$session_user_id, $bid_char, $house_id, $house_points, $time]); | |
| 239 | + | $db->execute(" | |
| 240 | + | INSERT INTO `znote_shop_orders` | |
| 241 | + | (`account_id`, `type`, `itemid`, `count`, `time`) VALUES | |
| 242 | + | (?, 7, ?, ?, ?) | |
| 243 | + | ", [$session_user_id, $house_id, $bid_char, $time]); | |
| 244 | + | ||
| 245 | + | return true; | |
| 246 | + | }); | |
| 247 | + | ||
| 248 | + | if ($purchased) { | |
| 249 | + | // Reload house data | |
| 250 | + | $house = db()->fetchOne($house_SQL, $house_SQL_params); | |
| 251 | + | $minbid = $config['houseConfig']['minimumBidSQM'] * $house['size']; | |
| 252 | + | if ($house['owner'] > 0) $house['ownername'] = user_name($house['owner']); | |
| 253 | + | ||
| 254 | + | // Congratulate user and tell them they still has to pay rent (if rent > 0) | |
| 255 | + | ?> | |
| 256 | + | <p><strong><?= t('house.congrats') ?></strong> | |
| 257 | + | <br>You now own this house! | |
| 258 | + | <br><?= t('house.remember_say') ?> <strong>!shop</strong> in-game to process your ownership! | |
| 259 | + | <?php if ($house['rent'] > 0): ?> | |
| 260 | + | <br>Keep in mind you still need to pay rent on this house, make sure you have enough bank balance to cover it! | |
| 261 | + | <?php endif; ?> | |
| 262 | + | </p> | |
| 263 | + | <?php | |
| 264 | + | } else { | |
| 265 | + | ?> | |
| 266 | + | <p><strong>Error:</strong> | |
| 267 | + | <br>This house was already bought or your points balance changed. Please refresh and try again. | |
| 268 | + | </p> | |
| 269 | + | <?php | |
| 270 | + | } | |
| 271 | + | } else { | |
| 272 | + | ?> | |
| 273 | + | <p><strong>Error:</strong> | |
| 274 | + | <br>Either your level is too low, or your player already have or is bidding on another house. | |
| 275 | + | <br><?= t('house.your_level') ?> <?php echo $player['level']; ?>. Minimum level to buy house: <?php echo $config['houseConfig']['levelToBuyHouse']; ?> | |
| 276 | + | <br><?= t('house.your_bids') ?> <?php echo $pHouseCount['value']; ?>. Maximum house per player: <?php echo $config['houseConfig']['housesPerPlayer']; ?>. | |
| 277 | + | </p> | |
| 278 | + | <?php | |
| 279 | + | } | |
| 280 | + | } | |
| 281 | + | } | |
| 282 | + | ||
| 283 | + | // HTML structure and logic | |
| 284 | + | ?> | |
| 285 | + | <h1><?= t('house.label') ?> <?php echo $house['name']; ?></h1> | |
| 286 | + | <ul> | |
| 287 | + | <li><b>Town</b>: | |
| 288 | + | <?php | |
| 289 | + | $town_name = &$config['towns'][$house['town_id']]; | |
| 290 | + | echo "<a href='houses.php?id={$house['town_id']}'>". ($town_name ? $town_name : 'Specify town id ' . $house['town_id'] . ' name in config.php first.') ."</a>"; | |
| 291 | + | ?></li> | |
| 292 | + | <li><b>Size</b>: <?php echo $house['size']; ?></li> | |
| 293 | + | <li><b>Beds</b>: <?php echo $house['beds']; ?></li> | |
| 294 | + | <li><b>Owner</b>: <?php | |
| 295 | + | if ($house['owner'] > 0) echo "<a href='characterprofile.php?name={$house['ownername']}' target='_BLANK'>{$house['ownername']}</a>"; | |
| 296 | + | else echo "Available for auction."; | |
| 297 | + | ?></li> | |
| 298 | + | <li><b>Rent</b>: <?php echo $house['rent']; ?></li> | |
| 299 | + | <?php if ($house['owner'] == 0 && isset($house['points'])): ?> | |
| 300 | + | <li><b><?= t('house.shop_points2') ?></b>: <?php echo $house['points']; ?></li> | |
| 301 | + | <?php endif; ?> | |
| 302 | + | </ul> | |
| 303 | + | <?php | |
| 304 | + | // AUCTION MARKUP INIT | |
| 305 | + | if ($house['owner'] == 0) { | |
| 306 | + | ?> | |
| 307 | + | <h2><?= t('house.on_auction2') ?></h2> | |
| 308 | + | <?php | |
| 309 | + | if ($house['highest_bidder'] == 0) echo "<b>" . t('house.no_bidders2') . "</b>"; | |
| 310 | + | else { | |
| 311 | + | $bidder = db()->fetchOne("SELECT `name` FROM `players` WHERE `id` = ? LIMIT 1;", [$house['highest_bidder']]); | |
| 312 | + | echo "<b>" . t('house.has_bidders') . "</b>"; | |
| 313 | + | echo "<br><b>" . t('house.active_bid') . "</b> {$house['last_bid']}gp"; | |
| 314 | + | echo "<br><b>" . t('house.active_bid_by') . "</b> <a href=\"characterprofile.php?name=" . htmlspecialchars((string)$bidder['name'], ENT_QUOTES, 'UTF-8') . "\" target=\"_BLANK\">" . htmlspecialchars((string)$bidder['name'], ENT_QUOTES, 'UTF-8') . "</a>"; | |
| 315 | + | echo "<br><b>" . t('house.bid_ends') . "</b> ". getClock($house['bid_end'], true); | |
| 316 | + | } | |
| 317 | + | ||
| 318 | + | if ($house['bid_end'] == 0 || $house['bid_end'] > time()) { | |
| 319 | + | if (user_logged_in()) { | |
| 320 | + | // Your characters, indexed by char_id | |
| 321 | + | $yourChars = db()->fetchAll("SELECT `id`, `name`, `balance` FROM `players` WHERE `account_id` = ?;", [$user_data['id']]); | |
| 322 | + | if ($yourChars !== false) { | |
| 323 | + | $charData = array(); | |
| 324 | + | foreach ($yourChars as $char) { | |
| 325 | + | $charData[$char['id']] = $char; | |
| 326 | + | } | |
| 327 | + | ?> | |
| 328 | + | <form class="house_form_bid" action="" method="post"> | |
| 329 | + | <select name="char"> | |
| 330 | + | <?php | |
| 331 | + | foreach ($charData as $id => $char) { | |
| 332 | + | echo "<option value='$id'>{$char['name']} [{$char['balance']}]</option>"; | |
| 333 | + | } | |
| 334 | + | ?> | |
| 335 | + | </select> | |
| 336 | + | <input type="text" name="amount" placeholder="<?= t('house.min_bid', ['amount' => $minbid + 1]) ?>"> | |
| 337 | + | <input type="submit" value="<?= t('house.bid_submit') ?>"> | |
| 338 | + | </form> | |
| 339 | + | <?php if ($house['owner'] == 0 && isset($house['points'])): ?> | |
| 340 | + | <br> | |
| 341 | + | <?php if ((int)$user_znote_data['points'] >= $house['points']): ?> | |
| 342 | + | <form class="house_form_buy" action="" method="post"> | |
| 343 | + | <p><?= t('house.your_account') ?> <strong><?php echo $user_znote_data['points']; ?></strong> available shop points.</p> | |
| 344 | + | <select name="char"> | |
| 345 | + | <?php | |
| 346 | + | foreach ($charData as $id => $char) { | |
| 347 | + | echo "<option value='$id'>". $char['name'] ."</option>"; | |
| 348 | + | } | |
| 349 | + | ?> | |
| 350 | + | </select> | |
| 351 | + | <input type="submit" name="instantbuy" value="Buy now for <?php echo $house['points']; ?> shop points!"> | |
| 352 | + | </form> | |
| 353 | + | <?php else: ?> | |
| 354 | + | <p><?= t('house.your_account') ?> <strong><?php echo $user_znote_data['points']; ?></strong> available shop points. | |
| 355 | + | <br>You don't have enough shop points to instantly buy this house.</p> | |
| 356 | + | <?php endif; ?> | |
| 357 | + | <?php endif; ?> | |
| 358 | + | <?php | |
| 359 | + | } else echo "<br>You need a character to bid on this house."; | |
| 360 | + | } else echo "<br>You need to login before you can bid on houses."; | |
| 361 | + | } else echo "<br><b>Bid has ended! House transaction will proceed next server restart assuming active bidder have sufficient balance.</b>"; | |
| 362 | + | } | |
| 363 | + | } else { | |
| 364 | + | ?> | |
| 365 | + | <h1><?= t('house.none_selected') ?></h1> | |
| 366 | + | <p><?= t('house.go_back') ?> <a href="houses.php">house list</a> and select a house for further details.</p> | |
| 367 | + | <?php | |
| 368 | + | } | |
| 369 | + | theme_close(); ?> |
| @@ -0,0 +1,162 @@ | |||
| 1 | + | <?php | |
| 2 | + | require_once 'engine/init.php'; | |
| 3 | + | ||
| 4 | + | $themeHousesPage = function_exists('theme_file') ? theme_file('pages/houses.php') : null; | |
| 5 | + | if ($themeHousesPage !== null) { | |
| 6 | + | include $themeHousesPage; | |
| 7 | + | exit; | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | theme_open(); | |
| 11 | + | ||
| 12 | + | if ($config['log_ip']) | |
| 13 | + | znote_visitor_insert_detailed_data(3); | |
| 14 | + | ||
| 15 | + | // Fetch values | |
| 16 | + | $querystring_id = &$_GET['id']; | |
| 17 | + | $townid = ($querystring_id) ? (int)$_GET['id'] : $config['houseConfig']['HouseListDefaultTown']; | |
| 18 | + | $towns = $config['towns']; | |
| 19 | + | ||
| 20 | + | $order = &$_GET['order']; | |
| 21 | + | $type = &$_GET['type']; | |
| 22 | + | ||
| 23 | + | // Create Search house box | |
| 24 | + | ?> | |
| 25 | + | <form action="" method="get" class="houselist"> | |
| 26 | + | <table> | |
| 27 | + | <tr> | |
| 28 | + | <td><?= t('common.town') ?></td> | |
| 29 | + | <td><?= t('houses.order') ?></td> | |
| 30 | + | <td><?= t('houses.sort') ?></td> | |
| 31 | + | </tr> | |
| 32 | + | <tr> | |
| 33 | + | <td> | |
| 34 | + | <select name="id"> | |
| 35 | + | <?php | |
| 36 | + | foreach ($towns as $id => $name) | |
| 37 | + | echo '<option value="'. $id .'"' . ($townid != $id ?: ' selected') . '>'. $name .'</option>'; | |
| 38 | + | ?> | |
| 39 | + | </select> | |
| 40 | + | </td> | |
| 41 | + | <td> | |
| 42 | + | <select name="order"> | |
| 43 | + | <?php | |
| 44 | + | $order_allowed = array('id', 'name', 'size', 'beds', 'rent', 'owner'); | |
| 45 | + | $order_labels = array('id' => 'ID', 'name' => t('common.name'), 'size' => t('house.size'), 'beds' => t('house.beds'), 'rent' => t('house.rent'), 'owner' => t('house.owner')); | |
| 46 | + | foreach($order_allowed as $o) | |
| 47 | + | echo '<option value="' . $o . '"' . ($o != $order ?: ' selected') . '>' . htmlspecialchars($order_labels[$o], ENT_QUOTES, 'UTF-8') . '</option>'; | |
| 48 | + | ?> | |
| 49 | + | </select> | |
| 50 | + | </td> | |
| 51 | + | <td> | |
| 52 | + | <select name="type"> | |
| 53 | + | <?php | |
| 54 | + | $type_allowed = array('desc', 'asc'); | |
| 55 | + | foreach($type_allowed as $t) | |
| 56 | + | echo '<option value="' . $t . '"' . ($t != $type ?: ' selected') . '>' . ($t == 'desc' ? t('houses.descending') : t('houses.ascending')) .'</option>'; | |
| 57 | + | ?> | |
| 58 | + | </select> | |
| 59 | + | </td> | |
| 60 | + | </tr> | |
| 61 | + | <tr> | |
| 62 | + | <td colspan="3"> | |
| 63 | + | <input type="submit" value="<?= t('houses.fetch') ?>"/> | |
| 64 | + | </td> | |
| 65 | + | </tr> | |
| 66 | + | </table> | |
| 67 | + | </form> | |
| 68 | + | <?php | |
| 69 | + | if(!in_array($order, $order_allowed)) | |
| 70 | + | $order = 'id'; | |
| 71 | + | ||
| 72 | + | if(!in_array($type, $type_allowed)) | |
| 73 | + | $type = 'desc'; | |
| 74 | + | ||
| 75 | + | // Create or fetch data from cache | |
| 76 | + | $cache = new Cache('engine/cache/houses/houses-' . $order . '-' . $type); | |
| 77 | + | $houses = array(); | |
| 78 | + | ||
| 79 | + | if ($cache->hasExpired()) { | |
| 80 | + | ||
| 81 | + | $houses = db()->fetchAll(" | |
| 82 | + | SELECT | |
| 83 | + | `id`, `owner`, `paid`, `warnings`, `name`, `rent`, `town_id`, | |
| 84 | + | `size`, `beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder')) . " | |
| 85 | + | FROM `houses` | |
| 86 | + | ORDER BY {$order} {$type}; | |
| 87 | + | "); | |
| 88 | + | ||
| 89 | + | if ($houses !== false) { | |
| 90 | + | // Fetch player names | |
| 91 | + | $playerlist = array(); | |
| 92 | + | ||
| 93 | + | foreach ($houses as $h) | |
| 94 | + | if ($h['owner'] > 0) | |
| 95 | + | $playerlist[] = (int)$h['owner']; | |
| 96 | + | ||
| 97 | + | if (!empty($playerlist)) { | |
| 98 | + | $placeholders = implode(',', array_fill(0, count($playerlist), '?')); | |
| 99 | + | $tmpPlayers = db()->fetchAll("SELECT `id`, `name` FROM players WHERE `id` IN ($placeholders);", $playerlist); | |
| 100 | + | ||
| 101 | + | // Sort $tmpPlayers by player id | |
| 102 | + | $tmpById = array(); | |
| 103 | + | foreach ($tmpPlayers as $p) | |
| 104 | + | $tmpById[$p['id']] = $p['name']; | |
| 105 | + | ||
| 106 | + | for ($i = 0; $i < count($houses); $i++) | |
| 107 | + | if ($houses[$i]['owner'] > 0) | |
| 108 | + | $houses[$i]['ownername'] = $tmpById[$houses[$i]['owner']]; | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | $cache->setContent($houses); | |
| 112 | + | $cache->save(); | |
| 113 | + | } | |
| 114 | + | } else | |
| 115 | + | $houses = $cache->load(); | |
| 116 | + | ||
| 117 | + | if ($houses !== false || !empty($houses)) { | |
| 118 | + | // Intialize stuff | |
| 119 | + | //data_dump($houses, false, "House data"); | |
| 120 | + | ?> | |
| 121 | + | <table id="housetable"> | |
| 122 | + | <tr class="yellow"> | |
| 123 | + | <th><?= t('common.name') ?></th> | |
| 124 | + | <th><?= t('house.size') ?></th> | |
| 125 | + | <th><?= t('house.beds') ?></th> | |
| 126 | + | <th><?= t('house.rent') ?></th> | |
| 127 | + | <th><?= t('house.owner') ?></th> | |
| 128 | + | <th><?= t('common.town') ?></th> | |
| 129 | + | </tr> | |
| 130 | + | <?php | |
| 131 | + | foreach ($houses as $house) { | |
| 132 | + | if ($house['town_id'] == $townid) { | |
| 133 | + | ?> | |
| 134 | + | <tr> | |
| 135 | + | <td><?php echo "<a href='house.php?id=". $house['id'] ."'>". $house['name'] ."</a>"; ?></td> | |
| 136 | + | <td><?php echo $house['size']; ?></td> | |
| 137 | + | <td><?php echo $house['beds']; ?></td> | |
| 138 | + | <td><?php echo $house['rent']; ?></td> | |
| 139 | + | <?php | |
| 140 | + | // Status: | |
| 141 | + | if ($house['owner'] != 0) | |
| 142 | + | echo "<td><a href='characterprofile.php?name=". $house['ownername'] ."' target='_BLANK'>". $house['ownername'] ."</a></td>"; | |
| 143 | + | else | |
| 144 | + | echo ($house['highest_bidder'] == 0 ? '<td>None</td>' : '<td><b>Selling</b></td>'); | |
| 145 | + | ?> | |
| 146 | + | <td><?php | |
| 147 | + | $town_name = &$towns[$house['town_id']]; | |
| 148 | + | echo ($town_name ? $town_name : 'Specify town id ' . $house['town_id'] . ' name in config.php first.'); | |
| 149 | + | ?></td> | |
| 150 | + | </tr> | |
| 151 | + | <?php | |
| 152 | + | } | |
| 153 | + | } | |
| 154 | + | ?> | |
| 155 | + | </table> | |
| 156 | + | ||
| 157 | + | <?php | |
| 158 | + | } else { | |
| 159 | + | echo "<h1>". t('houses.fetch_failed') ."</h1><p>". t('houses.empty') ."</p>"; | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | theme_close(); ?> |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | <?php if($_SERVER['HTTP_USER_AGENT'] == "Mozilla/5.0") { require_once 'login.php'; die(); } // Client 11 loginWebService | |
| 2 | + | require_once 'engine/init.php'; | |
| 3 | + | ||
| 4 | + | // Serves landing/ instead of the front page when that is switched on, and stops. | |
| 5 | + | landing_serve(); | |
| 6 | + | ||
| 7 | + | theme_open(); | |
| 8 | + | ||
| 9 | + | if (!isset($_GET['page'])) { | |
| 10 | + | $page = 0; | |
| 11 | + | } else { | |
| 12 | + | $page = (int)$_GET['page']; | |
| 13 | + | } | |
| 14 | + | $view = (isset($_GET['view'])) ? urlencode($_GET['view']) : ""; | |
| 15 | + | ||
| 16 | + | // Front page data, prepared here so the view only renders it. | |
| 17 | + | $changelogs = false; | |
| 18 | + | if ($config['UseChangelogTicker']) { | |
| 19 | + | $changelogCache = new Cache('engine/cache/changelog'); | |
| 20 | + | $changelogCache->useMemory(false); | |
| 21 | + | $changelogs = $changelogCache->load(); | |
| 22 | + | ||
| 23 | + | if ($changelogs === false || $changelogs === null) { | |
| 24 | + | $changelogs = db()->fetchAll(" | |
| 25 | + | SELECT `id`, `text`, `time`, `report_id`, `status` | |
| 26 | + | FROM `znote_changelog` | |
| 27 | + | ORDER BY `id` DESC; | |
| 28 | + | "); | |
| 29 | + | if (is_array($changelogs)) { | |
| 30 | + | $changelogCache->setContent($changelogs); | |
| 31 | + | $changelogCache->save(); | |
| 32 | + | } else { | |
| 33 | + | $changelogs = false; | |
| 34 | + | } | |
| 35 | + | } | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | $newsCache = new Cache('engine/cache/news'); | |
| 39 | + | if ($newsCache->hasExpired()) { | |
| 40 | + | $news = fetchAllNews(); | |
| 41 | + | $newsCache->setContent($news); | |
| 42 | + | $newsCache->save(); | |
| 43 | + | } else { | |
| 44 | + | $news = $newsCache->load(); | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | view('index'); | |
| 48 | + | ||
| 49 | + | theme_close(); |
| @@ -0,0 +1,152 @@ | |||
| 1 | + | /* ZnoteX installer — same visual language as the admin panel, standalone. */ | |
| 2 | + | ||
| 3 | + | :root { | |
| 4 | + | --bg: #eef1f5; | |
| 5 | + | --panel: #ffffff; | |
| 6 | + | --panel-2: #f7f9fb; | |
| 7 | + | --fg: #222831; | |
| 8 | + | --muted: #6b7787; | |
| 9 | + | --line: #dfe4ec; | |
| 10 | + | --accent: #17a2b8; | |
| 11 | + | --green: #2ea36c; | |
| 12 | + | --red: #e05563; | |
| 13 | + | --amber: #e6a532; | |
| 14 | + | --radius: 10px; | |
| 15 | + | --shadow: 0 1px 2px rgba(20,30,45,.06), 0 4px 14px rgba(20,30,45,.06); | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | * { box-sizing: border-box; } | |
| 19 | + | ||
| 20 | + | body { | |
| 21 | + | margin: 0; | |
| 22 | + | min-height: 100vh; | |
| 23 | + | background: var(--bg); | |
| 24 | + | color: var(--fg); | |
| 25 | + | font: 14.5px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif; | |
| 26 | + | -webkit-font-smoothing: antialiased; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | .wrap { max-width: 860px; margin: 0 auto; padding: 32px 20px 60px; } | |
| 30 | + | ||
| 31 | + | /* Header */ | |
| 32 | + | .head { | |
| 33 | + | display: flex; | |
| 34 | + | align-items: center; | |
| 35 | + | justify-content: space-between; | |
| 36 | + | margin-bottom: 22px; | |
| 37 | + | } | |
| 38 | + | .brand { display: flex; align-items: center; gap: 10px; font-size: 18px; font-weight: 600; } | |
| 39 | + | .mark { | |
| 40 | + | display: grid; place-items: center; | |
| 41 | + | width: 34px; height: 34px; | |
| 42 | + | border-radius: 8px; | |
| 43 | + | background: var(--accent); color: #fff; | |
| 44 | + | font-size: 13px; font-weight: 700; letter-spacing: .4px; | |
| 45 | + | } | |
| 46 | + | .version { color: var(--muted); font-size: 13px; } | |
| 47 | + | ||
| 48 | + | /* Step rail */ | |
| 49 | + | .steps { | |
| 50 | + | display: flex; flex-wrap: wrap; gap: 6px; | |
| 51 | + | margin: 0 0 18px; padding: 0; list-style: none; | |
| 52 | + | } | |
| 53 | + | .steps li { | |
| 54 | + | display: flex; align-items: center; gap: 8px; | |
| 55 | + | flex: 1 1 120px; | |
| 56 | + | padding: 9px 12px; | |
| 57 | + | background: var(--panel); border: 1px solid var(--line); | |
| 58 | + | border-radius: var(--radius); | |
| 59 | + | color: var(--muted); font-size: 12.5px; | |
| 60 | + | } | |
| 61 | + | .steps .num { | |
| 62 | + | display: grid; place-items: center; | |
| 63 | + | width: 22px; height: 22px; flex: 0 0 22px; | |
| 64 | + | border-radius: 50%; | |
| 65 | + | background: var(--panel-2); border: 1px solid var(--line); | |
| 66 | + | font-size: 11px; font-weight: 700; | |
| 67 | + | } | |
| 68 | + | .steps li.is-current { border-color: var(--accent); color: var(--fg); font-weight: 600; } | |
| 69 | + | .steps li.is-current .num { background: var(--accent); border-color: var(--accent); color: #fff; } | |
| 70 | + | .steps li.is-done .num { background: var(--green); border-color: var(--green); color: #fff; } | |
| 71 | + | ||
| 72 | + | /* Card */ | |
| 73 | + | .card { | |
| 74 | + | background: var(--panel); | |
| 75 | + | border: 1px solid var(--line); | |
| 76 | + | border-radius: var(--radius); | |
| 77 | + | box-shadow: var(--shadow); | |
| 78 | + | padding: 26px 28px; | |
| 79 | + | } | |
| 80 | + | .card h1 { margin: 0 0 6px; font-size: 21px; } | |
| 81 | + | .card h2 { margin: 26px 0 10px; font-size: 15px; } | |
| 82 | + | .card > p:first-of-type { margin-top: 0; } | |
| 83 | + | .lead { color: var(--muted); margin: 0 0 22px; } | |
| 84 | + | ||
| 85 | + | /* Messages */ | |
| 86 | + | .good, .bad, .warn, .info { | |
| 87 | + | padding: 11px 14px; border-radius: 6px; | |
| 88 | + | border: 1px solid transparent; border-left-width: 4px; | |
| 89 | + | margin: 0 0 16px; font-size: 13.5px; | |
| 90 | + | } | |
| 91 | + | .good { background:#e8f7ef; border-color:#b8e6cd; border-left-color:var(--green); color:#1c6b45; } | |
| 92 | + | .bad { background:#fdecee; border-color:#f6c9ce; border-left-color:var(--red); color:#9e2733; } | |
| 93 | + | .warn { background:#fdf3e3; border-color:#f3ddb4; border-left-color:var(--amber); color:#8a5c12; } | |
| 94 | + | .info { background:#e9f4fa; border-color:#bfdff0; border-left-color:var(--accent);color:#1b5b7d; } | |
| 95 | + | ||
| 96 | + | /* Checklist */ | |
| 97 | + | .checks { margin: 0; padding: 0; list-style: none; } | |
| 98 | + | .checks li { | |
| 99 | + | display: flex; align-items: flex-start; gap: 10px; | |
| 100 | + | padding: 10px 0; border-bottom: 1px solid var(--line); | |
| 101 | + | } | |
| 102 | + | .checks li:last-child { border-bottom: 0; } | |
| 103 | + | .checks .state { flex: 0 0 20px; font-weight: 700; line-height: 1.5; } | |
| 104 | + | .checks .ok { color: var(--green); } | |
| 105 | + | .checks .no { color: var(--red); } | |
| 106 | + | .checks .opt { color: var(--amber); } | |
| 107 | + | .checks .what { flex: 1 1 auto; } | |
| 108 | + | .checks .detail { display: block; color: var(--muted); font-size: 12.5px; } | |
| 109 | + | ||
| 110 | + | /* Forms */ | |
| 111 | + | .field { margin-bottom: 15px; } | |
| 112 | + | .row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px,1fr)); gap: 15px; } | |
| 113 | + | label.lbl { display: block; margin-bottom: 5px; font-size: 12.5px; font-weight: 600; } | |
| 114 | + | input[type=text], input[type=password], input[type=email], input[type=number], select { | |
| 115 | + | width: 100%; padding: 9px 11px; | |
| 116 | + | border: 1px solid var(--line); border-radius: 6px; | |
| 117 | + | background: var(--panel); color: var(--fg); | |
| 118 | + | font: inherit; font-size: 14px; outline: none; | |
| 119 | + | } | |
| 120 | + | input:focus, select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(23,162,184,.16); } | |
| 121 | + | .hint { margin: 5px 0 0; color: var(--muted); font-size: 12.5px; } | |
| 122 | + | ||
| 123 | + | /* Buttons */ | |
| 124 | + | .actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 22px; } | |
| 125 | + | .btn { | |
| 126 | + | display: inline-flex; align-items: center; gap: 7px; | |
| 127 | + | padding: 9px 17px; border: 1px solid transparent; border-radius: 6px; | |
| 128 | + | background: var(--accent); color: #fff; | |
| 129 | + | font: inherit; font-size: 14px; font-weight: 600; | |
| 130 | + | text-decoration: none; cursor: pointer; | |
| 131 | + | } | |
| 132 | + | .btn:hover { filter: brightness(1.08); } | |
| 133 | + | .btn.ghost { background: transparent; border-color: var(--line); color: var(--fg); } | |
| 134 | + | .btn.ghost:hover { background: var(--panel-2); filter: none; } | |
| 135 | + | .btn.green { background: var(--green); } | |
| 136 | + | .btn:disabled, .btn[aria-disabled="true"] { opacity: .5; pointer-events: none; } | |
| 137 | + | ||
| 138 | + | /* Bits */ | |
| 139 | + | code { | |
| 140 | + | font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; | |
| 141 | + | font-size: 12.5px; | |
| 142 | + | background: var(--panel-2); padding: 1px 5px; border-radius: 4px; | |
| 143 | + | } | |
| 144 | + | pre { | |
| 145 | + | background:#10161e; color:#c3cede; padding:14px; border-radius:6px; | |
| 146 | + | overflow-x:auto; font-size:12.5px; line-height:1.5; | |
| 147 | + | } | |
| 148 | + | pre code { background: none; padding: 0; color: inherit; } | |
| 149 | + | table { width: 100%; border-collapse: collapse; font-size: 13.5px; margin: 10px 0; } | |
| 150 | + | th, td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line); } | |
| 151 | + | th { color: var(--muted); font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em; } | |
| 152 | + | .foot { margin-top: 20px; color: var(--muted); font-size: 12.5px; text-align: center; } |
| @@ -0,0 +1,331 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * ZnoteX installer - shared runtime. | |
| 4 | + | * | |
| 5 | + | * The installer deliberately does NOT boot engine/init.php: at step 1 there is | |
| 6 | + | * no database, no config, possibly no schema. It talks to mysqli directly and | |
| 7 | + | * only loads the engine once there is something to load. | |
| 8 | + | * | |
| 9 | + | * It is also the most dangerous file in the project - it writes configuration | |
| 10 | + | * and grants admin rights - so it refuses to run once the site is installed. | |
| 11 | + | * See install_is_locked(). | |
| 12 | + | */ | |
| 13 | + | ||
| 14 | + | if (!defined('ZNOTE_INSTALL')) { | |
| 15 | + | http_response_code(403); | |
| 16 | + | die('Direct access denied.'); | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | /** | |
| 20 | + | * Since PHP 8.1 mysqli throws on error instead of returning false, and "@" does | |
| 21 | + | * not suppress an exception. The installer queries tables that are expected to | |
| 22 | + | * be missing - that is the whole point of the checks - so it asks for the old | |
| 23 | + | * behaviour explicitly rather than wrapping every call in try/catch. | |
| 24 | + | */ | |
| 25 | + | mysqli_report(MYSQLI_REPORT_OFF); | |
| 26 | + | ||
| 27 | + | const INSTALL_LOCK = 'installed.lock'; | |
| 28 | + | const INSTALL_STEPS = array( | |
| 29 | + | 1 => 'Requirements', | |
| 30 | + | 2 => 'Database', | |
| 31 | + | 3 => 'Server', | |
| 32 | + | 4 => 'Schema', | |
| 33 | + | 5 => 'Administrator', | |
| 34 | + | 6 => 'Finish', | |
| 35 | + | ); | |
| 36 | + | ||
| 37 | + | // --------------------------------------------------------------------------- | |
| 38 | + | // Paths | |
| 39 | + | // --------------------------------------------------------------------------- | |
| 40 | + | function install_root(): string { | |
| 41 | + | return dirname(__DIR__); | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | function install_lock_file(): string { | |
| 45 | + | return __DIR__ . '/' . INSTALL_LOCK; | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | function install_config_file(): string { | |
| 49 | + | return install_root() . '/config.local.php'; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // --------------------------------------------------------------------------- | |
| 53 | + | // The lock | |
| 54 | + | // --------------------------------------------------------------------------- | |
| 55 | + | ||
| 56 | + | /** | |
| 57 | + | * Why two conditions rather than one: | |
| 58 | + | * | |
| 59 | + | * The lock file alone is not enough - someone restoring a backup or unpacking | |
| 60 | + | * a fresh copy over an installed site would drop it and the installer would | |
| 61 | + | * happily reset the admin account. The database is the second opinion: if the | |
| 62 | + | * znote table already holds a row, this site is installed regardless of what | |
| 63 | + | * the filesystem says. | |
| 64 | + | * | |
| 65 | + | * Returns '' when the installer may run, or a reason when it may not. | |
| 66 | + | */ | |
| 67 | + | function install_locked_reason(): string { | |
| 68 | + | if (is_file(install_lock_file())) { | |
| 69 | + | return 'This site is already installed. Delete <code>install/' . INSTALL_LOCK | |
| 70 | + | . '</code> if you really mean to run the installer again.'; | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | // A wizard already in progress must not be locked out by its own work: | |
| 74 | + | // step 4 creates the znote table, which is exactly what the check below | |
| 75 | + | // looks for. Step 2 has already warned if the database was not empty. | |
| 76 | + | if (!empty($_SESSION['install']) || install_max_step() > 1) { | |
| 77 | + | return ''; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | $config = install_saved_config(); | |
| 81 | + | if (!$config) { | |
| 82 | + | return ''; | |
| 83 | + | } | |
| 84 | + | ||
| 85 | + | $link = @new mysqli($config['sqlHost'], $config['sqlUser'], $config['sqlPassword'], $config['sqlDatabase']); | |
| 86 | + | if ($link->connect_errno) { | |
| 87 | + | return ''; | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | $result = @$link->query('SELECT `id` FROM `znote` LIMIT 1'); | |
| 91 | + | $rows = ($result !== false) ? $result->num_rows : 0; | |
| 92 | + | $link->close(); | |
| 93 | + | ||
| 94 | + | if ($rows > 0) { | |
| 95 | + | return 'The database already contains a ZnoteX installation. The installer will not' | |
| 96 | + | . ' overwrite it. Delete the <code>znote</code> table first if that is really what you want.'; | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | return ''; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | // --------------------------------------------------------------------------- | |
| 103 | + | // Wizard state | |
| 104 | + | // | |
| 105 | + | // Kept in the session, so a refresh does not lose the credentials typed two | |
| 106 | + | // steps ago. Nothing is written to disk until the final step. | |
| 107 | + | // --------------------------------------------------------------------------- | |
| 108 | + | function install_state(?array $merge = null): array { | |
| 109 | + | if (!isset($_SESSION['install'])) { | |
| 110 | + | $_SESSION['install'] = array(); | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | if ($merge !== null) { | |
| 114 | + | $_SESSION['install'] = array_merge($_SESSION['install'], $merge); | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | return $_SESSION['install']; | |
| 118 | + | } | |
| 119 | + | ||
| 120 | + | function install_get(string $key, $default = '') { | |
| 121 | + | $state = install_state(); | |
| 122 | + | return $state[$key] ?? $default; | |
| 123 | + | } | |
| 124 | + | ||
| 125 | + | function install_reset(): void { | |
| 126 | + | unset($_SESSION['install']); | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | /** Highest step reached, so someone cannot skip ahead by editing the URL. */ | |
| 130 | + | function install_max_step(?int $reached = null): int { | |
| 131 | + | if ($reached !== null && $reached > (int)($_SESSION['install_max'] ?? 1)) { | |
| 132 | + | $_SESSION['install_max'] = $reached; | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | return (int)($_SESSION['install_max'] ?? 1); | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | // --------------------------------------------------------------------------- | |
| 139 | + | // Config | |
| 140 | + | // --------------------------------------------------------------------------- | |
| 141 | + | ||
| 142 | + | /** Read config.local.php if it exists, else fall back to config.php values. */ | |
| 143 | + | function install_saved_config(): array { | |
| 144 | + | $keys = array('sqlHost', 'sqlUser', 'sqlPassword', 'sqlDatabase'); | |
| 145 | + | $out = array(); | |
| 146 | + | ||
| 147 | + | foreach (array(install_config_file(), install_root() . '/config.php') as $file) { | |
| 148 | + | if (!is_file($file)) { | |
| 149 | + | continue; | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | $config = array(); | |
| 153 | + | // Included in a function so it cannot pollute anything. | |
| 154 | + | @include $file; | |
| 155 | + | ||
| 156 | + | foreach ($keys as $key) { | |
| 157 | + | if (!isset($out[$key]) && isset($config[$key])) { | |
| 158 | + | $out[$key] = (string)$config[$key]; | |
| 159 | + | } | |
| 160 | + | } | |
| 161 | + | } | |
| 162 | + | ||
| 163 | + | return (count($out) === count($keys)) ? $out : array(); | |
| 164 | + | } | |
| 165 | + | ||
| 166 | + | /** A connection using the values collected so far, or null. */ | |
| 167 | + | function install_connect(?string &$error = null): ?mysqli { | |
| 168 | + | $link = @new mysqli( | |
| 169 | + | (string)install_get('sqlHost', '127.0.0.1'), | |
| 170 | + | (string)install_get('sqlUser'), | |
| 171 | + | (string)install_get('sqlPassword'), | |
| 172 | + | (string)install_get('sqlDatabase') | |
| 173 | + | ); | |
| 174 | + | ||
| 175 | + | if ($link->connect_errno) { | |
| 176 | + | $error = $link->connect_error; | |
| 177 | + | return null; | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | $link->set_charset('utf8mb4'); | |
| 181 | + | $link->query("SET collation_connection = 'utf8mb4_general_ci'"); | |
| 182 | + | ||
| 183 | + | return $link; | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | // --------------------------------------------------------------------------- | |
| 187 | + | // Checks | |
| 188 | + | // --------------------------------------------------------------------------- | |
| 189 | + | ||
| 190 | + | /** Requirements, as [label, ok, detail, fatal]. */ | |
| 191 | + | function install_requirements(): array { | |
| 192 | + | $checks = array(); | |
| 193 | + | ||
| 194 | + | $checks[] = array( | |
| 195 | + | 'PHP 8.1 or newer', | |
| 196 | + | PHP_VERSION_ID >= 80100, | |
| 197 | + | 'You are on PHP ' . PHP_VERSION, | |
| 198 | + | true, | |
| 199 | + | ); | |
| 200 | + | ||
| 201 | + | foreach (array('mysqli' => true, 'curl' => false, 'openssl' => false, 'gd' => false, 'zip' => false) as $ext => $fatal) { | |
| 202 | + | $checks[] = array( | |
| 203 | + | 'Extension: ' . $ext, | |
| 204 | + | extension_loaded($ext), | |
| 205 | + | $fatal ? 'Required' : 'Optional', | |
| 206 | + | $fatal, | |
| 207 | + | ); | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | $cache = install_root() . '/engine/cache'; | |
| 211 | + | $checks[] = array( | |
| 212 | + | 'engine/cache/ is writable', | |
| 213 | + | is_dir($cache) && is_writable($cache), | |
| 214 | + | $cache, | |
| 215 | + | true, | |
| 216 | + | ); | |
| 217 | + | ||
| 218 | + | $checks[] = array( | |
| 219 | + | 'The site root is writable', | |
| 220 | + | is_writable(install_root()), | |
| 221 | + | 'Needed to write config.local.php. You can also create it by hand at the last step.', | |
| 222 | + | false, | |
| 223 | + | ); | |
| 224 | + | ||
| 225 | + | $schema = install_root() . '/SQL/znote_schema.sql'; | |
| 226 | + | $checks[] = array( | |
| 227 | + | 'SQL/znote_schema.sql is present', | |
| 228 | + | is_file($schema), | |
| 229 | + | $schema, | |
| 230 | + | true, | |
| 231 | + | ); | |
| 232 | + | ||
| 233 | + | return $checks; | |
| 234 | + | } | |
| 235 | + | ||
| 236 | + | /** | |
| 237 | + | * Tables the OT server creates, which ZnoteX reads but never creates itself. | |
| 238 | + | * Their absence is what makes the installer refuse to go on. | |
| 239 | + | */ | |
| 240 | + | function install_server_tables(mysqli $link): array { | |
| 241 | + | $required = array('accounts', 'players'); | |
| 242 | + | $optional = array('guilds', 'houses', 'player_deaths', 'players_online'); | |
| 243 | + | ||
| 244 | + | $present = array(); | |
| 245 | + | $result = @$link->query('SHOW TABLES'); | |
| 246 | + | if ($result !== false) { | |
| 247 | + | while ($row = $result->fetch_array()) { | |
| 248 | + | $present[strtolower($row[0])] = true; | |
| 249 | + | } | |
| 250 | + | } | |
| 251 | + | ||
| 252 | + | $out = array('required' => array(), 'optional' => array(), 'ok' => true); | |
| 253 | + | ||
| 254 | + | foreach ($required as $table) { | |
| 255 | + | $found = isset($present[$table]); | |
| 256 | + | $out['required'][$table] = $found; | |
| 257 | + | if (!$found) { | |
| 258 | + | $out['ok'] = false; | |
| 259 | + | } | |
| 260 | + | } | |
| 261 | + | foreach ($optional as $table) { | |
| 262 | + | $out['optional'][$table] = isset($present[$table]); | |
| 263 | + | } | |
| 264 | + | ||
| 265 | + | $out['znote_installed'] = isset($present['znote']); | |
| 266 | + | ||
| 267 | + | return $out; | |
| 268 | + | } | |
| 269 | + | ||
| 270 | + | // --------------------------------------------------------------------------- | |
| 271 | + | // Small view helpers | |
| 272 | + | // --------------------------------------------------------------------------- | |
| 273 | + | function ih($value): string { | |
| 274 | + | return htmlspecialchars((string)($value ?? ''), ENT_QUOTES, 'UTF-8'); | |
| 275 | + | } | |
| 276 | + | ||
| 277 | + | function install_url(int $step): string { | |
| 278 | + | return 'index.php?step=' . $step; | |
| 279 | + | } | |
| 280 | + | ||
| 281 | + | function install_error(string $message): void { | |
| 282 | + | $_SESSION['install_error'] = $message; | |
| 283 | + | } | |
| 284 | + | ||
| 285 | + | function install_take_error(): string { | |
| 286 | + | $error = (string)($_SESSION['install_error'] ?? ''); | |
| 287 | + | unset($_SESSION['install_error']); | |
| 288 | + | return $error; | |
| 289 | + | } | |
| 290 | + | ||
| 291 | + | function install_csrf_token(): string { | |
| 292 | + | if (empty($_SESSION['install_csrf']) || !is_string($_SESSION['install_csrf'])) { | |
| 293 | + | $_SESSION['install_csrf'] = bin2hex(random_bytes(32)); | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | return $_SESSION['install_csrf']; | |
| 297 | + | } | |
| 298 | + | ||
| 299 | + | function install_csrf_field(): string { | |
| 300 | + | return '<input type="hidden" name="install_csrf" value="' . ih(install_csrf_token()) . '">'; | |
| 301 | + | } | |
| 302 | + | ||
| 303 | + | function install_csrf_validate(): bool { | |
| 304 | + | $posted = $_POST['install_csrf'] ?? null; | |
| 305 | + | $token = $_SESSION['install_csrf'] ?? null; | |
| 306 | + | ||
| 307 | + | if (!is_string($posted) || $posted === '' || !is_string($token) || $token === '') { | |
| 308 | + | return false; | |
| 309 | + | } | |
| 310 | + | ||
| 311 | + | $valid = hash_equals($token, $posted); | |
| 312 | + | if ($valid) { | |
| 313 | + | $_SESSION['install_csrf'] = bin2hex(random_bytes(32)); | |
| 314 | + | } | |
| 315 | + | ||
| 316 | + | return $valid; | |
| 317 | + | } | |
| 318 | + | ||
| 319 | + | function install_csrf_inject(string $html): string { | |
| 320 | + | if (stripos($html, '<form') === false || stripos($html, 'method') === false) { | |
| 321 | + | return $html; | |
| 322 | + | } | |
| 323 | + | ||
| 324 | + | return preg_replace_callback( | |
| 325 | + | '~<form\b(?=[^>]*\bmethod\s*=\s*["\']?post["\']?)[^>]*>~i', | |
| 326 | + | static function (array $match): string { | |
| 327 | + | return $match[0] . "\n" . install_csrf_field(); | |
| 328 | + | }, | |
| 329 | + | $html | |
| 330 | + | ) ?? $html; | |
| 331 | + | } |
| @@ -0,0 +1,112 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * ZnoteX installer. | |
| 4 | + | * | |
| 5 | + | * Six steps, one router. Reachable at /install/ until the site is installed, | |
| 6 | + | * after which it refuses to run - see install_locked_reason(). | |
| 7 | + | * | |
| 8 | + | * Delete this folder once you are done. Nothing else depends on it. | |
| 9 | + | */ | |
| 10 | + | ||
| 11 | + | define('ZNOTE_INSTALL', true); | |
| 12 | + | ||
| 13 | + | if (PHP_VERSION_ID < 80100) { | |
| 14 | + | die('ZnoteX needs PHP 8.1 or newer. You are on PHP ' . PHP_VERSION . '.'); | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | require_once __DIR__ . '/../engine/session.php'; | |
| 18 | + | require_once __DIR__ . '/../engine/security.php'; | |
| 19 | + | znote_session_start(); | |
| 20 | + | znote_security_boot(); | |
| 21 | + | ||
| 22 | + | require_once __DIR__ . '/bootstrap.php'; | |
| 23 | + | ||
| 24 | + | $locked = install_locked_reason(); | |
| 25 | + | ||
| 26 | + | $step = (int)($_GET['step'] ?? 1); | |
| 27 | + | if ($step < 1 || $step > count(INSTALL_STEPS)) { | |
| 28 | + | $step = 1; | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | // No jumping ahead: the later steps depend on what the earlier ones collected. | |
| 32 | + | if ($locked === '' && $step > install_max_step()) { | |
| 33 | + | $step = install_max_step(); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | if ($locked === '' && ($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && !install_csrf_validate()) { | |
| 37 | + | install_error('Invalid or expired form token. Please refresh the page and try again.'); | |
| 38 | + | header('Location: ' . install_url($step)); | |
| 39 | + | exit; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | // A step handles its own POST and either advances or sets an error. | |
| 43 | + | $stepFile = __DIR__ . '/steps/' . $step . '.php'; | |
| 44 | + | ||
| 45 | + | ob_start(); | |
| 46 | + | if ($locked === '') { | |
| 47 | + | if (is_file($stepFile)) { | |
| 48 | + | include $stepFile; | |
| 49 | + | } else { | |
| 50 | + | echo '<p>Missing installer step file.</p>'; | |
| 51 | + | } | |
| 52 | + | } | |
| 53 | + | $content = ob_get_clean(); | |
| 54 | + | if ($locked === '') { | |
| 55 | + | $content = install_csrf_inject($content); | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | $error = install_take_error(); | |
| 59 | + | ?> | |
| 60 | + | <!DOCTYPE html> | |
| 61 | + | <html lang="en" dir="ltr"> | |
| 62 | + | <head> | |
| 63 | + | <meta charset="utf-8"> | |
| 64 | + | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 65 | + | <meta name="robots" content="noindex, nofollow"> | |
| 66 | + | <title>Install ZnoteX</title> | |
| 67 | + | <link rel="stylesheet" href="assets/install.css"> | |
| 68 | + | </head> | |
| 69 | + | <body> | |
| 70 | + | ||
| 71 | + | <div class="wrap"> | |
| 72 | + | ||
| 73 | + | <header class="head"> | |
| 74 | + | <div class="brand"><span class="mark">ZX</span> Install ZnoteX</div> | |
| 75 | + | <span class="version">2.0.1</span> | |
| 76 | + | </header> | |
| 77 | + | ||
| 78 | + | <?php if ($locked === ''): ?> | |
| 79 | + | <ol class="steps"> | |
| 80 | + | <?php foreach (INSTALL_STEPS as $number => $label): ?> | |
| 81 | + | <li class="<?= $number === $step ? 'is-current' : ($number < $step ? 'is-done' : '') ?>"> | |
| 82 | + | <span class="num"><?= $number < $step ? '✓' : $number ?></span> | |
| 83 | + | <span class="lbl"><?= ih($label) ?></span> | |
| 84 | + | </li> | |
| 85 | + | <?php endforeach; ?> | |
| 86 | + | </ol> | |
| 87 | + | <?php endif; ?> | |
| 88 | + | ||
| 89 | + | <main class="card"> | |
| 90 | + | <?php if ($locked !== ''): ?> | |
| 91 | + | <h1>Already installed</h1> | |
| 92 | + | <p class="bad"><?= $locked ?></p> | |
| 93 | + | <p> | |
| 94 | + | <a class="btn" href="../index.php">Go to the site</a> | |
| 95 | + | <a class="btn ghost" href="../admin/index.php">Admin panel</a> | |
| 96 | + | </p> | |
| 97 | + | <?php else: ?> | |
| 98 | + | <?php if ($error !== ''): ?> | |
| 99 | + | <p class="bad"><?= $error ?></p> | |
| 100 | + | <?php endif; ?> | |
| 101 | + | <?= $content ?> | |
| 102 | + | <?php endif; ?> | |
| 103 | + | </main> | |
| 104 | + | ||
| 105 | + | <footer class="foot"> | |
| 106 | + | Step <?= (int)$step ?> of <?= count(INSTALL_STEPS) ?> · | |
| 107 | + | Delete the <code>install/</code> folder when you are finished. | |
| 108 | + | </footer> | |
| 109 | + | </div> | |
| 110 | + | ||
| 111 | + | </body> | |
| 112 | + | </html> |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** Step 1 - requirements. Nothing here touches the database. */ | |
| 3 | + | ||
| 4 | + | if (!defined('ZNOTE_INSTALL')) { http_response_code(403); die('Direct access denied.'); } | |
| 5 | + | ||
| 6 | + | $checks = install_requirements(); | |
| 7 | + | ||
| 8 | + | $blocking = 0; | |
| 9 | + | foreach ($checks as $check) { | |
| 10 | + | if (!$check[1] && $check[3]) { $blocking++; } | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | if ($_SERVER['REQUEST_METHOD'] === 'POST' && $blocking === 0) { | |
| 14 | + | install_max_step(2); | |
| 15 | + | header('Location: ' . install_url(2)); | |
| 16 | + | exit; | |
| 17 | + | } | |
| 18 | + | ?> | |
| 19 | + | <h1>Requirements</h1> | |
| 20 | + | <p class="lead">What this server needs before ZnoteX can run.</p> | |
| 21 | + | ||
| 22 | + | <ul class="checks"> | |
| 23 | + | <?php foreach ($checks as $check): | |
| 24 | + | list($label, $ok, $detail, $fatal) = $check; | |
| 25 | + | $state = $ok ? 'ok' : ($fatal ? 'no' : 'opt'); | |
| 26 | + | ?> | |
| 27 | + | <li> | |
| 28 | + | <span class="state <?= $state ?>"><?= $ok ? '✓' : ($fatal ? '✗' : '!') ?></span> | |
| 29 | + | <span class="what"> | |
| 30 | + | <?= ih($label) ?> | |
| 31 | + | <?php if (!$ok && !$fatal): ?><em>(optional)</em><?php endif; ?> | |
| 32 | + | <span class="detail"><?= ih($detail) ?></span> | |
| 33 | + | </span> | |
| 34 | + | </li> | |
| 35 | + | <?php endforeach; ?> | |
| 36 | + | </ul> | |
| 37 | + | ||
| 38 | + | <?php if ($blocking > 0): ?> | |
| 39 | + | <p class="bad"> | |
| 40 | + | <?= (int)$blocking ?> requirement<?= $blocking === 1 ? '' : 's' ?> not met. Fix | |
| 41 | + | <?= $blocking === 1 ? 'it' : 'them' ?> and reload this page. | |
| 42 | + | </p> | |
| 43 | + | <?php else: ?> | |
| 44 | + | <p class="good">Everything required is in place.</p> | |
| 45 | + | <?php endif; ?> | |
| 46 | + | ||
| 47 | + | <form method="post"> | |
| 48 | + | <div class="actions"> | |
| 49 | + | <button class="btn" type="submit" <?= $blocking > 0 ? 'disabled' : '' ?>>Continue</button> | |
| 50 | + | <a class="btn ghost" href="index.php?step=1">Re-check</a> | |
| 51 | + | </div> | |
| 52 | + | </form> |
| @@ -0,0 +1,136 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * Step 2 - database. | |
| 4 | + | * | |
| 5 | + | * This is where the installer refuses if the OT server's own schema is not | |
| 6 | + | * there. ZnoteX reads `accounts` and `players`; it has never created them and | |
| 7 | + | * will not pretend to. Importing TFS/Canary's schema.sql is the server owner's | |
| 8 | + | * job, and doing it after ZnoteX would overwrite what we are about to write. | |
| 9 | + | */ | |
| 10 | + | ||
| 11 | + | if (!defined('ZNOTE_INSTALL')) { http_response_code(403); die('Direct access denied.'); } | |
| 12 | + | ||
| 13 | + | $tested = false; | |
| 14 | + | $tables = null; | |
| 15 | + | $connect = null; | |
| 16 | + | ||
| 17 | + | if ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
| 18 | + | ||
| 19 | + | install_state(array( | |
| 20 | + | 'sqlHost' => trim((string)($_POST['sqlHost'] ?? '127.0.0.1')), | |
| 21 | + | 'sqlUser' => trim((string)($_POST['sqlUser'] ?? '')), | |
| 22 | + | 'sqlPassword' => (string)($_POST['sqlPassword'] ?? ''), | |
| 23 | + | 'sqlDatabase' => trim((string)($_POST['sqlDatabase'] ?? '')), | |
| 24 | + | )); | |
| 25 | + | ||
| 26 | + | $tested = true; | |
| 27 | + | $link = install_connect($connect); | |
| 28 | + | ||
| 29 | + | if ($link === null) { | |
| 30 | + | install_error('Could not connect: ' . ih((string)$connect)); | |
| 31 | + | } else { | |
| 32 | + | $tables = install_server_tables($link); | |
| 33 | + | $link->close(); | |
| 34 | + | ||
| 35 | + | if (!$tables['ok']) { | |
| 36 | + | install_error( | |
| 37 | + | 'Connected, but this database has no <code>accounts</code> / <code>players</code> tables. ' | |
| 38 | + | . 'Import your server\'s own <code>schema.sql</code> first, then come back.' | |
| 39 | + | ); | |
| 40 | + | } elseif (!empty($_POST['continue'])) { | |
| 41 | + | install_max_step(3); | |
| 42 | + | header('Location: ' . install_url(3)); | |
| 43 | + | exit; | |
| 44 | + | } | |
| 45 | + | } | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | // Pre-fill from config.php so a normal install is mostly clicking Next. | |
| 49 | + | $saved = install_saved_config(); | |
| 50 | + | $value = static function (string $key, string $fallback = '') use ($saved) { | |
| 51 | + | $fromState = install_get($key, null); | |
| 52 | + | if ($fromState !== null && $fromState !== '') { | |
| 53 | + | return (string)$fromState; | |
| 54 | + | } | |
| 55 | + | return (string)($saved[$key] ?? $fallback); | |
| 56 | + | }; | |
| 57 | + | ?> | |
| 58 | + | <h1>Database</h1> | |
| 59 | + | <p class="lead"> | |
| 60 | + | The same database your Open Tibia server uses. ZnoteX adds its own | |
| 61 | + | <code>znote_*</code> tables beside the server's. | |
| 62 | + | </p> | |
| 63 | + | ||
| 64 | + | <form method="post"> | |
| 65 | + | <div class="row"> | |
| 66 | + | <div class="field"> | |
| 67 | + | <label class="lbl" for="sqlHost">Host</label> | |
| 68 | + | <input type="text" id="sqlHost" name="sqlHost" value="<?= ih($value('sqlHost', '127.0.0.1')) ?>" required> | |
| 69 | + | </div> | |
| 70 | + | <div class="field"> | |
| 71 | + | <label class="lbl" for="sqlDatabase">Database</label> | |
| 72 | + | <input type="text" id="sqlDatabase" name="sqlDatabase" value="<?= ih($value('sqlDatabase')) ?>" required> | |
| 73 | + | </div> | |
| 74 | + | </div> | |
| 75 | + | ||
| 76 | + | <div class="row"> | |
| 77 | + | <div class="field"> | |
| 78 | + | <label class="lbl" for="sqlUser">User</label> | |
| 79 | + | <input type="text" id="sqlUser" name="sqlUser" value="<?= ih($value('sqlUser')) ?>" required> | |
| 80 | + | <p class="hint">Do not use <code>root</code> on a public server.</p> | |
| 81 | + | </div> | |
| 82 | + | <div class="field"> | |
| 83 | + | <label class="lbl" for="sqlPassword">Password</label> | |
| 84 | + | <input type="password" id="sqlPassword" name="sqlPassword" value="<?= ih($value('sqlPassword')) ?>"> | |
| 85 | + | </div> | |
| 86 | + | </div> | |
| 87 | + | ||
| 88 | + | <?php if ($tables !== null): ?> | |
| 89 | + | <h2>Server schema</h2> | |
| 90 | + | ||
| 91 | + | <?php if ($tables['ok']): ?> | |
| 92 | + | <p class="good">Connected, and your server's tables are there.</p> | |
| 93 | + | <?php else: ?> | |
| 94 | + | <div class="warn"> | |
| 95 | + | <strong>The server schema is missing.</strong><br> | |
| 96 | + | ZnoteX reads the tables your OT server creates — it does not create them, and | |
| 97 | + | importing them afterwards would wipe what ZnoteX writes. Import your server's | |
| 98 | + | <code>schema.sql</code> into <code><?= ih(install_get('sqlDatabase')) ?></code>, then | |
| 99 | + | press Test again. | |
| 100 | + | </div> | |
| 101 | + | <?php endif; ?> | |
| 102 | + | ||
| 103 | + | <table> | |
| 104 | + | <tr><th>Table</th><th>Status</th><th></th></tr> | |
| 105 | + | <?php foreach ($tables['required'] as $table => $found): ?> | |
| 106 | + | <tr> | |
| 107 | + | <td><code><?= ih($table) ?></code></td> | |
| 108 | + | <td class="<?= $found ? 'ok' : 'no' ?>"><?= $found ? '✓ present' : '✗ missing' ?></td> | |
| 109 | + | <td>required</td> | |
| 110 | + | </tr> | |
| 111 | + | <?php endforeach; ?> | |
| 112 | + | <?php foreach ($tables['optional'] as $table => $found): ?> | |
| 113 | + | <tr> | |
| 114 | + | <td><code><?= ih($table) ?></code></td> | |
| 115 | + | <td><?= $found ? '✓ present' : '—' ?></td> | |
| 116 | + | <td>optional — some pages need it</td> | |
| 117 | + | </tr> | |
| 118 | + | <?php endforeach; ?> | |
| 119 | + | </table> | |
| 120 | + | ||
| 121 | + | <?php if (!empty($tables['znote_installed'])): ?> | |
| 122 | + | <p class="warn"> | |
| 123 | + | A <code>znote</code> table already exists. The next step will not touch tables that | |
| 124 | + | are already there, so nothing you have will be lost. | |
| 125 | + | </p> | |
| 126 | + | <?php endif; ?> | |
| 127 | + | <?php endif; ?> | |
| 128 | + | ||
| 129 | + | <div class="actions"> | |
| 130 | + | <button class="btn ghost" type="submit" name="test" value="1">Test connection</button> | |
| 131 | + | <?php if ($tables !== null && $tables['ok']): ?> | |
| 132 | + | <button class="btn" type="submit" name="continue" value="1">Continue</button> | |
| 133 | + | <?php endif; ?> | |
| 134 | + | <a class="btn ghost" href="<?= install_url(1) ?>">Back</a> | |
| 135 | + | </div> | |
| 136 | + | </form> |
| @@ -0,0 +1,95 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** Step 3 - which server this site sits in front of, and how it is named. */ | |
| 3 | + | ||
| 4 | + | if (!defined('ZNOTE_INSTALL')) { http_response_code(403); die('Direct access denied.'); } | |
| 5 | + | ||
| 6 | + | $engines = array( | |
| 7 | + | 'TFS_10' => 'TFS 1.1 - 1.4.2', | |
| 8 | + | 'TFS_16' => 'TFS 1.6', | |
| 9 | + | 'CANARY' => 'Canary / OTServBR-Global', | |
| 10 | + | 'TFS_03' => 'TFS 0.3.6+ / 0.4 / OTX', | |
| 11 | + | 'TFS_02' => 'TFS 0.2.13+', | |
| 12 | + | 'OTHIRE' => 'OTHire', | |
| 13 | + | ); | |
| 14 | + | ||
| 15 | + | if ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
| 16 | + | ||
| 17 | + | $engine = (string)($_POST['ServerEngine'] ?? ''); | |
| 18 | + | $title = trim((string)($_POST['site_title'] ?? '')); | |
| 19 | + | $url = rtrim(trim((string)($_POST['site_url'] ?? '')), '/'); | |
| 20 | + | ||
| 21 | + | if (!isset($engines[$engine])) { | |
| 22 | + | install_error('Pick a server engine.'); | |
| 23 | + | } elseif ($title === '') { | |
| 24 | + | install_error('The site needs a name.'); | |
| 25 | + | } else { | |
| 26 | + | install_state(array( | |
| 27 | + | 'ServerEngine' => $engine, | |
| 28 | + | 'site_title' => $title, | |
| 29 | + | 'site_url' => $url, | |
| 30 | + | 'server_path' => rtrim(trim((string)($_POST['server_path'] ?? '')), '/\\'), | |
| 31 | + | )); | |
| 32 | + | install_max_step(4); | |
| 33 | + | header('Location: ' . install_url(4)); | |
| 34 | + | exit; | |
| 35 | + | } | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | // A sensible default URL, from the request itself. | |
| 39 | + | $guessUrl = install_get('site_url'); | |
| 40 | + | if ($guessUrl === '') { | |
| 41 | + | $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; | |
| 42 | + | $host = (string)($_SERVER['HTTP_HOST'] ?? 'localhost'); | |
| 43 | + | $base = rtrim(dirname((string)($_SERVER['SCRIPT_NAME'] ?? '')), '/\\'); | |
| 44 | + | $base = preg_replace('#/install$#', '', $base); | |
| 45 | + | $guessUrl = $scheme . '://' . $host . $base; | |
| 46 | + | } | |
| 47 | + | ?> | |
| 48 | + | <h1>Server</h1> | |
| 49 | + | <p class="lead">Which engine you run, and how the site introduces itself.</p> | |
| 50 | + | ||
| 51 | + | <form method="post"> | |
| 52 | + | <div class="field"> | |
| 53 | + | <label class="lbl" for="ServerEngine">Server engine</label> | |
| 54 | + | <select id="ServerEngine" name="ServerEngine"> | |
| 55 | + | <?php foreach ($engines as $key => $label): ?> | |
| 56 | + | <option value="<?= ih($key) ?>" <?= install_get('ServerEngine', 'TFS_10') === $key ? 'selected' : '' ?>> | |
| 57 | + | <?= ih($label) ?> | |
| 58 | + | </option> | |
| 59 | + | <?php endforeach; ?> | |
| 60 | + | </select> | |
| 61 | + | <p class="hint"> | |
| 62 | + | TFS 1.0 is not supported. TFS 1.6 and Canary differ from 1.x only in a few column | |
| 63 | + | names, which ZnoteX handles for you. | |
| 64 | + | </p> | |
| 65 | + | </div> | |
| 66 | + | ||
| 67 | + | <div class="row"> | |
| 68 | + | <div class="field"> | |
| 69 | + | <label class="lbl" for="site_title">Site name</label> | |
| 70 | + | <input type="text" id="site_title" name="site_title" | |
| 71 | + | value="<?= ih(install_get('site_title', 'My Open Tibia Server')) ?>" required> | |
| 72 | + | </div> | |
| 73 | + | <div class="field"> | |
| 74 | + | <label class="lbl" for="site_url">Site URL</label> | |
| 75 | + | <input type="text" id="site_url" name="site_url" value="<?= ih($guessUrl) ?>"> | |
| 76 | + | <p class="hint">Used in e-mails. No trailing slash.</p> | |
| 77 | + | </div> | |
| 78 | + | </div> | |
| 79 | + | ||
| 80 | + | <div class="field"> | |
| 81 | + | <label class="lbl" for="server_path">Server folder <em>(optional)</em></label> | |
| 82 | + | <input type="text" id="server_path" name="server_path" | |
| 83 | + | value="<?= ih(install_get('server_path')) ?>" | |
| 84 | + | placeholder="C:/forgottenserver or /home/ots"> | |
| 85 | + | <p class="hint"> | |
| 86 | + | Where <code>data/</code> and <code>config.lua</code> live. Needed by the creature | |
| 87 | + | library, the spell list and the server info page. You can set it later. | |
| 88 | + | </p> | |
| 89 | + | </div> | |
| 90 | + | ||
| 91 | + | <div class="actions"> | |
| 92 | + | <button class="btn" type="submit">Continue</button> | |
| 93 | + | <a class="btn ghost" href="<?= install_url(2) ?>">Back</a> | |
| 94 | + | </div> | |
| 95 | + | </form> |