| @@ -0,0 +1,94 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * Top navigation for the default theme. | |
| 4 | + | * | |
| 5 | + | * The links come from the database and are managed in Admin Panel > Menus, so | |
| 6 | + | * adding one no longer means editing this file - and every theme shares the | |
| 7 | + | * same navigation instead of carrying its own copy. | |
| 8 | + | * | |
| 9 | + | * The markup is still entirely this theme's business: theme_menu_items() only | |
| 10 | + | * hands over the data, already filtered for the current visitor. An entry | |
| 11 | + | * marked "Admins only" is absent from the array, not hidden with CSS. | |
| 12 | + | * | |
| 13 | + | * A theme that would rather hardcode its menu simply does not call this. | |
| 14 | + | */ | |
| 15 | + | ||
| 16 | + | $menuItems = theme_menu_items('main'); | |
| 17 | + | $menuLive = function_exists('theme_menu_available') ? theme_menu_available() : (bool)$menuItems; | |
| 18 | + | $menuLabel = static function ($value): string { | |
| 19 | + | return htmlspecialchars(function_exists('theme_menu_label') ? theme_menu_label((string)$value) : (string)$value, ENT_QUOTES, 'UTF-8'); | |
| 20 | + | }; | |
| 21 | + | ||
| 22 | + | // A site that has not run the menu migration yet gets the old hardcoded links, | |
| 23 | + | // so upgrading never leaves someone with no navigation at all. | |
| 24 | + | $menuFallback = !$menuLive; | |
| 25 | + | ?> | |
| 26 | + | <nav> | |
| 27 | + | <div class="container"> | |
| 28 | + | ||
| 29 | + | <!-- Menu left aligned --> | |
| 30 | + | <div class="pull-left"> | |
| 31 | + | <?php if (!$menuFallback): ?> | |
| 32 | + | <ul> | |
| 33 | + | <?php foreach ($menuItems as $item): ?> | |
| 34 | + | <li> | |
| 35 | + | <?php // A category carries no URL: it opens its children, it does not navigate. ?> | |
| 36 | + | <?php if ($item['url'] === ''): ?> | |
| 37 | + | <span class="menuCategory"> | |
| 38 | + | <?php if ($item['icon'] !== ''): ?> | |
| 39 | + | <i class="fa <?= htmlspecialchars($item['icon'], ENT_QUOTES, 'UTF-8') ?>"></i> | |
| 40 | + | <?php endif; ?> | |
| 41 | + | <?= $menuLabel($item['label']) ?> | |
| 42 | + | </span> | |
| 43 | + | <?php else: ?> | |
| 44 | + | <a href="<?= htmlspecialchars($item['url'], ENT_QUOTES, 'UTF-8') ?>" | |
| 45 | + | <?= $item['target'] !== '' ? 'target="' . htmlspecialchars($item['target'], ENT_QUOTES, 'UTF-8') . '" rel="noopener"' : '' ?>> | |
| 46 | + | <?php if ($item['icon'] !== ''): ?> | |
| 47 | + | <i class="fa <?= htmlspecialchars($item['icon'], ENT_QUOTES, 'UTF-8') ?>"></i> | |
| 48 | + | <?php endif; ?> | |
| 49 | + | <?= $menuLabel($item['label']) ?> | |
| 50 | + | </a> | |
| 51 | + | <?php endif; ?> | |
| 52 | + | ||
| 53 | + | <?php if ($item['children']): ?> | |
| 54 | + | <ul> | |
| 55 | + | <?php foreach ($item['children'] as $child): ?> | |
| 56 | + | <li> | |
| 57 | + | <a href="<?= htmlspecialchars($child['url'], ENT_QUOTES, 'UTF-8') ?>" | |
| 58 | + | <?= $child['target'] !== '' ? 'target="' . htmlspecialchars($child['target'], ENT_QUOTES, 'UTF-8') . '" rel="noopener"' : '' ?>> | |
| 59 | + | <?= $menuLabel($child['label']) ?> | |
| 60 | + | </a> | |
| 61 | + | </li> | |
| 62 | + | <?php endforeach; ?> | |
| 63 | + | </ul> | |
| 64 | + | <?php endif; ?> | |
| 65 | + | </li> | |
| 66 | + | <?php endforeach; ?> | |
| 67 | + | </ul> | |
| 68 | + | <?php else: ?> | |
| 69 | + | <ul> | |
| 70 | + | <li><a href="/"><i class="fa fa-home"></i> <?= h(t('nav.home')) ?></a></li> | |
| 71 | + | <li><a id="accountLink" href="myaccount.php"><i class="fa fa-user-circle"></i> <?= h(t('nav.account')) ?></a></li> | |
| 72 | + | <li><a href="onlinelist.php"><i class="fa fa-users"></i> <?= h(t('nav.community')) ?></a></li> | |
| 73 | + | <li><a href="serverinfo.php"><i class="fa fa-book"></i> <?= h(t('nav.library')) ?></a></li> | |
| 74 | + | <li><a href="support.php"><i class="fa fa-info-circle"></i> <?= h(t('nav.support')) ?></a></li> | |
| 75 | + | <li><a href="shop.php"><i class="fa fa-shopping-cart"></i> <?= h(t('nav.shop')) ?></a></li> | |
| 76 | + | </ul> | |
| 77 | + | <?php endif; ?> | |
| 78 | + | </div> | |
| 79 | + | ||
| 80 | + | <!-- Menu right aligned --> | |
| 81 | + | <div class="pull-right"> | |
| 82 | + | <ul> | |
| 83 | + | <?php if (user_logged_in() === true): ?> | |
| 84 | + | <li><a href="myaccount.php"><i class="fa fa-user"></i> <?= htmlspecialchars($user_data['name'] ?? '', ENT_QUOTES, 'UTF-8') ?></a></li> | |
| 85 | + | <li><a href="logout.php"><i class="fa fa-sign-out"></i> <?= h(t('nav.logout')) ?></a></li> | |
| 86 | + | <?php else: ?> | |
| 87 | + | <li><a href="#loginContainer" class="modIcon loginBtn"><i class="fa fa-lock"></i><i class="fa fa-unlock"></i> <?= h(t('nav.login')) ?></a></li> | |
| 88 | + | <li><a href="register.php"><i class="fa fa-key"></i> <?= h(t('nav.register')) ?></a></li> | |
| 89 | + | <?php endif; ?> | |
| 90 | + | </ul> | |
| 91 | + | </div> | |
| 92 | + | ||
| 93 | + | </div> | |
| 94 | + | </nav> |
| @@ -0,0 +1,283 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * Character Management page: page.php?p=editcharacter&character=<name> | |
| 4 | + | * | |
| 5 | + | * One character's actions (comment, gender, visibility, rename, delete) on a | |
| 6 | + | * single dedicated page instead of a dropdown+submit combo on myaccount.php. | |
| 7 | + | * Reuses the exact same server-side rules as myaccount.php's own switch, just | |
| 8 | + | * scoped to the character validated below instead of a posted selector. | |
| 9 | + | */ | |
| 10 | + | ||
| 11 | + | protect_page(); | |
| 12 | + | ||
| 13 | + | $char_name = getValue($_GET['character'] ?? null); | |
| 14 | + | $char_id = $char_name !== false ? user_character_id($char_name) : false; | |
| 15 | + | ||
| 16 | + | if ($char_name === false || $char_id === false || (int)user_character_account_id($char_name) !== (int)$session_user_id) { | |
| 17 | + | echo '<h1>' . t('page.not_found') . '</h1>'; | |
| 18 | + | return; | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | $errors = []; | |
| 22 | + | $notice = null; | |
| 23 | + | $deleted = false; | |
| 24 | + | ||
| 25 | + | if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && !empty($_POST['action'])) { | |
| 26 | + | if (!Token::isValid($_POST['token'] ?? null)) { | |
| 27 | + | exit(); | |
| 28 | + | } | |
| 29 | + | $action = getValue($_POST['action'] ?? null); | |
| 30 | + | ||
| 31 | + | switch ($action) { | |
| 32 | + | case 'toggle_hide': | |
| 33 | + | $hide = (user_character_hide($char_name) == 1 ? 0 : 1); | |
| 34 | + | user_character_set_hide($char_id, $hide); | |
| 35 | + | break; | |
| 36 | + | ||
| 37 | + | case 'change_gender': | |
| 38 | + | if (!user_is_online_10($char_id)) { | |
| 39 | + | $tickets = shop_account_gender_tickets($session_user_id); | |
| 40 | + | $tickets = is_array($tickets) ? $tickets : array(); | |
| 41 | + | if (!empty($tickets) || $config['free_sex_change'] == true) { | |
| 42 | + | $infinite = false; | |
| 43 | + | $tks = 0; | |
| 44 | + | foreach ($tickets as $ticket) { | |
| 45 | + | if ($ticket['count'] == 0) $infinite = true; | |
| 46 | + | else if ((int)$ticket['count'] > 0 && $infinite === false) $tks += (int)$ticket['count']; | |
| 47 | + | } | |
| 48 | + | if ($infinite === true) $tks = 0; | |
| 49 | + | $dbid = isset($tickets[0]['id']) ? (int)$tickets[0]['id'] : 0; | |
| 50 | + | if ($dbid > 0 && $tickets[0]['count'] > 1) { | |
| 51 | + | $tks--; | |
| 52 | + | shop_update_row_count($dbid, ((int)$tickets[0]['count'] - 1)); | |
| 53 | + | } else if ($dbid > 0 && $tickets[0]['count'] == 1) { | |
| 54 | + | shop_delete_row_order($dbid); | |
| 55 | + | $tks--; | |
| 56 | + | } | |
| 57 | + | user_character_change_gender($char_name); | |
| 58 | + | $notice = t('acc.gender_changed', ['name' => $char_name]); | |
| 59 | + | if ($tks > 0) $notice .= ' ' . t('acc.gender_tickets_left', ['n' => $tks]); | |
| 60 | + | else if ($infinite !== true) $notice .= ' ' . t('acc.gender_tickets_out'); | |
| 61 | + | } else { | |
| 62 | + | $errors[] = t('acc.no_gender_tickets'); | |
| 63 | + | } | |
| 64 | + | } else { | |
| 65 | + | $errors[] = t('acc.must_be_offline'); | |
| 66 | + | } | |
| 67 | + | break; | |
| 68 | + | ||
| 69 | + | case 'update_comment': | |
| 70 | + | user_update_comment($char_id, getValue($_POST['comment'] ?? null)); | |
| 71 | + | $notice = t('acc.comment_updated'); | |
| 72 | + | break; | |
| 73 | + | ||
| 74 | + | case 'change_name': | |
| 75 | + | $newname = isset($_POST['newName']) ? getValue($_POST['newName'] ?? null) : ''; | |
| 76 | + | ||
| 77 | + | if (user_is_online_10($char_id)) { | |
| 78 | + | $errors[] = t('acc.must_be_offline'); | |
| 79 | + | } | |
| 80 | + | ||
| 81 | + | $order = db()->fetchOne( | |
| 82 | + | "SELECT `id`, `account_id` FROM `znote_shop_orders` WHERE `type` = 4 AND `account_id` = ? LIMIT 1;", | |
| 83 | + | [(int)$session_user_id] | |
| 84 | + | ); | |
| 85 | + | if ($order === false) { | |
| 86 | + | $errors[] = t('acc.no_name_tickets'); | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | $validated = validate_name($newname); | |
| 90 | + | if ($validated === false) { | |
| 91 | + | $errors[] = t('acc.name_max_words'); | |
| 92 | + | } else { | |
| 93 | + | $newname = $validated; | |
| 94 | + | if (empty($newname)) { | |
| 95 | + | $errors[] = t('acc.name_required'); | |
| 96 | + | } else if (user_character_exist($newname) !== false) { | |
| 97 | + | $errors[] = t('acc.name_taken'); | |
| 98 | + | } else if (!preg_match("/^[a-zA-Z_ ]+$/", $newname)) { | |
| 99 | + | $errors[] = t('acc.name_letters'); | |
| 100 | + | } else if (strlen($newname) < $config['minL'] || strlen($newname) > $config['maxL']) { | |
| 101 | + | $errors[] = t('acc.name_length', ['min' => $config['minL'], 'max' => $config['maxL']]); | |
| 102 | + | } else if (!ctype_upper($newname[0])) { | |
| 103 | + | $errors[] = t('acc.name_capital'); | |
| 104 | + | } | |
| 105 | + | ||
| 106 | + | $resname = explode(' ', (string)($_POST['newName'] ?? '')); | |
| 107 | + | foreach ($resname as $res) { | |
| 108 | + | if (in_array(strtolower($res), $config['invalidNameTags'])) { | |
| 109 | + | $errors[] = t('reg.restricted_word'); | |
| 110 | + | } else if (strlen($res) == 1) { | |
| 111 | + | $errors[] = t('reg.words_too_short'); | |
| 112 | + | } | |
| 113 | + | } | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | if (empty($errors) && !empty($newname)) { | |
| 117 | + | $db = db(); | |
| 118 | + | if (!$db->beginTransaction()) { | |
| 119 | + | $errors[] = t('acc.sync_failed'); | |
| 120 | + | } else { | |
| 121 | + | $ok = $db->execute("UPDATE `players` SET `name` = ? WHERE `id` = ? LIMIT 1;", [$newname, $char_id]); | |
| 122 | + | $ok = $ok && $db->execute("DELETE FROM `znote_shop_orders` WHERE `id` = ? LIMIT 1;", [(int)$order['id']]); | |
| 123 | + | if ($ok) { | |
| 124 | + | $db->commit(); | |
| 125 | + | $char_name = $newname; | |
| 126 | + | $notice = t('acc.name_changed', ['name' => $newname]); | |
| 127 | + | } else { | |
| 128 | + | $db->rollback(); | |
| 129 | + | $errors[] = t('acc.sync_failed'); | |
| 130 | + | } | |
| 131 | + | } | |
| 132 | + | } | |
| 133 | + | break; | |
| 134 | + | ||
| 135 | + | case 'delete_character': | |
| 136 | + | if (!user_is_online_10($char_id)) { | |
| 137 | + | if (guild_leader_gid($char_id) === false) { | |
| 138 | + | user_delete_character_soft($char_id); | |
| 139 | + | $deleted = true; | |
| 140 | + | } else { | |
| 141 | + | $errors[] = t('acc.is_guild_leader'); | |
| 142 | + | } | |
| 143 | + | } else { | |
| 144 | + | $errors[] = t('acc.must_be_offline'); | |
| 145 | + | } | |
| 146 | + | break; | |
| 147 | + | } | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | $char = null; | |
| 151 | + | $gender = null; | |
| 152 | + | $comment_data = null; | |
| 153 | + | if (!$deleted) { | |
| 154 | + | $rows = user_character_list($session_user_id); | |
| 155 | + | if (is_array($rows)) { | |
| 156 | + | foreach ($rows as $row) { | |
| 157 | + | if ($row['name'] === $char_name) { | |
| 158 | + | $char = $row; | |
| 159 | + | break; | |
| 160 | + | } | |
| 161 | + | } | |
| 162 | + | } | |
| 163 | + | $gender = user_character_data($char_id, 'sex'); | |
| 164 | + | $comment_data = user_znote_character_data($char_id, 'comment'); | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | $nameTicketCount = (int)(db()->fetchOne( | |
| 168 | + | "SELECT COUNT(*) AS `c` FROM `znote_shop_orders` WHERE `type` = 4 AND `account_id` = ?;", | |
| 169 | + | [(int)$session_user_id] | |
| 170 | + | )['c'] ?? 0); | |
| 171 | + | ||
| 172 | + | $genderTickets = shop_account_gender_tickets($session_user_id); | |
| 173 | + | $genderTickets = is_array($genderTickets) ? $genderTickets : array(); | |
| 174 | + | $genderTicketInfinite = false; | |
| 175 | + | $genderTicketCount = 0; | |
| 176 | + | foreach ($genderTickets as $ticket) { | |
| 177 | + | if ($ticket['count'] == 0) $genderTicketInfinite = true; | |
| 178 | + | else if ((int)$ticket['count'] > 0 && $genderTicketInfinite === false) $genderTicketCount += (int)$ticket['count']; | |
| 179 | + | } | |
| 180 | + | ?> | |
| 181 | + | <div class="znx-acct"> | |
| 182 | + | ||
| 183 | + | <div class="znx-editchar-back"><a href="myaccount.php">« <?= t_default('acc.back_to_account', 'Back to My Account') ?></a></div> | |
| 184 | + | ||
| 185 | + | <?php if (!empty($errors)): ?> | |
| 186 | + | <div class="znx-acct-info znx-editchar-notice znx-editchar-notice--error"><?php echo output_errors($errors); ?></div> | |
| 187 | + | <?php endif; ?> | |
| 188 | + | <?php if ($notice): ?> | |
| 189 | + | <div class="znx-acct-info znx-editchar-notice znx-editchar-notice--ok"><?php echo $notice; ?></div> | |
| 190 | + | <?php endif; ?> | |
| 191 | + | ||
| 192 | + | <?php if ($deleted): ?> | |
| 193 | + | <div class="znx-acct-head"><?= htmlspecialchars($char_name, ENT_QUOTES, 'UTF-8') ?></div> | |
| 194 | + | <div class="znx-acct-info"> | |
| 195 | + | <p><?= t_default('acc.delete_scheduled', 'This character is scheduled for deletion. You can cancel it from your account page.') ?></p> | |
| 196 | + | <a href="myaccount.php" class="znx-acct-btn"><?= t_default('acc.back_to_account', 'Back to My Account') ?></a> | |
| 197 | + | </div> | |
| 198 | + | <?php elseif ($char !== null): ?> | |
| 199 | + | ||
| 200 | + | <div class="znx-acct-head"><?= htmlspecialchars($char_name, ENT_QUOTES, 'UTF-8') ?> — <?= t_default('acc.character_management', 'Character Management') ?></div> | |
| 201 | + | <div class="znx-acct-info znx-editchar-info"> | |
| 202 | + | <div class="znx-editchar-info__row"><span><?= mb_strtoupper(t('common.vocation')) ?></span><strong><?= htmlspecialchars((string)$char['vocation'], ENT_QUOTES, 'UTF-8') ?></strong></div> | |
| 203 | + | <div class="znx-editchar-info__row"><span><?= mb_strtoupper(t('common.level')) ?></span><strong><?= (int)$char['level'] ?></strong></div> | |
| 204 | + | <div class="znx-editchar-info__row"><span><?= mb_strtoupper(t('common.town')) ?></span><strong><?= htmlspecialchars((string)$char['town_id'], ENT_QUOTES, 'UTF-8') ?></strong></div> | |
| 205 | + | </div> | |
| 206 | + | ||
| 207 | + | <div class="znx-acct-head"><?= t('acc.change_name') ?></div> | |
| 208 | + | <?php if ($nameTicketCount > 0): ?> | |
| 209 | + | <form action="" method="post" class="znx-editchar-box"> | |
| 210 | + | <input type="hidden" name="action" value="change_name"> | |
| 211 | + | <?php Token::create(); ?> | |
| 212 | + | <div class="znx-editchar-ticket-note"><?= t_default('acc.tickets_remaining', 'Remaining:') ?> <strong><?= $nameTicketCount ?></strong></div> | |
| 213 | + | <div class="znx-acct-toolbar"> | |
| 214 | + | <div class="znx-acct-toolbar__field"> | |
| 215 | + | <input type="text" name="newName" placeholder="<?= htmlspecialchars(t('common.new_name_placeholder'), ENT_QUOTES, 'UTF-8') ?>" class="znx-acct-select"> | |
| 216 | + | </div> | |
| 217 | + | <div class="znx-acct-toolbar__submit"><button type="submit" class="znx-acct-btn"><?= t('common.submit') ?></button></div> | |
| 218 | + | </div> | |
| 219 | + | </form> | |
| 220 | + | <?php else: ?> | |
| 221 | + | <div class="znx-editchar-box znx-editchar-box--muted"><?= t('acc.no_name_tickets') ?></div> | |
| 222 | + | <?php endif; ?> | |
| 223 | + | ||
| 224 | + | <div class="znx-acct-head"><?= t('acc.change_gender') ?></div> | |
| 225 | + | <form action="" method="post" class="znx-editchar-box"> | |
| 226 | + | <input type="hidden" name="action" value="change_gender"> | |
| 227 | + | <?php Token::create(); ?> | |
| 228 | + | <div class="znx-editchar-inline"> | |
| 229 | + | <span><?= t_default('acc.current_gender', 'Current gender:') ?> <strong><?= ($gender && (int)$gender['sex'] === 1) ? t_default('common.male', 'Male') : t_default('common.female', 'Female') ?></strong></span> | |
| 230 | + | <button type="submit" class="znx-char-action"><?= t('acc.change_gender') ?></button> | |
| 231 | + | </div> | |
| 232 | + | <div class="znx-editchar-ticket-note"> | |
| 233 | + | <?php if ($config['free_sex_change'] == true): ?> | |
| 234 | + | <?= t_default('acc.tickets_free', 'Free for this account.') ?> | |
| 235 | + | <?php elseif ($genderTicketInfinite): ?> | |
| 236 | + | <?= t_default('acc.tickets_unlimited', 'Unlimited gender change tickets.') ?> | |
| 237 | + | <?php elseif ($genderTicketCount > 0): ?> | |
| 238 | + | <?= t_default('acc.tickets_remaining', 'Remaining:') ?> <strong><?= $genderTicketCount ?></strong> | |
| 239 | + | <?php else: ?> | |
| 240 | + | <?= t('acc.no_gender_tickets') ?> | |
| 241 | + | <?php endif; ?> | |
| 242 | + | </div> | |
| 243 | + | </form> | |
| 244 | + | ||
| 245 | + | <div class="znx-acct-head"><?= t_default('acc.visibility', 'Visibility') ?></div> | |
| 246 | + | <form action="" method="post" class="znx-editchar-box"> | |
| 247 | + | <input type="hidden" name="action" value="toggle_hide"> | |
| 248 | + | <?php Token::create(); ?> | |
| 249 | + | <div class="znx-editchar-inline"> | |
| 250 | + | <span><?= t_default('acc.current_status', 'Current status:') ?> <strong><?= htmlspecialchars(hide_char_to_name($char['hide_char']), ENT_QUOTES, 'UTF-8') ?></strong></span> | |
| 251 | + | <button type="submit" class="znx-char-action"><?= t('acc.toggle_hide') ?></button> | |
| 252 | + | </div> | |
| 253 | + | </form> | |
| 254 | + | ||
| 255 | + | <div class="znx-acct-head"><?= t('acc.change_comment') ?></div> | |
| 256 | + | <form action="" method="post" class="znx-editchar-box"> | |
| 257 | + | <input type="hidden" name="action" value="update_comment"> | |
| 258 | + | <?php Token::create(); ?> | |
| 259 | + | <textarea name="comment" class="znx-editchar-textarea" rows="6"><?php echo htmlspecialchars((string)($comment_data['comment'] ?? ''), ENT_QUOTES, 'UTF-8'); ?></textarea> | |
| 260 | + | <button type="submit" class="znx-acct-btn"><?= t('acc.update_comment') ?></button> | |
| 261 | + | </form> | |
| 262 | + | ||
| 263 | + | <div class="znx-acct-head"><?= t('acc.delete_char') ?></div> | |
| 264 | + | <form action="" method="post" class="znx-editchar-box"> | |
| 265 | + | <input type="hidden" name="action" value="delete_character"> | |
| 266 | + | <?php Token::create(); ?> | |
| 267 | + | <button type="submit" class="znx-char-action znx-char-action--danger needconfirmation"><?= t('acc.delete_char') ?></button> | |
| 268 | + | </form> | |
| 269 | + | ||
| 270 | + | <?php endif; ?> | |
| 271 | + | ||
| 272 | + | </div> | |
| 273 | + | <script> | |
| 274 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 275 | + | document.querySelectorAll('.needconfirmation').forEach(function (btn) { | |
| 276 | + | btn.addEventListener('click', function (event) { | |
| 277 | + | if (!confirm(<?= json_encode(t('acc.confirm_delete', ['name' => $char_name])) ?>)) { | |
| 278 | + | event.preventDefault(); | |
| 279 | + | } | |
| 280 | + | }); | |
| 281 | + | }); | |
| 282 | + | }); | |
| 283 | + | </script> |
| @@ -0,0 +1,146 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * Houses list. | |
| 4 | + | * | |
| 5 | + | * Loaded directly by the root houses.php (theme_file('pages/houses.php')), | |
| 6 | + | * not through page.php - so this file opens and closes the theme itself. | |
| 7 | + | */ | |
| 8 | + | $h = static function ($v): string { return htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8'); }; | |
| 9 | + | ||
| 10 | + | theme_open(); | |
| 11 | + | ||
| 12 | + | if ($config['log_ip']) | |
| 13 | + | znote_visitor_insert_detailed_data(3); | |
| 14 | + | ||
| 15 | + | $querystring_id = &$_GET['id']; | |
| 16 | + | $townid = ($querystring_id) ? (int)$_GET['id'] : $config['houseConfig']['HouseListDefaultTown']; | |
| 17 | + | $towns = $config['towns']; | |
| 18 | + | ||
| 19 | + | $order = &$_GET['order']; | |
| 20 | + | $type = &$_GET['type']; | |
| 21 | + | ?> | |
| 22 | + | <div class="znx-acct"> | |
| 23 | + | ||
| 24 | + | <div class="znx-acct-head"><?= t('common.town') ?></div> | |
| 25 | + | ||
| 26 | + | <form action="" method="get" class="znx-acct-toolbar"> | |
| 27 | + | <div class="znx-acct-toolbar__field"> | |
| 28 | + | <select name="id" class="znx-acct-select"> | |
| 29 | + | <?php foreach ($towns as $id => $name): ?> | |
| 30 | + | <option value="<?= $h($id) ?>" <?= $townid == $id ? 'selected' : '' ?>><?= $h($name) ?></option> | |
| 31 | + | <?php endforeach; ?> | |
| 32 | + | </select> | |
| 33 | + | </div> | |
| 34 | + | ||
| 35 | + | <?php | |
| 36 | + | $order_allowed = array('id', 'name', 'size', 'beds', 'rent', 'owner'); | |
| 37 | + | $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')); | |
| 38 | + | ?> | |
| 39 | + | <div class="znx-acct-toolbar__field"> | |
| 40 | + | <select name="order" class="znx-acct-select"> | |
| 41 | + | <?php foreach ($order_allowed as $o): ?> | |
| 42 | + | <option value="<?= $h($o) ?>" <?= $o == $order ? 'selected' : '' ?>><?= $h($order_labels[$o]) ?></option> | |
| 43 | + | <?php endforeach; ?> | |
| 44 | + | </select> | |
| 45 | + | </div> | |
| 46 | + | ||
| 47 | + | <?php $type_allowed = array('desc', 'asc'); ?> | |
| 48 | + | <div class="znx-acct-toolbar__field"> | |
| 49 | + | <select name="type" class="znx-acct-select"> | |
| 50 | + | <?php foreach ($type_allowed as $t): ?> | |
| 51 | + | <option value="<?= $h($t) ?>" <?= $t == $type ? 'selected' : '' ?>><?= $t == 'desc' ? t('houses.descending') : t('houses.ascending') ?></option> | |
| 52 | + | <?php endforeach; ?> | |
| 53 | + | </select> | |
| 54 | + | </div> | |
| 55 | + | ||
| 56 | + | <div class="znx-acct-toolbar__submit"><button type="submit" class="znx-acct-btn"><?= t('houses.fetch') ?></button></div> | |
| 57 | + | </form> | |
| 58 | + | ||
| 59 | + | <?php | |
| 60 | + | if (!in_array($order, $order_allowed)) | |
| 61 | + | $order = 'id'; | |
| 62 | + | ||
| 63 | + | if (!in_array($type, $type_allowed)) | |
| 64 | + | $type = 'desc'; | |
| 65 | + | ||
| 66 | + | $cache = new Cache('engine/cache/houses/houses-' . $order . '-' . $type); | |
| 67 | + | $houses = array(); | |
| 68 | + | ||
| 69 | + | if ($cache->hasExpired()) { | |
| 70 | + | ||
| 71 | + | $houses = db()->fetchAll(" | |
| 72 | + | SELECT | |
| 73 | + | `id`, `owner`, `paid`, `warnings`, `name`, `rent`, `town_id`, | |
| 74 | + | `size`, `beds`, " . houseSelect(array('bid','bid_end','last_bid','highest_bidder')) . " | |
| 75 | + | FROM `houses` | |
| 76 | + | ORDER BY {$order} {$type}; | |
| 77 | + | "); | |
| 78 | + | ||
| 79 | + | if ($houses !== false) { | |
| 80 | + | $playerlist = array(); | |
| 81 | + | ||
| 82 | + | foreach ($houses as $houseRow) | |
| 83 | + | if ($houseRow['owner'] > 0) | |
| 84 | + | $playerlist[] = (int)$houseRow['owner']; | |
| 85 | + | ||
| 86 | + | if (!empty($playerlist)) { | |
| 87 | + | $placeholders = implode(',', array_fill(0, count($playerlist), '?')); | |
| 88 | + | $tmpPlayers = db()->fetchAll("SELECT `id`, `name` FROM players WHERE `id` IN ($placeholders);", $playerlist); | |
| 89 | + | ||
| 90 | + | $tmpById = array(); | |
| 91 | + | foreach ($tmpPlayers as $p) | |
| 92 | + | $tmpById[$p['id']] = $p['name']; | |
| 93 | + | ||
| 94 | + | for ($i = 0; $i < count($houses); $i++) | |
| 95 | + | if ($houses[$i]['owner'] > 0) | |
| 96 | + | $houses[$i]['ownername'] = $tmpById[$houses[$i]['owner']]; | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | $cache->setContent($houses); | |
| 100 | + | $cache->save(); | |
| 101 | + | } | |
| 102 | + | } else | |
| 103 | + | $houses = $cache->load(); | |
| 104 | + | ||
| 105 | + | if ($houses !== false || !empty($houses)): | |
| 106 | + | ?> | |
| 107 | + | <div class="znx-acct-table-wrap"> | |
| 108 | + | <table id="housetable" class="znx-acct-table"> | |
| 109 | + | <tr class="yellow"> | |
| 110 | + | <th><?= t('common.name') ?></th> | |
| 111 | + | <th><?= t('house.size') ?></th> | |
| 112 | + | <th><?= t('house.beds') ?></th> | |
| 113 | + | <th><?= t('house.rent') ?></th> | |
| 114 | + | <th><?= t('house.owner') ?></th> | |
| 115 | + | <th><?= t('common.town') ?></th> | |
| 116 | + | </tr> | |
| 117 | + | <?php foreach ($houses as $house): ?> | |
| 118 | + | <?php if ($house['town_id'] == $townid): ?> | |
| 119 | + | <?php $town_name = $towns[$house['town_id']] ?? null; ?> | |
| 120 | + | <tr> | |
| 121 | + | <td><a href="house.php?id=<?= (int) $house['id'] ?>"><?= $h($house['name']) ?></a></td> | |
| 122 | + | <td><?= (int) $house['size'] ?></td> | |
| 123 | + | <td><?= (int) $house['beds'] ?></td> | |
| 124 | + | <td><?= (int) $house['rent'] ?></td> | |
| 125 | + | <?php if ($house['owner'] != 0): ?> | |
| 126 | + | <td><a href="characterprofile.php?name=<?= $h($house['ownername']) ?>" target="_blank"><?= $h($house['ownername']) ?></a></td> | |
| 127 | + | <?php else: ?> | |
| 128 | + | <td><?= ($house['highest_bidder'] == 0) ? $h(t('common.none')) : '<b>' . $h(t_default('houses.selling', 'Selling')) . '</b>' ?></td> | |
| 129 | + | <?php endif; ?> | |
| 130 | + | <td><?= $town_name ? $h($town_name) : $h(t_default('houses.unknown_town', 'Specify town id') . ' ' . $house['town_id'] . ' ' . t_default('houses.unknown_town_suffix', 'name in config.php first.')) ?></td> | |
| 131 | + | </tr> | |
| 132 | + | <?php endif; ?> | |
| 133 | + | <?php endforeach; ?> | |
| 134 | + | </table> | |
| 135 | + | </div> | |
| 136 | + | <?php | |
| 137 | + | else: | |
| 138 | + | ?> | |
| 139 | + | <div class="znx-acct-head"><?= t('houses.fetch_failed') ?></div> | |
| 140 | + | <div class="znx-empty-box"><?= t('houses.empty') ?></div> | |
| 141 | + | <?php | |
| 142 | + | endif; ?> | |
| 143 | + | ||
| 144 | + | </div><!-- .znx-acct --> | |
| 145 | + | <?php | |
| 146 | + | theme_close(); |
| @@ -0,0 +1,170 @@ | |||
| 1 | + | <?php | |
| 2 | + | $znxPayH = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); | |
| 3 | + | ||
| 4 | + | $providerConfigs = array( | |
| 5 | + | 'paypal' => $paypal ?? array(), | |
| 6 | + | 'stripe' => $stripe ?? array(), | |
| 7 | + | 'mercadopago' => $mercadopago ?? array(), | |
| 8 | + | ); | |
| 9 | + | $providerLabels = array( | |
| 10 | + | 'paypal' => t('buypoints.paypal'), | |
| 11 | + | 'stripe' => 'Stripe', | |
| 12 | + | 'mercadopago' => 'Mercado Pago', | |
| 13 | + | ); | |
| 14 | + | $enabledProviders = array(); | |
| 15 | + | foreach (array('paypal', 'stripe', 'mercadopago') as $provider) { | |
| 16 | + | if (!empty($providerConfigs[$provider]['enabled'])) $enabledProviders[] = $provider; | |
| 17 | + | } | |
| 18 | + | $hasPagseguro = !empty($config['pagseguro']['enabled']); | |
| 19 | + | $hasPaygol = !empty($config['paygol']['enabled']); | |
| 20 | + | $anyEnabled = $enabledProviders || $hasPagseguro || $hasPaygol; | |
| 21 | + | $firstTable = true; | |
| 22 | + | ?> | |
| 23 | + | ||
| 24 | + | <style> | |
| 25 | + | .znx-pay-modern{--pay-surface:var(--box-inner-bg,var(--box-bg,var(--nz-panel,var(--wc-bg-2,var(--z-panel-2,var(--s-panel,var(--primary,rgb(30,33,40))))))));--pay-surface-2:color-mix(in srgb,var(--pay-surface) 82%,#000);--pay-well:color-mix(in srgb,var(--pay-surface) 65%,#000);--pay-text:var(--font-color,var(--text,var(--nz-text,var(--wc-text,var(--z-text,var(--s-text,rgb(155,162,177)))))));--pay-muted:color-mix(in srgb,var(--pay-text) 58%,transparent);--pay-border:var(--border,var(--box-inner-border,var(--box-border,var(--nz-border-soft,var(--wc-line,var(--z-border,var(--s-border2,rgb(19,20,23))))))));--pay-accent:var(--accent,var(--link,var(--nz-accent,#d1a233)));--pay-good:#79c36a;--pay-radius:6px;margin:0 0 18px;color:var(--pay-text)} | |
| 26 | + | .znx-pay-modern *{box-sizing:border-box} | |
| 27 | + | .znx-pay-head{margin:0 0 12px;padding:14px 16px;border:1px solid var(--pay-border);border-radius:var(--pay-radius);background:var(--pay-surface);box-shadow:inset 0 1px 0 rgba(255,255,255,.03)} | |
| 28 | + | .znx-pay-head h1{margin:0 0 5px;font-size:26px;line-height:1.05;color:var(--pay-accent)} | |
| 29 | + | .znx-pay-head p{margin:0;color:var(--pay-muted)} | |
| 30 | + | .znx-pay-section{margin:0 0 14px;border:1px solid var(--pay-border);border-radius:var(--pay-radius);background:var(--pay-surface-2);box-shadow:none;overflow:hidden} | |
| 31 | + | .znx-pay-section__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px;background:var(--pay-well);color:var(--pay-accent);border-bottom:1px solid var(--pay-border)} | |
| 32 | + | .znx-pay-section__head h2{margin:0;font-size:16px;color:var(--pay-accent)} | |
| 33 | + | .znx-pay-section__head span{color:var(--pay-muted);font-size:13px} | |
| 34 | + | .znx-pay-table-wrap{overflow-x:auto} | |
| 35 | + | .znx-pay-table{width:100%;border-collapse:separate;border-spacing:0} | |
| 36 | + | .znx-pay-table th{padding:10px 12px;background:var(--pay-surface);color:var(--pay-muted);font-size:12px;letter-spacing:.04em;text-align:left;text-transform:uppercase;white-space:nowrap} | |
| 37 | + | .znx-pay-table td{padding:11px 12px;border-top:1px solid var(--pay-border);vertical-align:middle} | |
| 38 | + | .znx-pay-table tr:hover td{background:color-mix(in srgb,var(--pay-accent) 6%,transparent)} | |
| 39 | + | .znx-pay-price{font-weight:900;color:var(--pay-text)} | |
| 40 | + | .znx-pay-points{display:inline-flex;align-items:center;gap:7px;font-weight:900;color:var(--pay-accent)} | |
| 41 | + | .znx-pay-points::before{content:"";display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--pay-accent)} | |
| 42 | + | .znx-pay-bonus{display:inline-flex;padding:4px 8px;border:1px solid rgba(121,195,106,.35);border-radius:var(--pay-radius);background:rgba(121,195,106,.08);color:var(--pay-good);font-size:12px;font-weight:900} | |
| 43 | + | .znx-pay-action form{margin:0} | |
| 44 | + | .znx-pay-btn{display:inline-flex;align-items:center;justify-content:center;min-height:34px;padding:8px 14px;border:1px solid var(--pay-accent);border-radius:var(--pay-radius);background:var(--pay-accent);color:#1a1510;font-weight:900;cursor:pointer;box-shadow:none;white-space:nowrap} | |
| 45 | + | .znx-pay-btn:hover{filter:brightness(1.1)} | |
| 46 | + | .znx-pay-provider-form{padding:14px} | |
| 47 | + | .znx-pay-provider-form form{display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:0} | |
| 48 | + | .znx-pay-provider-form input[type=number]{min-height:34px;padding:7px 10px;border:1px solid var(--pay-border);border-radius:var(--pay-radius);background:var(--pay-well);color:var(--pay-text)} | |
| 49 | + | .znx-pay-empty{padding:18px;border:1px solid var(--pay-border);border-radius:var(--pay-radius);background:var(--pay-surface-2)} | |
| 50 | + | @media(max-width:640px){.znx-pay-section__head{display:block}.znx-pay-section__head span{display:block;margin-top:4px}.znx-pay-table th:nth-child(3),.znx-pay-table td:nth-child(3){display:none}} | |
| 51 | + | </style> | |
| 52 | + | ||
| 53 | + | <section class="znx-pay-modern buypoints" id="buypoints"> | |
| 54 | + | <header class="znx-pay-head"> | |
| 55 | + | <h1><?= $znxPayH(t('buypoints.title')) ?></h1> | |
| 56 | + | <p><?= $znxPayH(t('buypoints.points')) ?> / <?= $znxPayH(t('buypoints.price')) ?></p> | |
| 57 | + | </header> | |
| 58 | + | ||
| 59 | + | <?php if (!$anyEnabled): ?> | |
| 60 | + | <div class="znx-pay-empty"> | |
| 61 | + | <h2><?= $znxPayH(t('buypoints.disabled')) ?></h2> | |
| 62 | + | <p><?= $znxPayH(t('buypoints.disabled_text')) ?></p> | |
| 63 | + | </div> | |
| 64 | + | <?php endif; ?> | |
| 65 | + | ||
| 66 | + | <?php foreach ($enabledProviders as $provider): ?> | |
| 67 | + | <?php | |
| 68 | + | $cfg = $providerConfigs[$provider]; | |
| 69 | + | $showBonus = !empty($cfg['showBonus']); | |
| 70 | + | $currency = (string)($cfg['currency'] ?? ''); | |
| 71 | + | $pointsPerCurrency = (float)($cfg['points_per_currency'] ?? 0); | |
| 72 | + | ?> | |
| 73 | + | <section class="znx-pay-section"> | |
| 74 | + | <div class="znx-pay-section__head"> | |
| 75 | + | <h2><?= $znxPayH($providerLabels[$provider]) ?></h2> | |
| 76 | + | <span><?= $znxPayH($currency) ?></span> | |
| 77 | + | </div> | |
| 78 | + | <div class="znx-pay-table-wrap"> | |
| 79 | + | <table<?= $firstTable ? ' id="buypointsTable"' : '' ?> class="znx-pay-table"> | |
| 80 | + | <thead> | |
| 81 | + | <tr> | |
| 82 | + | <th><?= $znxPayH(t('buypoints.price')) ?></th> | |
| 83 | + | <th><?= $znxPayH(t('buypoints.points')) ?></th> | |
| 84 | + | <?php if ($showBonus): ?><th><?= $znxPayH(t('buypoints.bonus_col')) ?></th><?php endif; ?> | |
| 85 | + | <th><?= $znxPayH(t('buypoints.action')) ?></th> | |
| 86 | + | </tr> | |
| 87 | + | </thead> | |
| 88 | + | <tbody> | |
| 89 | + | <?php foreach ((array)$prices as $price => $points): ?> | |
| 90 | + | <tr> | |
| 91 | + | <td><span class="znx-pay-price"><?= $znxPayH($price) ?> <?= $znxPayH($currency) ?></span></td> | |
| 92 | + | <td><span class="znx-pay-points"><?= number_format((int)$points) ?></span></td> | |
| 93 | + | <?php if ($showBonus): ?><td><span class="znx-pay-bonus"><?= $znxPayH(calculate_discount($pointsPerCurrency * (float)$price, (int)$points)) ?> <?= $znxPayH(t('buypoints.bonus')) ?></span></td><?php endif; ?> | |
| 94 | + | <td class="znx-pay-action"> | |
| 95 | + | <?php if ($provider === 'paypal'): ?> | |
| 96 | + | <form action="https://www.paypal.com/cgi-bin/webscr" method="POST"> | |
| 97 | + | <input type="hidden" name="cmd" value="_xclick"> | |
| 98 | + | <input type="hidden" name="business" value="<?= $znxPayH($paypal['email']) ?>"> | |
| 99 | + | <input type="hidden" name="item_name" value="<?= $znxPayH(t('buypoints.shop_points_on', array('points' => $points, 'site' => $config['site_title']))) ?>"> | |
| 100 | + | <input type="hidden" name="item_number" value="1"> | |
| 101 | + | <input type="hidden" name="amount" value="<?= $znxPayH($price) ?>"> | |
| 102 | + | <input type="hidden" name="no_shipping" value="1"> | |
| 103 | + | <input type="hidden" name="no_note" value="1"> | |
| 104 | + | <input type="hidden" name="currency_code" value="<?= $znxPayH($paypal['currency']) ?>"> | |
| 105 | + | <input type="hidden" name="lc" value="GB"> | |
| 106 | + | <input type="hidden" name="bn" value="PP-BuyNowBF"> | |
| 107 | + | <input type="hidden" name="return" value="<?= $znxPayH($paypal['success']) ?>"> | |
| 108 | + | <input type="hidden" name="cancel_return" value="<?= $znxPayH($paypal['failed']) ?>"> | |
| 109 | + | <input type="hidden" name="rm" value="2"> | |
| 110 | + | <input type="hidden" name="notify_url" value="<?= $znxPayH($paypal['ipn']) ?>"> | |
| 111 | + | <input type="hidden" name="custom" value="<?= (int)$session_user_id ?>"> | |
| 112 | + | <button type="submit" class="znx-pay-btn"><?= $znxPayH(t('buypoints.purchase')) ?></button> | |
| 113 | + | </form> | |
| 114 | + | <?php else: ?> | |
| 115 | + | <form action="payment.php" method="post"> | |
| 116 | + | <?php Token::create(); ?> | |
| 117 | + | <input type="hidden" name="provider" value="<?= $znxPayH($provider) ?>"> | |
| 118 | + | <input type="hidden" name="price" value="<?= $znxPayH($price) ?>"> | |
| 119 | + | <button type="submit" class="znx-pay-btn"><?= $znxPayH(t('buypoints.purchase')) ?></button> | |
| 120 | + | </form> | |
| 121 | + | <?php endif; ?> | |
| 122 | + | </td> | |
| 123 | + | </tr> | |
| 124 | + | <?php endforeach; ?> | |
| 125 | + | </tbody> | |
| 126 | + | </table> | |
| 127 | + | </div> | |
| 128 | + | </section> | |
| 129 | + | <?php $firstTable = false; ?> | |
| 130 | + | <?php endforeach; ?> | |
| 131 | + | ||
| 132 | + | <?php if ($hasPagseguro): $pagseguroCfg = $config['pagseguro']; ?> | |
| 133 | + | <section class="znx-pay-section"> | |
| 134 | + | <div class="znx-pay-section__head"><h2><?= $znxPayH(t('buypoints.pagseguro')) ?></h2><span><?= $znxPayH($pagseguroCfg['currency']) ?></span></div> | |
| 135 | + | <div class="znx-pay-provider-form"> | |
| 136 | + | <form target="pagseguro" action="https://<?= $znxPayH($pagseguroCfg['urls']['www']) ?>/checkout/checkout.jhtml" method="post"> | |
| 137 | + | <input type="hidden" name="email_cobranca" value="<?= $znxPayH($pagseguroCfg['email']) ?>"> | |
| 138 | + | <input type="hidden" name="tipo" value="CP"> | |
| 139 | + | <input type="hidden" name="moeda" value="<?= $znxPayH($pagseguroCfg['currency']) ?>"> | |
| 140 | + | <input type="hidden" name="ref_transacao" value="<?= (int)$session_user_id ?>"> | |
| 141 | + | <input type="hidden" name="item_id_1" value="1"> | |
| 142 | + | <input type="hidden" name="item_descr_1" value="<?= $znxPayH($pagseguroCfg['product_name']) ?>"> | |
| 143 | + | <input type="number" name="item_quant_1" min="1" step="4" value="1"> | |
| 144 | + | <input type="hidden" name="item_peso_1" value="0"> | |
| 145 | + | <input type="hidden" name="item_valor_1" value="<?= $znxPayH($pagseguroCfg['price']) ?>"> | |
| 146 | + | <button type="submit" class="znx-pay-btn"><?= $znxPayH(t('buypoints.purchase')) ?></button> | |
| 147 | + | </form> | |
| 148 | + | </div> | |
| 149 | + | </section> | |
| 150 | + | <?php endif; ?> | |
| 151 | + | ||
| 152 | + | <?php if ($hasPaygol): $paygolCfg = $config['paygol']; ?> | |
| 153 | + | <section class="znx-pay-section"> | |
| 154 | + | <div class="znx-pay-section__head"><h2><?= $znxPayH(t('buypoints.paygol')) ?></h2><span><?= $znxPayH($paygolCfg['currency']) ?></span></div> | |
| 155 | + | <div class="znx-pay-provider-form"> | |
| 156 | + | <p><?= t('buypoints.paygol_line', array('price' => $paygolCfg['price'], 'currency' => $znxPayH($paygolCfg['currency']), 'points' => $paygolCfg['points'])) ?></p> | |
| 157 | + | <form name="pg_frm" method="post" action="http://www.paygol.com/micropayment/paynow"> | |
| 158 | + | <input type="hidden" name="pg_serviceid" value="<?= $znxPayH($paygolCfg['serviceID']) ?>"> | |
| 159 | + | <input type="hidden" name="pg_currency" value="<?= $znxPayH($paygolCfg['currency']) ?>"> | |
| 160 | + | <input type="hidden" name="pg_name" value="<?= $znxPayH($paygolCfg['name']) ?>"> | |
| 161 | + | <input type="hidden" name="pg_custom" value="<?= $znxPayH($session_user_id) ?>"> | |
| 162 | + | <input type="hidden" name="pg_price" value="<?= $znxPayH($paygolCfg['price']) ?>"> | |
| 163 | + | <input type="hidden" name="pg_return_url" value="<?= $znxPayH($paygolCfg['returnURL']) ?>"> | |
| 164 | + | <input type="hidden" name="pg_cancel_url" value="<?= $znxPayH($paygolCfg['cancelURL']) ?>"> | |
| 165 | + | <input type="image" name="pg_button" src="https://www.paygol.com/micropayment/img/buttons/150/black_en_pbm.png" border="0" alt="PayGol"> | |
| 166 | + | </form> | |
| 167 | + | </div> | |
| 168 | + | </section> | |
| 169 | + | <?php endif; ?> | |
| 170 | + | </section> |
| @@ -0,0 +1,47 @@ | |||
| 1 | + | <?php | |
| 2 | + | $znxClogH = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); | |
| 3 | + | ?> | |
| 4 | + | ||
| 5 | + | <style> | |
| 6 | + | .znx-clog{--clog-surface:var(--box-inner-bg,var(--box-bg,var(--nz-panel,var(--wc-bg-2,var(--z-panel-2,var(--s-panel,var(--primary,rgb(30,33,40))))))));--clog-surface-2:color-mix(in srgb,var(--clog-surface) 82%,#000);--clog-text:var(--font-color,var(--text,var(--nz-text,var(--wc-text,var(--z-text,var(--s-text,rgb(155,162,177)))))));--clog-muted:color-mix(in srgb,var(--clog-text) 58%,transparent);--clog-border:var(--border,var(--box-inner-border,var(--box-border,var(--nz-border-soft,var(--wc-line,var(--z-border,var(--s-border2,rgb(19,20,23))))))));--clog-accent:var(--accent,var(--link,var(--nz-accent,#d1a233)));--clog-radius:6px;margin:0 0 18px;color:var(--clog-text)} | |
| 7 | + | .znx-clog *{box-sizing:border-box} | |
| 8 | + | .znx-clog__head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 14px;border:1px solid var(--clog-border);border-radius:var(--clog-radius) var(--clog-radius) 0 0;background:var(--clog-surface);font-weight:800;color:var(--clog-accent)} | |
| 9 | + | .znx-clog__head a{color:var(--clog-accent);font-weight:700;font-size:12.5px} | |
| 10 | + | .znx-clog__body{border:1px solid var(--clog-border);border-top:0;border-radius:0 0 var(--clog-radius) var(--clog-radius);background:var(--clog-surface-2);overflow:hidden} | |
| 11 | + | .znx-clog__empty{padding:16px;text-align:center;color:var(--clog-muted)} | |
| 12 | + | .znx-clog-row{border-top:1px solid var(--clog-border)} | |
| 13 | + | .znx-clog-row:first-child{border-top:0} | |
| 14 | + | .znx-clog-row__summary{display:flex;align-items:baseline;gap:10px;padding:9px 14px;cursor:pointer;list-style:none} | |
| 15 | + | .znx-clog-row__summary::-webkit-details-marker{display:none} | |
| 16 | + | .znx-clog-row__summary:hover{background:color-mix(in srgb,var(--clog-accent) 8%,transparent)} | |
| 17 | + | .znx-clog-row__time{flex:0 0 auto;font-size:12px;color:var(--clog-muted);white-space:nowrap} | |
| 18 | + | .znx-clog-row__preview{flex:1 1 auto;min-width:0;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} | |
| 19 | + | .znx-clog-row[open] .znx-clog-row__preview{display:none} | |
| 20 | + | .znx-clog-row__toggle{flex:0 0 auto;margin-left:auto;width:16px;height:16px;border-radius:4px;border:1px solid var(--clog-accent);color:var(--clog-accent);font-size:12px;line-height:14px;text-align:center;font-weight:800} | |
| 21 | + | .znx-clog-row__toggle::before{content:"+"} | |
| 22 | + | .znx-clog-row[open] .znx-clog-row__toggle::before{content:"\2212"} | |
| 23 | + | .znx-clog-row__full{padding:0 14px 12px;color:var(--clog-text);font-size:13px;line-height:1.5} | |
| 24 | + | </style> | |
| 25 | + | ||
| 26 | + | <div class="znx-clog"> | |
| 27 | + | <div class="znx-clog__head"> | |
| 28 | + | <span><?= $znxClogH(t('news.changelog_ticker')) ?></span> | |
| 29 | + | <a href="changelog.php"><?= $znxClogH(t('news.changelog_full')) ?></a> | |
| 30 | + | </div> | |
| 31 | + | <div class="znx-clog__body"> | |
| 32 | + | <?php if (!empty($changelogs) && $changelogs !== false): ?> | |
| 33 | + | <?php for ($i = 0; $i < count($changelogs) && $i < 5; $i++): ?> | |
| 34 | + | <details class="znx-clog-row"> | |
| 35 | + | <summary class="znx-clog-row__summary"> | |
| 36 | + | <span class="znx-clog-row__time"><?= $znxClogH(getClock($changelogs[$i]['time'] ?? 0, true, true)) ?></span> | |
| 37 | + | <span class="znx-clog-row__preview"><?= $changelogs[$i]['text'] ?? '' ?></span> | |
| 38 | + | <span class="znx-clog-row__toggle" aria-hidden="true"></span> | |
| 39 | + | </summary> | |
| 40 | + | <div class="znx-clog-row__full"><?= $changelogs[$i]['text'] ?? '' ?></div> | |
| 41 | + | </details> | |
| 42 | + | <?php endfor; ?> | |
| 43 | + | <?php else: ?> | |
| 44 | + | <div class="znx-clog__empty"><?= $znxClogH(t('news.no_changelogs')) ?></div> | |
| 45 | + | <?php endif; ?> | |
| 46 | + | </div> | |
| 47 | + | </div> |
| @@ -0,0 +1,185 @@ | |||
| 1 | + | <?php | |
| 2 | + | $znxGalH = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); | |
| 3 | + | $znxGalDesc = static function (?string $desc): string { | |
| 4 | + | $desc = str_replace('\\r', '', (string)$desc); | |
| 5 | + | return str_replace('\\n', '<br>', $desc); | |
| 6 | + | }; | |
| 7 | + | ?> | |
| 8 | + | ||
| 9 | + | <style> | |
| 10 | + | .znx-gal{--gal-surface:var(--box-inner-bg,var(--box-bg,var(--nz-panel,var(--wc-bg-2,var(--z-panel-2,var(--s-panel,var(--primary,rgb(30,33,40))))))));--gal-surface-2:color-mix(in srgb,var(--gal-surface) 82%,#000);--gal-well:color-mix(in srgb,var(--gal-surface) 65%,#000);--gal-text:var(--font-color,var(--text,var(--nz-text,var(--wc-text,var(--z-text,var(--s-text,rgb(155,162,177)))))));--gal-muted:color-mix(in srgb,var(--gal-text) 58%,transparent);--gal-border:var(--border,var(--box-inner-border,var(--box-border,var(--nz-border-soft,var(--wc-line,var(--z-border,var(--s-border2,rgb(19,20,23))))))));--gal-accent:var(--accent,var(--link,var(--nz-accent,#d1a233)));--gal-radius:6px;margin:0 0 18px;color:var(--gal-text)} | |
| 11 | + | .znx-gal *{box-sizing:border-box} | |
| 12 | + | .znx-gal__head{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin:0 0 12px;padding:14px 16px;border:1px solid var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-surface)} | |
| 13 | + | .znx-gal__head h1{margin:0 0 5px;font-size:24px;line-height:1.1;color:var(--gal-accent)} | |
| 14 | + | .znx-gal__head p{margin:0;color:var(--gal-muted);font-size:13px} | |
| 15 | + | .znx-gal__head form{margin:0} | |
| 16 | + | .znx-gal-btn{display:inline-flex;align-items:center;justify-content:center;min-height:36px;padding:0 16px;border:1px solid var(--gal-accent);border-radius:var(--gal-radius);background:var(--gal-accent);color:#1a1510;font-weight:800;font-size:13px;cursor:pointer} | |
| 17 | + | .znx-gal-btn:hover{filter:brightness(1.1)} | |
| 18 | + | .znx-gal-empty{padding:30px 16px;text-align:center;color:var(--gal-muted);border:1px dashed var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-surface-2)} | |
| 19 | + | .znx-gal-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));align-items:stretch;gap:14px} | |
| 20 | + | .znx-gal-card{display:flex;height:100%;flex-direction:column;overflow:hidden;border:1px solid var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-surface-2)} | |
| 21 | + | .znx-gal-card__media{display:block;aspect-ratio:16/10;overflow:hidden;background:var(--gal-well)} | |
| 22 | + | .znx-gal-card__media img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .2s ease} | |
| 23 | + | .znx-gal-card__media:hover img{transform:scale(1.03)} | |
| 24 | + | .znx-gal-card__body{padding:12px 14px 14px} | |
| 25 | + | .znx-gal-card__title{margin:0 0 6px;font-size:14.5px;font-weight:800;color:var(--gal-text)} | |
| 26 | + | .znx-gal-card__desc{margin:0;font-size:12.5px;line-height:1.5;color:var(--gal-muted)} | |
| 27 | + | .znx-gal-panel{max-width:560px;margin:0 0 18px;padding:18px 20px;border:1px solid var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-surface)} | |
| 28 | + | .znx-gal-panel h1{margin:0 0 4px;font-size:20px;color:var(--gal-accent)} | |
| 29 | + | .znx-gal-panel > p{margin:0 0 16px;color:var(--gal-muted);font-size:13px} | |
| 30 | + | .znx-gal-field{margin:0 0 14px} | |
| 31 | + | .znx-gal-field label{display:block;margin:0 0 6px;font-size:12.5px;font-weight:700;color:var(--gal-muted)} | |
| 32 | + | .znx-gal-field input[type=file],.znx-gal-field input[type=text],.znx-gal-field textarea{width:100%;padding:8px 10px;border:1px solid var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-well);color:var(--gal-text);font:inherit} | |
| 33 | + | .znx-gal-field textarea{resize:vertical} | |
| 34 | + | .znx-gal-notice{max-width:560px;margin:0 0 18px;padding:16px 18px;border:1px solid var(--gal-border);border-radius:var(--gal-radius);background:var(--gal-surface)} | |
| 35 | + | .znx-gal-notice h1{margin:0 0 6px;font-size:19px;color:var(--gal-accent)} | |
| 36 | + | .znx-gal-notice p{margin:0;color:var(--gal-muted);font-size:13px} | |
| 37 | + | .znx-gal-preview{max-width:420px;margin:16px 0 0} | |
| 38 | + | .znx-gal-preview h2{margin:0 0 10px;font-size:13px;text-transform:uppercase;letter-spacing:.05em;color:var(--gal-muted)} | |
| 39 | + | </style> | |
| 40 | + | ||
| 41 | + | <?php if ($logged_in === true && !empty($_POST['new'])): ?> | |
| 42 | + | ||
| 43 | + | <section class="znx-gal"> | |
| 44 | + | <div class="znx-gal-panel"> | |
| 45 | + | <h1><?= $znxGalH(t('gallery.create')) ?></h1> | |
| 46 | + | <p><?= $znxGalH(t('gallery.powered')) ?></p> | |
| 47 | + | <form action="" method="post" enctype="multipart/form-data"> | |
| 48 | + | <div class="znx-gal-field"> | |
| 49 | + | <label for="imagefile"><?= $znxGalH(t('gallery.select')) ?></label> | |
| 50 | + | <input type="file" name="imagefile" id="imagefile"> | |
| 51 | + | </div> | |
| 52 | + | <div class="znx-gal-field"> | |
| 53 | + | <label for="galleryTitle"><?= $znxGalH(t('gallery.image_title')) ?></label> | |
| 54 | + | <input type="text" name="title" id="galleryTitle" size="70"> | |
| 55 | + | </div> | |
| 56 | + | <div class="znx-gal-field"> | |
| 57 | + | <label for="galleryDesc"><?= $znxGalH(t('gallery.image_desc')) ?></label> | |
| 58 | + | <textarea name="desc" id="galleryDesc" cols="55" rows="8"></textarea> | |
| 59 | + | </div> | |
| 60 | + | <button type="submit" class="znx-gal-btn" name="submit"><?= $znxGalH(t('gallery.upload')) ?></button> | |
| 61 | + | </form> | |
| 62 | + | </div> | |
| 63 | + | </section> | |
| 64 | + | ||
| 65 | + | <?php endif; ?> | |
| 66 | + | ||
| 67 | + | <?php if ($logged_in === true && isset($_FILES['imagefile']) && !empty($_FILES['imagefile'])): ?> | |
| 68 | + | <?php | |
| 69 | + | $upload = $_FILES['imagefile']; | |
| 70 | + | if (($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK | |
| 71 | + | || !is_uploaded_file((string)$upload['tmp_name']) | |
| 72 | + | || (int)$upload['size'] > 5 * 1024 * 1024 | |
| 73 | + | || !getimagesize($upload['tmp_name']) | |
| 74 | + | ): ?> | |
| 75 | + | <section class="znx-gal"> | |
| 76 | + | <div class="znx-gal-notice"> | |
| 77 | + | <h1><?= $znxGalH(t('gallery.failed')) ?></h1> | |
| 78 | + | <p><?= $znxGalH(t('gallery.failed_text')) ?></p> | |
| 79 | + | </div> | |
| 80 | + | </section> | |
| 81 | + | <?php else: | |
| 82 | + | $image = file_get_contents($upload['tmp_name']); | |
| 83 | + | $imgurClientID = $config['gallery']['Client ID']; | |
| 84 | + | ||
| 85 | + | $ch = curl_init(); | |
| 86 | + | curl_setopt($ch, CURLOPT_URL, 'https://api.imgur.com/3/image/'); | |
| 87 | + | curl_setopt($ch, CURLOPT_HEADER, false); | |
| 88 | + | curl_setopt($ch, CURLOPT_POST, true); | |
| 89 | + | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |
| 90 | + | curl_setopt($ch, CURLOPT_POSTFIELDS, array( | |
| 91 | + | 'type' => 'file', | |
| 92 | + | 'name' => $upload['name'], | |
| 93 | + | 'image' => $image, | |
| 94 | + | )); | |
| 95 | + | curl_setopt($ch, CURLOPT_HTTPHEADER, array( | |
| 96 | + | "Authorization: Client-ID {$imgurClientID}", | |
| 97 | + | )); | |
| 98 | + | $response = json_decode(curl_exec($ch)); | |
| 99 | + | $image_url = $response->data->link ?? false; | |
| 100 | + | $image_delete = $response->data->deletehash ?? ''; | |
| 101 | + | $title = $_POST['title'] ?? ''; | |
| 102 | + | $desc = $_POST['desc'] ?? ''; | |
| 103 | + | ||
| 104 | + | if ($image_url !== false): | |
| 105 | + | $inserted = insertImage((int)$session_user_id, $title, $desc, $image_url, $image_delete); | |
| 106 | + | if ($inserted === true): | |
| 107 | + | $safeTitle = $znxGalH($title); | |
| 108 | + | ?> | |
| 109 | + | <section class="znx-gal"> | |
| 110 | + | <div class="znx-gal-notice"> | |
| 111 | + | <h1><?= $znxGalH(t('gallery.posted')) ?></h1> | |
| 112 | + | <p><?= $znxGalH(t('gallery.posted_text')) ?></p> | |
| 113 | + | <div class="znx-gal-preview"> | |
| 114 | + | <h2><?= $znxGalH(t('gallery.preview')) ?></h2> | |
| 115 | + | <div class="znx-gal-card"> | |
| 116 | + | <a class="znx-gal-card__media" href="<?= $znxGalH($image_url) ?>" target="_blank"> | |
| 117 | + | <img src="<?= $znxGalH($image_url) ?>" alt="<?= $safeTitle ?>"> | |
| 118 | + | </a> | |
| 119 | + | <div class="znx-gal-card__body"> | |
| 120 | + | <h3 class="znx-gal-card__title"><?= $safeTitle ?></h3> | |
| 121 | + | <p class="znx-gal-card__desc"><?= $znxGalDesc($znxGalH($desc)) ?></p> | |
| 122 | + | </div> | |
| 123 | + | </div> | |
| 124 | + | </div> | |
| 125 | + | </div> | |
| 126 | + | </section> | |
| 127 | + | <?php | |
| 128 | + | else: ?> | |
| 129 | + | <section class="znx-gal"> | |
| 130 | + | <div class="znx-gal-notice"> | |
| 131 | + | <h1><?= $znxGalH(t('gallery.exists')) ?></h1> | |
| 132 | + | <p><?= $znxGalH(t('gallery.exists_text')) ?></p> | |
| 133 | + | </div> | |
| 134 | + | </section> | |
| 135 | + | <?php endif; | |
| 136 | + | else: ?> | |
| 137 | + | <section class="znx-gal"> | |
| 138 | + | <div class="znx-gal-notice"> | |
| 139 | + | <h1><?= $znxGalH(t('gallery.failed')) ?></h1> | |
| 140 | + | <p><?= $znxGalH(t('gallery.failed_text')) ?></p> | |
| 141 | + | </div> | |
| 142 | + | </section> | |
| 143 | + | <?php endif; | |
| 144 | + | endif; ?> | |
| 145 | + | <?php endif; ?> | |
| 146 | + | ||
| 147 | + | <?php if (empty($_POST)): ?> | |
| 148 | + | ||
| 149 | + | <section class="znx-gal"> | |
| 150 | + | <header class="znx-gal__head"> | |
| 151 | + | <div> | |
| 152 | + | <h1><?= $znxGalH(t('gallery.title')) ?></h1> | |
| 153 | + | <?php if ($logged_in === true): ?><p><?= $znxGalH(t('gallery.invite')) ?></p><?php endif; ?> | |
| 154 | + | </div> | |
| 155 | + | <?php if ($logged_in === true): ?> | |
| 156 | + | <form action="" method="post"> | |
| 157 | + | <button type="submit" class="znx-gal-btn" name="new" value="1"><?= $znxGalH(t('gallery.add')) ?></button> | |
| 158 | + | </form> | |
| 159 | + | <?php endif; ?> | |
| 160 | + | </header> | |
| 161 | + | ||
| 162 | + | <?php if (is_array($images) && !empty($images)): ?> | |
| 163 | + | <div class="znx-gal-grid"> | |
| 164 | + | <?php foreach ($images as $image): ?> | |
| 165 | + | <article class="znx-gal-card"> | |
| 166 | + | <a class="znx-gal-card__media" href="<?= $znxGalH($image['image']) ?>" target="_blank"> | |
| 167 | + | <img src="<?= $znxGalH($image['image']) ?>" alt="<?= $znxGalH($image['title']) ?>" loading="lazy"> | |
| 168 | + | </a> | |
| 169 | + | <div class="znx-gal-card__body"> | |
| 170 | + | <h3 class="znx-gal-card__title"><?= $znxGalH($image['title']) ?></h3> | |
| 171 | + | <p class="znx-gal-card__desc"><?= $znxGalDesc($znxGalH($image['desc'])) ?></p> | |
| 172 | + | </div> | |
| 173 | + | </article> | |
| 174 | + | <?php endforeach; ?> | |
| 175 | + | </div> | |
| 176 | + | <?php else: ?> | |
| 177 | + | <div class="znx-gal-empty"><?= $znxGalH(t('gallery.empty')) ?></div> | |
| 178 | + | <?php endif; ?> | |
| 179 | + | ||
| 180 | + | <?php if ($logged_in === false): ?> | |
| 181 | + | <p style="margin-top:14px;color:var(--gal-muted,var(--color-default,#968452));"><?= $znxGalH(t('gallery.need_login')) ?></p> | |
| 182 | + | <?php endif; ?> | |
| 183 | + | </section> | |
| 184 | + | ||
| 185 | + | <?php endif; ?> |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | <?php | |
| 2 | + | $znxPageH = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); | |
| 3 | + | ||
| 4 | + | $znxCurrentPage = (int)($page ?? 0); | |
| 5 | + | $znxTotalPages = (int)($page_amount ?? 0); | |
| 6 | + | $znxBaseUrl = $baseUrl ?? 'index.php'; | |
| 7 | + | ||
| 8 | + | if ($znxTotalPages > 0): | |
| 9 | + | $znxDelta = 2; | |
| 10 | + | $znxSteps = array(); | |
| 11 | + | for ($i = 0; $i < $znxTotalPages; $i++) { | |
| 12 | + | if ($i === 0 || $i === $znxTotalPages - 1 || ($i >= $znxCurrentPage - $znxDelta && $i <= $znxCurrentPage + $znxDelta)) { | |
| 13 | + | $znxSteps[] = $i; | |
| 14 | + | } | |
| 15 | + | } | |
| 16 | + | $znxItems = array(); | |
| 17 | + | $znxLast = null; | |
| 18 | + | foreach ($znxSteps as $i) { | |
| 19 | + | if ($znxLast !== null && $i - $znxLast > 1) $znxItems[] = '...'; | |
| 20 | + | $znxItems[] = $i; | |
| 21 | + | $znxLast = $i; | |
| 22 | + | } | |
| 23 | + | ?> | |
| 24 | + | <style> | |
| 25 | + | .znx-pager{--pager-surface:var(--box-inner-bg,var(--box-bg,var(--nz-panel,var(--wc-bg-2,var(--z-panel-2,var(--s-panel,var(--primary,rgb(30,33,40))))))));--pager-text:var(--font-color,var(--text,var(--nz-text,var(--wc-text,var(--z-text,var(--s-text,rgb(155,162,177)))))));--pager-muted:color-mix(in srgb,var(--pager-text) 58%,transparent);--pager-border:var(--border,var(--box-inner-border,var(--box-border,var(--nz-border-soft,var(--wc-line,var(--z-border,var(--s-border2,rgb(19,20,23))))))));--pager-accent:var(--accent,var(--link,var(--nz-accent,#d1a233)));display:flex;justify-content:center;align-items:center;gap:6px;flex-wrap:wrap;margin:22px 0 0;padding-top:18px;border-top:1px solid var(--pager-border)} | |
| 26 | + | .znx-pager a,.znx-pager__current,.znx-pager__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:34px;padding:0 8px;border-radius:6px;font-size:13px;font-weight:700;text-decoration:none} | |
| 27 | + | .znx-pager a{border:1px solid var(--pager-border);background:var(--pager-surface);color:var(--pager-text);cursor:pointer} | |
| 28 | + | .znx-pager a:hover{border-color:var(--pager-accent);color:var(--pager-accent)} | |
| 29 | + | .znx-pager__current{border:1px solid var(--pager-accent);background:var(--pager-accent);color:#1a1510;cursor:default} | |
| 30 | + | .znx-pager__ellipsis{color:var(--pager-muted)} | |
| 31 | + | </style> | |
| 32 | + | <nav class="znx-pager" aria-label="<?= $znxPageH(t_default('common.pagination', 'Pagination')) ?>"> | |
| 33 | + | <?php if ($znxCurrentPage > 0): ?> | |
| 34 | + | <a href="<?= $znxPageH($znxBaseUrl) ?>?page=<?= $znxCurrentPage - 1 ?>" aria-label="<?= $znxPageH(t('common.previous')) ?>">«</a> | |
| 35 | + | <?php endif; ?> | |
| 36 | + | <?php foreach ($znxItems as $znxItem): ?> | |
| 37 | + | <?php if ($znxItem === '...'): ?> | |
| 38 | + | <span class="znx-pager__ellipsis">…</span> | |
| 39 | + | <?php elseif ($znxItem === $znxCurrentPage): ?> | |
| 40 | + | <span class="znx-pager__current" aria-current="page"><?= (int)$znxItem + 1 ?></span> | |
| 41 | + | <?php else: ?> | |
| 42 | + | <a href="<?= $znxPageH($znxBaseUrl) ?>?page=<?= (int)$znxItem ?>"><?= (int)$znxItem + 1 ?></a> | |
| 43 | + | <?php endif; ?> | |
| 44 | + | <?php endforeach; ?> | |
| 45 | + | <?php if ($znxCurrentPage < $znxTotalPages - 1): ?> | |
| 46 | + | <a href="<?= $znxPageH($znxBaseUrl) ?>?page=<?= $znxCurrentPage + 1 ?>" aria-label="<?= $znxPageH(t('common.next')) ?>">»</a> | |
| 47 | + | <?php endif; ?> | |
| 48 | + | </nav> | |
| 49 | + | <?php endif; ?> |
| @@ -0,0 +1,197 @@ | |||
| 1 | + | <?php | |
| 2 | + | $znxShopH = static fn($v): string => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); | |
| 3 | + | ||
| 4 | + | $shopSession = ''; | |
| 5 | + | if (($loggedin ?? false) === true) { | |
| 6 | + | $shopSession = bin2hex(random_bytes(32)); | |
| 7 | + | $_SESSION['shop_session'] = $shopSession; | |
| 8 | + | } | |
| 9 | + | ||
| 10 | + | if (empty($shop['enabled'])) { | |
| 11 | + | echo '<h1>' . t('buypoints.disabled') . '</h1><p>' . t('buypoints.disabled_text') . '</p>'; | |
| 12 | + | return; | |
| 13 | + | } | |
| 14 | + | ||
| 15 | + | $outfitFallbackIds = array(136,137,138,139,140,141,142,147,148,149,150,155,156,157,158,252,269,270,279,288,324,336,366,431,433,464,466,471,513,514,542,128,129,130,131,132,133,134,143,144,145,146,151,152,153,154,251,268,273,278,289,325,335,367,430,432,463,465,472,512,516,541); | |
| 16 | + | $shopCategories = array( | |
| 17 | + | 'outfits' => array('label' => t('shop.cat_outfits'), 'items' => array()), | |
| 18 | + | 'mounts' => array('label' => t('shop.cat_mounts'), 'items' => array()), | |
| 19 | + | 'items' => array('label' => t('shop.cat_items'), 'items' => array()), | |
| 20 | + | 'premium' => array('label' => t('shop.cat_premium'), 'items' => array()), | |
| 21 | + | 'misc' => array('label' => t('shop.cat_misc'), 'items' => array()), | |
| 22 | + | ); | |
| 23 | + | ||
| 24 | + | foreach ((array)$shop_list as $offerId => $offer) { | |
| 25 | + | switch ((int)($offer['type'] ?? 0)) { | |
| 26 | + | case 1: $shopCategories['items']['items'][$offerId] = $offer; break; | |
| 27 | + | case 2: $shopCategories['premium']['items'][$offerId] = $offer; break; | |
| 28 | + | case 5: $shopCategories['outfits']['items'][$offerId] = $offer; break; | |
| 29 | + | case 6: $shopCategories['mounts']['items'][$offerId] = $offer; break; | |
| 30 | + | default: $shopCategories['misc']['items'][$offerId] = $offer; break; | |
| 31 | + | } | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | $visibleCategories = array_filter($shopCategories, static fn($category): bool => !empty($category['items'])); | |
| 35 | + | $firstCategory = true; | |
| 36 | + | ||
| 37 | + | $renderImage = static function (array $offer, string $category) use ($config, $outfitFallbackIds, $znxShopH): string { | |
| 38 | + | $type = (int)($offer['type'] ?? 0); | |
| 39 | + | $itemId = $offer['itemid'] ?? 0; | |
| 40 | + | $count = (int)($offer['count'] ?? 0); | |
| 41 | + | ||
| 42 | + | if ($category === 'outfits' && !empty($config['show_outfits']['shop'])) { | |
| 43 | + | $ids = is_array($itemId) ? array_values($itemId) : array($itemId); | |
| 44 | + | $html = ''; | |
| 45 | + | foreach (array_slice($ids, 0, 2) as $id) { | |
| 46 | + | $html .= '<img class="znx-shop-card__img is-outfit" src="' . $znxShopH($config['show_outfits']['imageServer']) . '?id=' . (int)$id . '&addons=' . $count . '&head=78&body=68&legs=58&feet=76" alt="">'; | |
| 47 | + | } | |
| 48 | + | return $html; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | if ($category === 'mounts' && !empty($config['show_outfits']['shop'])) { | |
| 52 | + | $outfitId = $outfitFallbackIds[array_rand($outfitFallbackIds)]; | |
| 53 | + | return '<img class="znx-shop-card__img is-mount" src="' . $znxShopH($config['show_outfits']['imageServer']) . '?id=' . (int)$outfitId . '&addons=3&head=78&body=68&legs=58&feet=76&mount=' . (int)$itemId . '&direction=2" alt="">'; | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | if (!empty($config['shop']['showImage']) && (int)$itemId > 0 && $type !== 3 && $type !== 4) { | |
| 57 | + | return '<img class="znx-shop-card__img" src="' . $znxShopH(znote_item_image_url((int)$itemId)) . '" alt="">'; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | $icon = $type === 2 ? 'VIP' : ($type === 3 ? 'SEX' : ($type === 4 ? 'NAME' : 'ITEM')); | |
| 61 | + | return '<span class="znx-shop-card__placeholder" aria-hidden="true">' . $icon . '</span>'; | |
| 62 | + | }; | |
| 63 | + | ||
| 64 | + | $offerMeta = static function (array $offer, string $category) use ($znxShopH): string { | |
| 65 | + | $count = (int)($offer['count'] ?? 0); | |
| 66 | + | if ($category === 'premium') return $znxShopH(t('shop.days', array('count' => $count))); | |
| 67 | + | if ($category === 'items' && $count > 1) return $count . 'x'; | |
| 68 | + | if ($category === 'misc') return $count === 0 ? t('common.unlimited') : $count . 'x'; | |
| 69 | + | if ($category === 'outfits' && $count > 0) return 'Addons ' . $count; | |
| 70 | + | return ''; | |
| 71 | + | }; | |
| 72 | + | ?> | |
| 73 | + | ||
| 74 | + | <style> | |
| 75 | + | .znx-shop-modern{--shop-surface:var(--box-inner-bg,var(--box-bg,var(--nz-panel,var(--wc-bg-2,var(--z-panel-2,var(--s-panel,var(--primary,rgb(30,33,40))))))));--shop-surface-2:color-mix(in srgb,var(--shop-surface) 82%,#000);--shop-well:color-mix(in srgb,var(--shop-surface) 65%,#000);--shop-text:var(--font-color,var(--text,var(--nz-text,var(--wc-text,var(--z-text,var(--s-text,rgb(155,162,177)))))));--shop-muted:color-mix(in srgb,var(--shop-text) 58%,transparent);--shop-border:var(--border,var(--box-inner-border,var(--box-border,var(--nz-border-soft,var(--wc-line,var(--z-border,var(--s-border2,rgb(19,20,23))))))));--shop-accent:var(--accent,var(--link,var(--nz-accent,#d1a233)));--shop-radius:6px;margin:0 0 18px;color:var(--shop-text)} | |
| 76 | + | .znx-shop-modern *{box-sizing:border-box} | |
| 77 | + | .znx-shop-hero{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin:0 0 12px;padding:14px 16px;border:1px solid var(--shop-border);border-radius:var(--shop-radius);background:var(--shop-surface);box-shadow:inset 0 1px 0 rgba(255,255,255,.03)} | |
| 78 | + | .znx-shop-hero h1{margin:0 0 5px;font-size:26px;line-height:1.05;color:var(--shop-accent)} | |
| 79 | + | .znx-shop-hero p{margin:0;color:var(--shop-muted)} | |
| 80 | + | .znx-shop-hero__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:9px} | |
| 81 | + | .znx-shop-hero__link{display:inline-flex;align-items:center;padding:6px 13px;border:1px solid var(--shop-accent);border-radius:999px;color:var(--shop-accent);font-weight:700;font-size:12px;text-decoration:none;white-space:nowrap} | |
| 82 | + | .znx-shop-hero__link:hover{background:var(--shop-accent);color:#1a1510} | |
| 83 | + | .znx-shop-balance{min-width:135px;padding:9px 12px;border:1px solid var(--shop-accent);border-radius:var(--shop-radius);background:var(--shop-well);color:var(--shop-muted);text-align:center} | |
| 84 | + | .znx-shop-balance strong{display:block;font-size:22px;line-height:1;color:var(--shop-accent)} | |
| 85 | + | .znx-shop-balance a{color:var(--shop-accent)} | |
| 86 | + | .znx-shop-tabs{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px;padding:7px;border:1px solid var(--shop-border);border-radius:var(--shop-radius);background:var(--shop-surface)} | |
| 87 | + | .znx-shop-tab{display:inline-flex;align-items:center;justify-content:center;padding:7px 12px;border:1px solid var(--shop-border);border-radius:var(--shop-radius);background:var(--shop-well);color:var(--shop-muted);font-weight:700;cursor:pointer} | |
| 88 | + | .znx-shop-tab:hover{border-color:var(--shop-accent);color:var(--shop-accent)} | |
| 89 | + | .znx-shop-tab.is-active{background:var(--shop-accent);border-color:var(--shop-accent);color:#1a1510} | |
| 90 | + | .znx-shop-panel{display:none} | |
| 91 | + | .znx-shop-panel.is-active{display:block} | |
| 92 | + | .znx-shop-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(155px,1fr));align-items:stretch;gap:10px} | |
| 93 | + | .znx-shop-card{display:flex;min-height:230px;height:100%;flex-direction:column;position:relative;overflow:hidden;padding:10px;border:1px solid var(--shop-border);border-radius:var(--shop-radius);background:var(--shop-surface-2);box-shadow:none} | |
| 94 | + | .znx-shop-card:hover{transform:translateY(-1px);border-color:var(--shop-accent)} | |
| 95 | + | .znx-shop-card__media{display:flex;height:82px;align-items:center;justify-content:center;margin:0 0 9px;border:1px solid rgba(0,0,0,.25);border-radius:var(--shop-radius);background:var(--shop-well)} | |
| 96 | + | .znx-shop-card__img{max-width:66px;max-height:66px;object-fit:contain;image-rendering:auto;filter:drop-shadow(0 5px 8px rgba(0,0,0,.35))} | |
| 97 | + | .znx-shop-card__img.is-outfit,.znx-shop-card__img.is-mount{max-width:86px;max-height:76px} | |
| 98 | + | .znx-shop-card__placeholder{display:grid;width:54px;height:42px;place-items:center;border:1px solid var(--shop-accent);border-radius:var(--shop-radius);background:var(--shop-surface);color:var(--shop-accent);font-size:12px;font-weight:900} | |
| 99 | + | .znx-shop-card__body{display:flex;flex:1;flex-direction:column;text-align:center} | |
| 100 | + | .znx-shop-card__title{margin:0 0 6px;font-size:14px;font-weight:800;line-height:1.25;color:var(--shop-text)} | |
| 101 | + | .znx-shop-card__meta{min-height:18px;color:var(--shop-muted);font-size:12px} | |
| 102 | + | .znx-shop-card__price{display:inline-flex;align-items:center;justify-content:center;gap:5px;margin:9px auto 10px;padding:5px 9px;border:1px solid color-mix(in srgb,var(--shop-accent) 35%,transparent);border-radius:var(--shop-radius);background:var(--shop-well);color:var(--shop-accent);font-size:12px;font-weight:900} | |
| 103 | + | .znx-shop-card__price::before{content:"";display:inline-block;width:5px;height:5px;border-radius:50%;background:var(--shop-accent)} | |
| 104 | + | .znx-shop-card form{margin:auto 0 0} | |
| 105 | + | .znx-shop-buy{width:100%;min-height:34px;border:1px solid var(--shop-accent);border-radius:var(--shop-radius);background:var(--shop-accent);color:#1a1510;font-size:12px;font-weight:900;cursor:pointer;box-shadow:none} | |
| 106 | + | .znx-shop-buy:hover{filter:brightness(1.1)} | |
| 107 | + | .znx-shop-login{margin:auto 0 0;padding:8px;border-radius:var(--shop-radius);background:var(--shop-surface);color:var(--shop-muted);font-weight:700;text-align:center} | |
| 108 | + | @media(max-width:680px){.znx-shop-hero{display:block}.znx-shop-balance{margin-top:10px}.znx-shop-tab{flex:1}.znx-shop-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}} | |
| 109 | + | </style> | |
| 110 | + | ||
| 111 | + | <section class="znx-shop-modern shop" id="shop"> | |
| 112 | + | <header class="znx-shop-hero"> | |
| 113 | + | <div> | |
| 114 | + | <h1><?= $znxShopH(t('shop.offers')) ?></h1> | |
| 115 | + | <?php if (($loggedin ?? false) === true): ?> | |
| 116 | + | <p><?= $znxShopH(t('shop.you_have')) ?> <strong><?= (int)$user_znote_data['points'] ?></strong> <?= $znxShopH(t('common.points')) ?>.</p> | |
| 117 | + | <div class="znx-shop-hero__actions"> | |
| 118 | + | <a href="buypoints.php" class="znx-shop-hero__link"><?= $znxShopH(t('shop.buy_points')) ?></a> | |
| 119 | + | <?php if (!empty($config['shop_auction']['characterAuction'])): ?> | |
| 120 | + | <a href="auctionChar.php" class="znx-shop-hero__link"><?= $znxShopH(t_default('shop.auction_button', 'Character Auctions')) ?></a> | |
| 121 | + | <?php endif; ?> | |
| 122 | + | </div> | |
| 123 | + | <?php else: ?> | |
| 124 | + | <p><?= $znxShopH(t('shop.need_login')) ?></p> | |
| 125 | + | <?php endif; ?> | |
| 126 | + | </div> | |
| 127 | + | <?php if (($loggedin ?? false) === true): ?> | |
| 128 | + | <div class="znx-shop-balance"><span><?= $znxShopH(t('common.points')) ?></span><strong><?= number_format((int)$user_znote_data['points']) ?></strong></div> | |
| 129 | + | <?php endif; ?> | |
| 130 | + | </header> | |
| 131 | + | ||
| 132 | + | <?php if (!$visibleCategories): ?> | |
| 133 | + | <p><?= $znxShopH(t('shop.offers')) ?>: 0</p> | |
| 134 | + | <?php else: ?> | |
| 135 | + | <nav class="znx-shop-tabs" aria-label="<?= $znxShopH(t('shop.offers')) ?>"> | |
| 136 | + | <?php foreach ($visibleCategories as $key => $category): ?> | |
| 137 | + | <button type="button" class="znx-shop-tab<?= $firstCategory ? ' is-active' : '' ?>" data-shop-tab="<?= $znxShopH($key) ?>"><?= $znxShopH($category['label']) ?></button> | |
| 138 | + | <?php $firstCategory = false; ?> | |
| 139 | + | <?php endforeach; ?> | |
| 140 | + | </nav> | |
| 141 | + | ||
| 142 | + | <?php $firstCategory = true; ?> | |
| 143 | + | <?php foreach ($visibleCategories as $categoryKey => $category): ?> | |
| 144 | + | <section class="znx-shop-panel<?= $firstCategory ? ' is-active' : '' ?>" data-shop-panel="<?= $znxShopH($categoryKey) ?>"> | |
| 145 | + | <div class="znx-shop-grid"> | |
| 146 | + | <?php foreach ($category['items'] as $offerId => $offer): ?> | |
| 147 | + | <?php | |
| 148 | + | $titlePlain = trim(strip_tags((string)($offer['description'] ?? ''))); | |
| 149 | + | $meta = $offerMeta($offer, $categoryKey); | |
| 150 | + | ?> | |
| 151 | + | <article class="znx-shop-card shop-offer-card"> | |
| 152 | + | <div class="znx-shop-card__media"><?= $renderImage($offer, $categoryKey) ?></div> | |
| 153 | + | <div class="znx-shop-card__body"> | |
| 154 | + | <h3 class="znx-shop-card__title"><?= (string)($offer['description'] ?? '') ?></h3> | |
| 155 | + | <div class="znx-shop-card__meta"><?= $meta !== '' ? $znxShopH($meta) : ' ' ?></div> | |
| 156 | + | <div class="znx-shop-card__price shop-offer-price"><?= (int)$offer['points'] ?> <?= $znxShopH(t('common.points')) ?></div> | |
| 157 | + | <?php if (($loggedin ?? false) === true): ?> | |
| 158 | + | <form action="" method="POST"> | |
| 159 | + | <input type="hidden" name="buy" value="<?= (int)$offerId ?>"> | |
| 160 | + | <input type="hidden" name="session" value="<?= $znxShopH($shopSession) ?>"> | |
| 161 | + | <button type="submit" class="znx-shop-buy needconfirmation" data-item-name="<?= $znxShopH($titlePlain) ?>" data-item-cost="<?= (int)$offer['points'] ?>"><?= $znxShopH(t('shop.purchase')) ?></button> | |
| 162 | + | </form> | |
| 163 | + | <?php else: ?> | |
| 164 | + | <div class="znx-shop-login"><?= $znxShopH(t('shop.need_login')) ?></div> | |
| 165 | + | <?php endif; ?> | |
| 166 | + | </div> | |
| 167 | + | </article> | |
| 168 | + | <?php endforeach; ?> | |
| 169 | + | </div> | |
| 170 | + | </section> | |
| 171 | + | <?php $firstCategory = false; ?> | |
| 172 | + | <?php endforeach; ?> | |
| 173 | + | <?php endif; ?> | |
| 174 | + | </section> | |
| 175 | + | ||
| 176 | + | <script> | |
| 177 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 178 | + | document.querySelectorAll('.znx-shop-tab').forEach(function (tab) { | |
| 179 | + | tab.addEventListener('click', function () { | |
| 180 | + | var code = tab.getAttribute('data-shop-tab'); | |
| 181 | + | document.querySelectorAll('.znx-shop-tab').forEach(function (t) { t.classList.toggle('is-active', t === tab); }); | |
| 182 | + | document.querySelectorAll('.znx-shop-panel').forEach(function (panel) { panel.classList.toggle('is-active', panel.getAttribute('data-shop-panel') === code); }); | |
| 183 | + | }); | |
| 184 | + | }); | |
| 185 | + | <?php if (!empty($shop['enableShopConfirmation'])): ?> | |
| 186 | + | document.querySelectorAll('.needconfirmation').forEach(function (button) { | |
| 187 | + | button.addEventListener('click', function (event) { | |
| 188 | + | var itemName = this.getAttribute('data-item-name') || 'this offer'; | |
| 189 | + | var itemCost = this.getAttribute('data-item-cost') || '0'; | |
| 190 | + | if (!confirm('Do you really want to purchase ' + itemName + ' for ' + itemCost + ' points?')) { | |
| 191 | + | event.preventDefault(); | |
| 192 | + | } | |
| 193 | + | }); | |
| 194 | + | }); | |
| 195 | + | <?php endif; ?> | |
| 196 | + | }); | |
| 197 | + | </script> |
| @@ -0,0 +1,271 @@ | |||
| 1 | + | <?php | |
| 2 | + | $follow = array( | |
| 3 | + | "twitter" => "https://www.twitter.com/", | |
| 4 | + | "facebook" => "https://www.facebook.com/", | |
| 5 | + | "youtube" => "https://www.youtube.com/", | |
| 6 | + | "twitch" => "https://www.twitch.tv/" | |
| 7 | + | ); | |
| 8 | + | $url_param = "/?subtopic="; | |
| 9 | + | ||
| 10 | + | /* | |
| 11 | + | isNew -- determines wether the page is new or not, set to _true_ if it is a new page | |
| 12 | + | ||
| 13 | + | isPage -- if the menu item is not a page but a link to a file to be downloaded, set to _false_ | |
| 14 | + | ||
| 15 | + | */ | |
| 16 | + | $menu_items = array( | |
| 17 | + | "Main" => array( | |
| 18 | + | "fontIcon" => "home", | |
| 19 | + | "Latest News" => array("latestnews", "isNew" => false, "isPage" => true), | |
| 20 | + | "News Archive" => array("newsarchive", "isNew" => false, "isPage" => true), | |
| 21 | + | "Report Bug(s)" => array("reportbug", "isNew" => true, "isPage" => true) | |
| 22 | + | ), | |
| 23 | + | "Account" => array( | |
| 24 | + | "fontIcon" => "user-circle", | |
| 25 | + | "My Account" => array("accountmanagement", "isNew" => false, "isPage" => true), | |
| 26 | + | "Create Account" => array("createaccount", "isNew" => false, "isPage" => true), | |
| 27 | + | "Downloads" => array("downloads", "isNew" => false, "isPage" => true), | |
| 28 | + | "Recover Password" => array("loastaccount", "isNew" => true, "isPage" => true) | |
| 29 | + | ), | |
| 30 | + | "Community" => array( | |
| 31 | + | "fontIcon" => "users", | |
| 32 | + | "Characters" => array("characters", "isNew" => false, "isPage" => true), | |
| 33 | + | "Who is online" => array("whoisonline", "isNew" => false, "isPage" => true), | |
| 34 | + | "Highscores" => array("highscores", "isNew" => false, "isPage" => true), | |
| 35 | + | "Houses" => array("houses", "isNew" => false, "isPage" => true), | |
| 36 | + | "Latest Kills" => array("killstatistics", "isNew" => false, "isPage" => true), | |
| 37 | + | "Guilds" => array("guilds", "isNew" => false, "isPage" => true) | |
| 38 | + | ), | |
| 39 | + | "Library" => array( | |
| 40 | + | "fontIcon" => "book", | |
| 41 | + | "Server Rules" => array("tibiarules", "isNew" => false, "isPage" => true), | |
| 42 | + | "Server Info" => array("serverinfo", "isNew" => false, "isPage" => true), | |
| 43 | + | "Exp Table" => array("experiencetable", "isNew" => true, "isPage" => true) | |
| 44 | + | ), | |
| 45 | + | "Support" => array( | |
| 46 | + | "fontIcon" => "info-circle", | |
| 47 | + | "Team" => array("team", "isNew" => false, "isPage" => true), | |
| 48 | + | "testDLFILE" => array("file.txt", "isNew" => true, "isPage" => false) | |
| 49 | + | ), | |
| 50 | + | "Shop" => array( | |
| 51 | + | "fontIcon" => "shopping-cart", | |
| 52 | + | "Donate" => array("donate", "isNew" => true, "isPage" => true), | |
| 53 | + | "Buy Points" => array("buypoints", "isNew" => false, "isPage" => true), | |
| 54 | + | "Items" => array("shopoffer", "isNew" => false, "isPage" => true) | |
| 55 | + | ) | |
| 56 | + | ); | |
| 57 | + | ||
| 58 | + | $countDown = "Apr 5, 2019 15:37:25"; | |
| 59 | + | ?> | |
| 60 | + | <!DOCTYPE html> | |
| 61 | + | <html lang="en" dir="ltr"> | |
| 62 | + | <head> | |
| 63 | + | <meta charset="utf-8"> | |
| 64 | + | <title>OTS Title | Layout</title> | |
| 65 | + | ||
| 66 | + | <!-- Stylesheet(s) --> | |
| 67 | + | <link rel="stylesheet" href="./css/style.css"> | |
| 68 | + | <link rel="stylesheet" href="./fontawesome/css/font-awesome.min.css"> | |
| 69 | + | <link rel="stylesheet" href="./css/resp.css"> | |
| 70 | + | ||
| 71 | + | <!-- JavaScript(s) --> | |
| 72 | + | <script src="./js/countdown.js" charset="utf-8"></script> | |
| 73 | + | <script type="text/javascript"> | |
| 74 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 75 | + | var timer = document.getElementById('countDownTimer'); | |
| 76 | + | if (timer) { | |
| 77 | + | countDown('countDownTimer', timer.getAttribute('data-date')); | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | document.querySelectorAll('.loginBtn').forEach(function (button) { | |
| 81 | + | button.addEventListener('click', function () { | |
| 82 | + | var container = document.querySelector('.loginContainer'); | |
| 83 | + | if (container) { | |
| 84 | + | container.style.display = 'block'; | |
| 85 | + | } | |
| 86 | + | }); | |
| 87 | + | }); | |
| 88 | + | }); | |
| 89 | + | </script> | |
| 90 | + | </head> | |
| 91 | + | <body> | |
| 92 | + | <!-- | |
| 93 | + | Author: Blackwolf (Snavy on otland) | |
| 94 | + | ||
| 95 | + | Otland: https://otland.net/members/snavy.155163/ | |
| 96 | + | Facebook: http://www.facebook.com/idont.reallywolf.1 | |
| 97 | + | Twitter: @idontreallywolf | |
| 98 | + | --> | |
| 99 | + | <!-- Main container --> | |
| 100 | + | <div class="main"> | |
| 101 | + | <nav> | |
| 102 | + | <div class="container"> | |
| 103 | + | <div class="pull-left"> | |
| 104 | + | <ul> | |
| 105 | + | <?php foreach ($menu_items as $category => $items): ?> | |
| 106 | + | <?php $icon = $items['fontIcon'] ?? 'circle'; ?> | |
| 107 | + | <li> | |
| 108 | + | <a><i class="fa fa-<?= htmlspecialchars($icon) ?>"></i> <?= htmlspecialchars($category) ?></a> | |
| 109 | + | <ul> | |
| 110 | + | <?php foreach ($items as $item => $properties): ?> | |
| 111 | + | <?php if ($item === 'fontIcon') continue; ?> | |
| 112 | + | <?php | |
| 113 | + | $target = $properties[0] ?? ''; | |
| 114 | + | $isPage = $properties['isPage'] ?? true; | |
| 115 | + | $href = $isPage ? ($url_param . $target) : $target; | |
| 116 | + | ?> | |
| 117 | + | <li> | |
| 118 | + | <a href="<?= htmlspecialchars($href) ?>"> | |
| 119 | + | <?= htmlspecialchars($item) ?> | |
| 120 | + | </a> | |
| 121 | + | </li> | |
| 122 | + | <?php endforeach; ?> | |
| 123 | + | </ul> | |
| 124 | + | </li> | |
| 125 | + | <?php endforeach; ?> | |
| 126 | + | </ul> | |
| 127 | + | </div> | |
| 128 | + | <div class="pull-right"> | |
| 129 | + | <ul> | |
| 130 | + | <li><a class="modIcon loginBtn"><i class="fa fa-lock"></i><i class="fa fa-unlock"></i> Login</a> </li> | |
| 131 | + | <li><a href="/?subtopic=createaccount"><i class="fa fa-key"></i> Register</a> </li> | |
| 132 | + | </ul> | |
| 133 | + | </div> | |
| 134 | + | </div> | |
| 135 | + | </nav> | |
| 136 | + | ||
| 137 | + | <div class="well banner"></div> | |
| 138 | + | ||
| 139 | + | <div class="well feedContainer preventCollapse"> | |
| 140 | + | ||
| 141 | + | <div class="well topPane preventCollapse"> | |
| 142 | + | <div class="well pull-left"> | |
| 143 | + | <div id="countDownTimer" data-date="<?=$countDown?>"></div> | |
| 144 | + | </div> | |
| 145 | + | ||
| 146 | + | <div class="well pull-right"> | |
| 147 | + | <form class="searchForm" action="/?subtopic=characters" method="post"> | |
| 148 | + | <input type="text" name="name" placeholder="e.g: John Sheppard"> | |
| 149 | + | </form> | |
| 150 | + | </div> | |
| 151 | + | </div> | |
| 152 | + | <!-- MAIN FEED --> | |
| 153 | + | <div class="pull-left leftPane"> | |
| 154 | + | ||
| 155 | + | ||
| 156 | + | <div class="postHolder"> | |
| 157 | + | <div class="well"> | |
| 158 | + | <div class="header"> | |
| 159 | + | test | |
| 160 | + | </div> | |
| 161 | + | <div class="body"> | |
| 162 | + | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. | |
| 163 | + | </div> | |
| 164 | + | </div> | |
| 165 | + | </div> | |
| 166 | + | ||
| 167 | + | <div class="postHolder"> | |
| 168 | + | <div class="well"> | |
| 169 | + | <div class="header"> | |
| 170 | + | test | |
| 171 | + | </div> | |
| 172 | + | <div class="body"> | |
| 173 | + | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. | |
| 174 | + | </div> | |
| 175 | + | </div> | |
| 176 | + | </div> | |
| 177 | + | ||
| 178 | + | ||
| 179 | + | </div> | |
| 180 | + | <!-- MAIN FEED END --> | |
| 181 | + | ||
| 182 | + | <!-- RIGHT PANE --> | |
| 183 | + | <div class="pull-right rightPane"> | |
| 184 | + | ||
| 185 | + | <div class="well loginContainer"> | |
| 186 | + | <div class="header"> | |
| 187 | + | Login | |
| 188 | + | </div> | |
| 189 | + | <div class="body"> | |
| 190 | + | <form class="loginForm" action="/?subtopic=accountmanagement" method="post"> | |
| 191 | + | <input type="text" name="accountLogin" placeholder="Account name"> | |
| 192 | + | <input type="password" name="accountPassword" placeholder="••••••••"> | |
| 193 | + | <button type="submit" name="submit">continue</button> | |
| 194 | + | </form> | |
| 195 | + | </div> | |
| 196 | + | </div> | |
| 197 | + | ||
| 198 | + | <div class="well"> | |
| 199 | + | <div class="header"> | |
| 200 | + | Follow Us | |
| 201 | + | </div> | |
| 202 | + | <div class="body"> | |
| 203 | + | <table class="smedia centralizeContent"> | |
| 204 | + | <tr> | |
| 205 | + | <td><a href="<?= htmlspecialchars($follow['facebook'] ?? '#') ?>" target="_blank"><i class="fa fa-facebook"></i></a></td> | |
| 206 | + | <td><a href="<?= htmlspecialchars($follow['twitter'] ?? '#') ?>" target="_blank"><i class="fa fa-twitter"></i></a></td> | |
| 207 | + | <td><a href="<?= htmlspecialchars($follow['youtube'] ?? '#') ?>" target="_blank"><i class="fa fa-youtube"></i></a></td> | |
| 208 | + | <td><a href="<?= htmlspecialchars($follow['twitch'] ?? '#') ?>" target="_blank"><i class="fa fa-twitch"></i></a></td> | |
| 209 | + | </tr> | |
| 210 | + | </table> | |
| 211 | + | ||
| 212 | + | </div> | |
| 213 | + | </div> | |
| 214 | + | ||
| 215 | + | <div class="well"> | |
| 216 | + | <div class="header"> | |
| 217 | + | Events | |
| 218 | + | </div> | |
| 219 | + | <div class="body"> | |
| 220 | + | <table> | |
| 221 | + | <tr><td>Event Name</td><td><i class="fa fa-clock-o"></i> 2h 5m 10s</td></tr> | |
| 222 | + | <tr><td>Event Name</td><td><i class="fa fa-clock-o"></i> 2h 5m 10s</td></tr> | |
| 223 | + | <tr><td>Event Name</td><td><i class="fa fa-clock-o"></i> 2h 5m 10s</td></tr> | |
| 224 | + | <tr><td>Event Name</td><td><i class="fa fa-clock-o"></i> 2h 5m 10s</td></tr> | |
| 225 | + | <tr><td>Event Name</td><td><i class="fa fa-clock-o"></i> 2h 5m 10s</td></tr> | |
| 226 | + | </table> | |
| 227 | + | </div> | |
| 228 | + | </div> | |
| 229 | + | ||
| 230 | + | <div class="well"> | |
| 231 | + | <div class="header"> | |
| 232 | + | Top 10 Players | |
| 233 | + | </div> | |
| 234 | + | <div class="body"> | |
| 235 | + | <table> | |
| 236 | + | <tr><td>#</td><td>Name</td></tr> | |
| 237 | + | <tr><td>1</td><td>Name</td></tr> | |
| 238 | + | <tr><td>2</td><td>Name</td></tr> | |
| 239 | + | <tr><td>3</td><td>Name</td></tr> | |
| 240 | + | <tr><td>4</td><td>Name</td></tr> | |
| 241 | + | <tr><td>5</td><td>Name</td></tr> | |
| 242 | + | <tr><td>6</td><td>Name</td></tr> | |
| 243 | + | <tr><td>7</td><td>Name</td></tr> | |
| 244 | + | <tr><td>8</td><td>Name</td></tr> | |
| 245 | + | <tr><td>9</td><td>Name</td></tr> | |
| 246 | + | <tr><td>10</td><td>Name</td></tr> | |
| 247 | + | </table> | |
| 248 | + | </div> | |
| 249 | + | </div> | |
| 250 | + | </div> | |
| 251 | + | <!-- RIGHT PANE END --> | |
| 252 | + | </div> | |
| 253 | + | ||
| 254 | + | <footer class="well preventCollapse"> | |
| 255 | + | <div class="pull-left"> | |
| 256 | + | Designed By <a href="https://otland.net/members/snavy.155163/" target="_blank">Snavy</a> | |
| 257 | + | </div> | |
| 258 | + | <div class="pull-right"> | |
| 259 | + | Github repository : <a href="https://github.com/idontreallywolf/ots_layouts" target="_blank">ots_layouts</a> | |
| 260 | + | </div> | |
| 261 | + | </footer> | |
| 262 | + | </div><!-- Main container END --> | |
| 263 | + | </body> | |
| 264 | + | </html> | |
| 265 | + | <!-- | |
| 266 | + | Author: Blackwolf (Snavy on otland) | |
| 267 | + | ||
| 268 | + | Otland: https://otland.net/members/snavy.155163/ | |
| 269 | + | Facebook: http://www.facebook.com/idont.reallywolf.1 | |
| 270 | + | Twitter: @idontreallywolf | |
| 271 | + | --> |
| @@ -0,0 +1,119 @@ | |||
| 1 | + | <?php | |
| 2 | + | /** | |
| 3 | + | * Default shell - the frame every page is rendered inside. | |
| 4 | + | * | |
| 5 | + | * A shell is plain HTML plus a handful of one-line calls. Everything dynamic | |
| 6 | + | * comes from a function, so you can move any block anywhere, delete what you | |
| 7 | + | * do not want, or rewrite the whole thing. | |
| 8 | + | * | |
| 9 | + | * theme_title() the site title, escaped | |
| 10 | + | * theme_body_class() "theme-default page_highscores" | |
| 11 | + | * theme_asset('x') URL of one of your files in assets/ | |
| 12 | + | * theme_content() the page itself - this one you must keep | |
| 13 | + | * theme_menu() includes this theme's menu.php (optional) | |
| 14 | + | * theme_sidebar() includes this theme's aside.php (optional) | |
| 15 | + | * widget('login') one widget from widgets/ (optional) | |
| 16 | + | */ | |
| 17 | + | ||
| 18 | + | $countDown = $countDown ?? null; | |
| 19 | + | $countDown_hide = $countDown_hide ?? 0; | |
| 20 | + | $countDown_complete = $countDown_complete ?? ''; | |
| 21 | + | ||
| 22 | + | $launch_seconds = $countDown ? (strtotime($countDown) - time()) : 0; | |
| 23 | + | $delay_hide = $launch_seconds + $countDown_hide; | |
| 24 | + | ||
| 25 | + | $aacQueries = $aacQueries ?? 0; | |
| 26 | + | ?> | |
| 27 | + | <!DOCTYPE html> | |
| 28 | + | <html lang="en" dir="ltr"> | |
| 29 | + | <head> | |
| 30 | + | <meta charset="utf-8"> | |
| 31 | + | <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| 32 | + | <title><?= theme_title() ?></title> | |
| 33 | + | ||
| 34 | + | <link rel="stylesheet" href="<?= theme_asset('css/style.css') ?>"> | |
| 35 | + | <link rel="stylesheet" href="assets/fontawesome/css/font-awesome.min.css"> | |
| 36 | + | <link rel="stylesheet" href="<?= theme_asset('css/resp.css') ?>"> | |
| 37 | + | ||
| 38 | + | <?php if ($delay_hide > 0): ?> | |
| 39 | + | <script src="<?= theme_asset('js/countdown.js') ?>" charset="utf-8"></script> | |
| 40 | + | <?php endif; ?> | |
| 41 | + | ||
| 42 | + | <script> | |
| 43 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 44 | + | <?php if ($delay_hide > 0): ?> | |
| 45 | + | countDown("countDownTimer", <?= (int)$launch_seconds ?>, <?= json_encode($countDown_complete) ?>); | |
| 46 | + | <?php endif; ?> | |
| 47 | + | ||
| 48 | + | document.querySelectorAll('.loginBtn').forEach(function (button) { | |
| 49 | + | button.addEventListener('click', function (event) { | |
| 50 | + | var firstLoginInput = document.querySelector('.loginContainer input:first-of-type'); | |
| 51 | + | if (firstLoginInput) { | |
| 52 | + | event.preventDefault(); | |
| 53 | + | firstLoginInput.focus(); | |
| 54 | + | } | |
| 55 | + | }); | |
| 56 | + | }); | |
| 57 | + | ||
| 58 | + | var accountLink = document.getElementById('accountLink'); | |
| 59 | + | if (accountLink) { | |
| 60 | + | accountLink.addEventListener('click', function (event) { | |
| 61 | + | var firstLoginInput = document.querySelector('.loginContainer input:first-of-type'); | |
| 62 | + | if (firstLoginInput && this.href.indexOf('#') >= 0) { | |
| 63 | + | event.preventDefault(); | |
| 64 | + | firstLoginInput.focus(); | |
| 65 | + | } | |
| 66 | + | }); | |
| 67 | + | } | |
| 68 | + | }); | |
| 69 | + | </script> | |
| 70 | + | </head> | |
| 71 | + | <body class="<?= theme_body_class() ?>"> | |
| 72 | + | ||
| 73 | + | <div class="main"> | |
| 74 | + | ||
| 75 | + | <?php theme_menu(); ?> | |
| 76 | + | ||
| 77 | + | <div class="well banner"></div> | |
| 78 | + | ||
| 79 | + | <div class="well feedContainer preventCollapse"> | |
| 80 | + | ||
| 81 | + | <?php if ($delay_hide > 0): ?> | |
| 82 | + | <div class="well topPane preventCollapse"> | |
| 83 | + | <div class="well pull-left"> | |
| 84 | + | <div id="countDownTimer" data-date="<?= htmlspecialchars((string)$countDown, ENT_QUOTES, 'UTF-8') ?>"></div> | |
| 85 | + | </div> | |
| 86 | + | </div> | |
| 87 | + | <?php endif; ?> | |
| 88 | + | ||
| 89 | + | <!-- MAIN FEED --> | |
| 90 | + | <div class="pull-left leftPane"> | |
| 91 | + | <?php theme_content(); ?> | |
| 92 | + | </div> | |
| 93 | + | <!-- MAIN FEED END --> | |
| 94 | + | ||
| 95 | + | <?php theme_sidebar(); ?> | |
| 96 | + | </div> | |
| 97 | + | ||
| 98 | + | <footer class="well preventCollapse"> | |
| 99 | + | <?php $footerText = theme_option('footer_html'); ?> | |
| 100 | + | <?php if ($footerText !== ''): ?> | |
| 101 | + | <div class="well preventCollapse footerCustom"><?= $footerText ?></div> | |
| 102 | + | <?php endif; ?> | |
| 103 | + | <div class="pull-left"> | |
| 104 | + | <p>© <?= theme_title() ?>. | |
| 105 | + | <?= h(t('footer.generated_in', ['time' => elapsedTime()])) ?> | |
| 106 | + | Q: <?= (int)$aacQueries ?>. | |
| 107 | + | <?= h(t('footer.designed_by')) ?> <a href="https://otland.net/members/snavy.155163/" target="_blank" rel="noopener">Snavy</a>. | |
| 108 | + | <?= h(t('footer.engine')) ?> <a href="credits.php">ZnoteX</a>. | |
| 109 | + | </p> | |
| 110 | + | </div> | |
| 111 | + | <div class="pull-right"> | |
| 112 | + | <p><?= h(t('footer.server_clock', ['clock' => getClock(false, true)])) ?></p> | |
| 113 | + | </div> | |
| 114 | + | </footer> | |
| 115 | + | ||
| 116 | + | </div><!-- .main --> | |
| 117 | + | ||
| 118 | + | </body> | |
| 119 | + | </html> |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | <?php | |
| 2 | + | /* Znote AAC Sub System | |
| 3 | + | - Used to create custom pages | |
| 4 | + | - Place the contents of the page in /layout/sub/ folder. | |
| 5 | + | : You don't need to include init, header or footer. | |
| 6 | + | Its already taken care of, just write the contents you want. | |
| 7 | + | ||
| 8 | + | Then add that page to the configuration below. Config syntax: | |
| 9 | + | 'PAGENAME' => array( | |
| 10 | + | 'file' => 'fileName.php', | |
| 11 | + | 'override' => false | |
| 12 | + | ), | |
| 13 | + | ................ | |
| 14 | + | There are 2 ways to view your page, by using sub.php file, or by overriding an existing default page. | |
| 15 | + | 1: yourwebiste.com/sub.php?page=PAGENAME | |
| 16 | + | 2: By having override => true, then it will load your sub file instead of the default znote aac file. | |
| 17 | + | ||
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | $subpages = array( | |
| 21 | + | // website.com/sub.php?page=blank | |
| 22 | + | 'blank' => array( | |
| 23 | + | // layout/sub/blank.php | |
| 24 | + | 'file' => 'blank.php', | |
| 25 | + | // false means don't run this file instead of the regular file at website.com/blank.php | |
| 26 | + | 'override' => false | |
| 27 | + | ), | |
| 28 | + | 'recover' => array( | |
| 29 | + | 'file' => 'recover.php', | |
| 30 | + | 'override' => false | |
| 31 | + | ), | |
| 32 | + | 'search' => array( | |
| 33 | + | 'file' => 'search.php', | |
| 34 | + | 'override' => false | |
| 35 | + | ), | |
| 36 | + | 'houses' => array( | |
| 37 | + | 'file' => 'houses.php', | |
| 38 | + | 'override' => false | |
| 39 | + | ), | |
| 40 | + | 'downloads' => array( | |
| 41 | + | 'file' => 'downloads.php', | |
| 42 | + | 'override' => false | |
| 43 | + | ), | |
| 44 | + | 'loginhelp' => array( | |
| 45 | + | 'file' => 'loginhelp.php', | |
| 46 | + | 'override' => false | |
| 47 | + | ) | |
| 48 | + | ); | |
| 49 | + | ?> |
| @@ -0,0 +1,110 @@ | |||
| 1 | + | <?php | |
| 2 | + | if (!empty($config['UseChangelogTicker'])) { | |
| 3 | + | theme_include('parts/changelog-ticker.php', array('changelogs' => $changelogs ?? false)); | |
| 4 | + | } | |
| 5 | + | ||
| 6 | + | $page = isset($page) && is_numeric($page) ? (int)$page : 0; | |
| 7 | + | $view = $view ?? ''; | |
| 8 | + | ||
| 9 | + | $cache = new Cache('engine/cache/news'); | |
| 10 | + | if ($cache->hasExpired()) { | |
| 11 | + | $news = fetchAllNews(); | |
| 12 | + | $cache->setContent($news); | |
| 13 | + | $cache->save(); | |
| 14 | + | } else { | |
| 15 | + | $news = $cache->load(); | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | // Design and present the list | |
| 19 | + | if ($news) { | |
| 20 | + | ||
| 21 | + | $total_news = count($news); | |
| 22 | + | $row_news = $total_news / $config['news_per_page']; | |
| 23 | + | $page_amount = ceil($total_news / $config['news_per_page']); | |
| 24 | + | $current = $config['news_per_page'] * $page; | |
| 25 | + | ||
| 26 | + | if ($view !== "") { // We want to view a specific news post | |
| 27 | + | $si = false; | |
| 28 | + | if (ctype_digit($view) === false) { | |
| 29 | + | for ($i = 0; $i < count($news); $i++) if ($view === urlencode($news[$i]['title'])) $si = $i; | |
| 30 | + | } else { | |
| 31 | + | for ($i = 0; $i < count($news); $i++) if ((int)$view === (int)$news[$i]['id']) $si = $i; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | if ($si !== false) { | |
| 35 | + | ?> | |
| 36 | + | <div class="postHolder"> | |
| 37 | + | <div class="well"> | |
| 38 | + | <div class="header"> | |
| 39 | + | <?php | |
| 40 | + | echo | |
| 41 | + | '<a href="?view=' . (int)$news[$si]['id'] . '">[#' . (int)$news[$si]['id'] . ']</a> ' | |
| 42 | + | . getClock($news[$si]['date'], true) | |
| 43 | + | . ' ' . h(t('news.by')) . ' <a href="characterprofile.php?name=' | |
| 44 | + | . urlencode($news[$si]['name']) | |
| 45 | + | . '">' | |
| 46 | + | . htmlspecialchars($news[$si]['name'], ENT_QUOTES, 'UTF-8') | |
| 47 | + | . '</a> - <b>' | |
| 48 | + | . znote_bbcode_raw($news[$si]['title']) | |
| 49 | + | . '</b>'; | |
| 50 | + | ?> | |
| 51 | + | </div> | |
| 52 | + | <div class="body"> | |
| 53 | + | <p><?php echo znote_bbcode_raw($news[$si]['text']); ?></p> | |
| 54 | + | </div> | |
| 55 | + | </div> | |
| 56 | + | </div> | |
| 57 | + | <!-- OLD DESIGN: --> | |
| 58 | + | <?php | |
| 59 | + | } else { | |
| 60 | + | ?> | |
| 61 | + | <table id="news"> | |
| 62 | + | <tr class="yellow"> | |
| 63 | + | <td class="zheadline"><?= h(t('news.not_found')) ?></td> | |
| 64 | + | </tr> | |
| 65 | + | <tr> | |
| 66 | + | <td> | |
| 67 | + | <p><?= h(t('news.not_found_text')) ?></p> | |
| 68 | + | </td> | |
| 69 | + | </tr> | |
| 70 | + | </table> | |
| 71 | + | <?php | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | } else { // We want to view latest news or a page of news. | |
| 75 | + | echo '<div class="znx-section-head">' . h(t_default('news.section_title', 'Latest News')) . '</div>'; | |
| 76 | + | for ($i = $current; $i < $current + $config['news_per_page']; $i++) { | |
| 77 | + | if (isset($news[$i])) { | |
| 78 | + | ?> | |
| 79 | + | <div class="postHolder"> | |
| 80 | + | <div class="well"> | |
| 81 | + | <div class="header"> | |
| 82 | + | <?php | |
| 83 | + | echo '<a href="?view=' . urlencode($news[$i]['title']) . '">' | |
| 84 | + | . getClock($news[$i]['date'], true) | |
| 85 | + | . '</a> ' . h(t('news.by')) . ' <a href="characterprofile.php?name=' | |
| 86 | + | . urlencode($news[$i]['name']) | |
| 87 | + | . '">' | |
| 88 | + | . htmlspecialchars($news[$i]['name'], ENT_QUOTES, 'UTF-8') | |
| 89 | + | . '</a> - <b>' | |
| 90 | + | . znote_bbcode_raw($news[$i]['title']) | |
| 91 | + | . '</b>'; | |
| 92 | + | ?> | |
| 93 | + | </div> | |
| 94 | + | <div class="body"> | |
| 95 | + | <p><?php echo znote_bbcode_raw($news[$i]['text']); ?></p> | |
| 96 | + | </div> | |
| 97 | + | </div> | |
| 98 | + | </div> | |
| 99 | + | <?php | |
| 100 | + | } | |
| 101 | + | } | |
| 102 | + | ||
| 103 | + | theme_include('parts/pagination.php', array('page' => $page, 'page_amount' => $page_amount, 'baseUrl' => 'index.php')); | |
| 104 | + | ||
| 105 | + | } | |
| 106 | + | ||
| 107 | + | } else { | |
| 108 | + | echo '<div class="znx-empty-box">' . h(t('news.none')) . '</div>'; | |
| 109 | + | } | |
| 110 | + | ?> |
| @@ -0,0 +1,22 @@ | |||
| 1 | + | <!-- Tell the user to use the aside menu --> | |
| 2 | + | <h1 class="toright"><?= h(t('login.title')) ?></h1> | |
| 3 | + | <p class="toright"><?= h(t('login.help_text')) ?> →</p> | |
| 4 | + | <style type="text/css"> | |
| 5 | + | /* Align the text to the right of the page. */ | |
| 6 | + | .toright { | |
| 7 | + | text-align: right; | |
| 8 | + | } | |
| 9 | + | div.leftPane { | |
| 10 | + | box-sizing: border-box; | |
| 11 | + | padding-right: 30px; | |
| 12 | + | } | |
| 13 | + | </style> | |
| 14 | + | <script type="text/javascript"> | |
| 15 | + | // Auto focus to login username box | |
| 16 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 17 | + | var input = document.getElementById('login_username'); | |
| 18 | + | if (input) { | |
| 19 | + | input.focus(); | |
| 20 | + | } | |
| 21 | + | }); | |
| 22 | + | </script> |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | <div class="postHolder"> | |
| 2 | + | <div class="well"> | |
| 3 | + | <div class="header"> | |
| 4 | + | <?= h(t('recovery.lost_account_title')) ?> | |
| 5 | + | </div> | |
| 6 | + | <div class="body"> | |
| 7 | + | <?= t('widget.login.lost') ?> | |
| 8 | + | </div> | |
| 9 | + | </div> | |
| 10 | + | </div> |
| @@ -0,0 +1,13 @@ | |||
| 1 | + | <div class="postHolder"> | |
| 2 | + | <div class="well"> | |
| 3 | + | <div class="header"> | |
| 4 | + | <?= h(t('widget.search.title')) ?> | |
| 5 | + | </div> | |
| 6 | + | <div class="body"> | |
| 7 | + | <form type="submit" action="characterprofile.php" method="get"> | |
| 8 | + | <input type="text" name="name" class="search"> | |
| 9 | + | <input type="submit" name="submitName" value="<?= h(t('common.search')) ?>"> | |
| 10 | + | </form> | |
| 11 | + | </div> | |
| 12 | + | </div> | |
| 13 | + | </div> |
| @@ -0,0 +1,44 @@ | |||
| 1 | + | { | |
| 2 | + | "name": "Default", | |
| 3 | + | "author": "Blackwolf (Snavy) - converted by Znote", | |
| 4 | + | "version": "2.0.0", | |
| 5 | + | "requires": { | |
| 6 | + | "znotex": ">=2.0.0 <3.0.0", | |
| 7 | + | "php": ">=8.1", | |
| 8 | + | "api": "^1.0" | |
| 9 | + | }, | |
| 10 | + | "description": "The classic Znote AAC layout. Also the fallback: any file a theme leaves out is taken from here.", | |
| 11 | + | "url": "https://otland.net/members/snavy.155163/", | |
| 12 | + | "menus": { | |
| 13 | + | "main": "Main navigation" | |
| 14 | + | }, | |
| 15 | + | "options": [ | |
| 16 | + | { | |
| 17 | + | "key": "favicon", | |
| 18 | + | "label": "Browser tab icon", | |
| 19 | + | "type": "image", | |
| 20 | + | "help": "Shown in the browser tab. Leave empty for no icon." | |
| 21 | + | }, | |
| 22 | + | { | |
| 23 | + | "key": "background_image", | |
| 24 | + | "label": "Page background image", | |
| 25 | + | "type": "image", | |
| 26 | + | "css": "body{background-image:url('%s')!important;background-size:cover;background-attachment:fixed;background-position:center top;}", | |
| 27 | + | "help": "Tiled behind the whole site. Leave empty for the theme's flat background." | |
| 28 | + | }, | |
| 29 | + | { | |
| 30 | + | "key": "logo_header", | |
| 31 | + | "label": "Header banner logo", | |
| 32 | + | "type": "image", | |
| 33 | + | "default": "layouts/default/assets/img/header.png", | |
| 34 | + | "css": ".banner{background-image:url('%s')!important;}", | |
| 35 | + | "help": "The wide banner above the content. 1000x240 or the same ratio." | |
| 36 | + | }, | |
| 37 | + | { | |
| 38 | + | "key": "footer_html", | |
| 39 | + | "label": "Footer text", | |
| 40 | + | "type": "textarea", | |
| 41 | + | "help": "Your own line in the footer, above the credits. HTML is allowed. The copyright and engine credits stay in the theme files and are not editable here." | |
| 42 | + | } | |
| 43 | + | ] | |
| 44 | + | } |
| @@ -0,0 +1,74 @@ | |||
| 1 | + | <?php | |
| 2 | + | ||
| 3 | + | if (!empty($config['Ach'])): | |
| 4 | + | ?> | |
| 5 | + | ||
| 6 | + | <center><h3><?= t('achievements.title') ?> <?php echo htmlspecialchars($config['site_title'] ?? '', ENT_QUOTES); ?></h3></center> | |
| 7 | + | ||
| 8 | + | <div class="panel-body"> | |
| 9 | + | <table class="table table-striped table-bordered table-condensed"> | |
| 10 | + | <tr> | |
| 11 | + | <td width="10%"><?= t('achievements.grade') ?></td> | |
| 12 | + | <td width="17%"><?= t('common.name') ?></td> | |
| 13 | + | <td><?= t('achievements.description') ?></td> | |
| 14 | + | <td width="7%"><?= t('achievements.secret') ?></td> | |
| 15 | + | <td width="2%"><?= t('common.points') ?></td> | |
| 16 | + | </tr> | |
| 17 | + | ||
| 18 | + | <style> | |
| 19 | + | #wtf { | |
| 20 | + | margin-left:0px; | |
| 21 | + | } | |
| 22 | + | </style> | |
| 23 | + | ||
| 24 | + | <?php | |
| 25 | + | if (!empty($config['achievements']) && is_array($config['achievements'])): | |
| 26 | + | foreach ($config['achievements'] as $achName): | |
| 27 | + | ||
| 28 | + | $achName['secret'] = $achName['secret'] ?? false; | |
| 29 | + | $achName['img'] = $achName['img'] ?? 'https://i.imgur.com/ZqWp1TE.png'; | |
| 30 | + | $achName['points'] = (int)($achName['points'] ?? 0); | |
| 31 | + | ||
| 32 | + | $name = $achName[0] ?? ''; | |
| 33 | + | $desc = $achName[1] ?? ''; | |
| 34 | + | ?> | |
| 35 | + | <tr> | |
| 36 | + | <td> | |
| 37 | + | <?php | |
| 38 | + | if ($achName['points'] >= 1 && $achName['points'] <= 3) { | |
| 39 | + | echo '<center><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center>'; | |
| 40 | + | } elseif ($achName['points'] >= 4 && $achName['points'] <= 6) { | |
| 41 | + | echo '<center><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center>'; | |
| 42 | + | } elseif ($achName['points'] >= 7) { | |
| 43 | + | echo '<center><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img class="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center>'; | |
| 44 | + | } else { | |
| 45 | + | echo '<img class="wtf" src="' . htmlspecialchars($achName['img'], ENT_QUOTES) . '">'; | |
| 46 | + | } | |
| 47 | + | ?> | |
| 48 | + | </td> | |
| 49 | + | ||
| 50 | + | <td><?php echo htmlspecialchars($name, ENT_QUOTES); ?></td> | |
| 51 | + | <td><?php echo htmlspecialchars($desc, ENT_QUOTES); ?></td> | |
| 52 | + | ||
| 53 | + | <td> | |
| 54 | + | <?php if (!empty($achName['secret'])): ?> | |
| 55 | + | <img class="wtf" src="https://i.imgur.com/NbPRl7b.gif"> | |
| 56 | + | <?php endif; ?> | |
| 57 | + | </td> | |
| 58 | + | ||
| 59 | + | <td><?php echo $achName['points']; ?></td> | |
| 60 | + | </tr> | |
| 61 | + | ||
| 62 | + | <?php | |
| 63 | + | endforeach; | |
| 64 | + | endif; | |
| 65 | + | ?> | |
| 66 | + | ||
| 67 | + | </table> | |
| 68 | + | </div> | |
| 69 | + | ||
| 70 | + | <?php | |
| 71 | + | else: | |
| 72 | + | echo t('achievements.disabled'); | |
| 73 | + | endif; | |
| 74 | + | ?> |
| @@ -0,0 +1,12 @@ | |||
| 1 | + | <table class="auction_error"> | |
| 2 | + | <tr class="yellow"> | |
| 3 | + | <td>#</td> | |
| 4 | + | <td><?= t('auc.claim_issues') ?></td> | |
| 5 | + | </tr> | |
| 6 | + | <?php foreach($errors as $i => $error): ?> | |
| 7 | + | <tr> | |
| 8 | + | <td><?php echo $i+1; ?></td> | |
| 9 | + | <td><?php echo $error; ?></td> | |
| 10 | + | </tr> | |
| 11 | + | <?php endforeach; ?> | |
| 12 | + | </table> |
| @@ -0,0 +1,39 @@ | |||
| 1 | + | <?php if (is_array($own_characters) && !empty($own_characters)): ?> | |
| 2 | + | <p><a href="/auctionChar.php?action=list"><?= t('auc.go_back_list') ?></a></p> | |
| 3 | + | <form action="/auctionChar.php" method="POST"> | |
| 4 | + | <input type="hidden" name="action" value="add"> | |
| 5 | + | <p><?= t('auc.char_offline') ?></p> | |
| 6 | + | <select name="pid"> | |
| 7 | + | <?php foreach($own_characters as $char): ?> | |
| 8 | + | <option value="<?php echo $char['id']; ?>"> | |
| 9 | + | <?php echo "Level: ", $char['level'], " ", vocation_id_to_name($char['vocation']), ": ", $char['name']; ?> | |
| 10 | + | </option> | |
| 11 | + | <?php endforeach; ?> | |
| 12 | + | </select> | |
| 13 | + | <p><strong><?= t('auc.shop_points') ?></strong> | |
| 14 | + | <br><?= t('auc.your_points') ?> <?php echo $own_characters[0]['points']; ?> | |
| 15 | + | <br><?= t('auc.minimum') ?> <?php echo $auction['lowestPrice']; ?> | |
| 16 | + | <br>deposit: <?php echo $auction['deposit']; ?>% | |
| 17 | + | <br><?= t('auc.your_max') ?> <?php echo $max; ?> | |
| 18 | + | </p> | |
| 19 | + | <p><strong><?= t('auc.deposit_info') ?></strong> | |
| 20 | + | <br>To ensure you as the seller is a legitimate account, and to encourage fair prices you have to temporarily invest <?php echo $auction['deposit']; ?>% of the selling price as a deposit. | |
| 21 | + | </p> | |
| 22 | + | <p>Once the auction has completed, the deposit fee will be refunded back to your account.</p> | |
| 23 | + | <p>If you wish to reclaim your character, you can do it after the bidding period if nobody has placed an offer on it. But if you do this you will not get the deposit back. It is therefore advisable that you create a good and appealing offer to our community.</p> | |
| 24 | + | <p><?= t('auc.sell_price') ?></p> | |
| 25 | + | <input type="number" name="cost" min="<?php echo $auction['lowestPrice']; ?>" max="<?php echo $max; ?>" step="5" placeholder="<?php echo $auction['lowestPrice']; ?> - <?php echo $max; ?>"> | |
| 26 | + | <br> | |
| 27 | + | <p><?= t('auc.verify_pw') ?></p> | |
| 28 | + | <input type="password" name="password"> | |
| 29 | + | <br> | |
| 30 | + | <input type="submit" value="<?= t('auc.sell_btn') ?>"> | |
| 31 | + | </form> | |
| 32 | + | <?php else: ?> | |
| 33 | + | <p><a href="/auctionChar.php?action=list"><?= t('auc.go_back_list') ?></a></p> | |
| 34 | + | <p>Your account does not follow the required rules to sell characters. | |
| 35 | + | <br>1. Minimum level: <?php echo $auction['lowestLevel']; ?> | |
| 36 | + | <br>2. Minimum already earned shop points: <?php echo $minToCreate; ?> | |
| 37 | + | <br>3. Eligible characters must be offline. | |
| 38 | + | </p> | |
| 39 | + | <?php endif; ?> |
| @@ -0,0 +1,1 @@ | |||
| 1 | + | <p><?= t('auc.disabled2') ?></p> |
| @@ -0,0 +1,1 @@ | |||
| 1 | + | <h1><?= t('auc.page_title') ?></h1> |
| @@ -0,0 +1,237 @@ | |||
| 1 | + | <?php | |
| 2 | + | ?> | |
| 3 | + | <style> | |
| 4 | + | .auc2-filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; margin-bottom: 15px; } | |
| 5 | + | .auc2-filters .auc2-field { display: flex; flex-direction: column; gap: 3px; font-size: 12px; } | |
| 6 | + | .auc2-filters input, .auc2-filters select { padding: 4px 6px; } | |
| 7 | + | .auc2-results-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 13px; } | |
| 8 | + | .auc2-pagination { display: flex; gap: 4px; flex-wrap: wrap; } | |
| 9 | + | .auc2-pagination a, .auc2-pagination span { padding: 3px 8px; border: 1px solid #999; text-decoration: none; } | |
| 10 | + | .auc2-pagination .is-current { font-weight: bold; } | |
| 11 | + | .auc2-card { border: 1px solid #999; margin-bottom: 12px; padding: 8px 10px; } | |
| 12 | + | .auc2-card-header { margin-bottom: 6px; } | |
| 13 | + | .auc2-card-header .auc2-name { font-weight: bold; } | |
| 14 | + | .auc2-card-row { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; } | |
| 15 | + | .auc2-col-outfit { width: 64px; flex: 0 0 auto; text-align: center; } | |
| 16 | + | .auc2-col-items { flex: 0 0 auto; display: grid; grid-template-columns: repeat(2, 34px); grid-template-rows: repeat(2, 34px); gap: 4px; } | |
| 17 | + | .auc2-item-tile { position: relative; width: 34px; height: 34px; border: 1px solid #ccc; display: flex; align-items: center; justify-content: center; } | |
| 18 | + | .auc2-item-tile img { max-width: 28px; max-height: 28px; } | |
| 19 | + | .auc2-item-count { position: absolute; bottom: 0; right: 1px; font-size: 9px; background: rgba(0,0,0,.6); color: #fff; padding: 0 2px; } | |
| 20 | + | .auc2-col-data { flex: 1 1 220px; font-size: 12px; line-height: 1.5; } | |
| 21 | + | .auc2-col-data .auc2-label { color: #666; } | |
| 22 | + | .auc2-coin { width: 14px; height: 14px; vertical-align: middle; margin-left: 2px; } | |
| 23 | + | .auc2-amount { white-space: nowrap; } | |
| 24 | + | .auc2-col-bid { flex: 0 0 180px; border-left: 1px solid #ccc; padding-left: 12px; } | |
| 25 | + | .auc2-col-bid form { display: flex; flex-direction: column; gap: 4px; } | |
| 26 | + | </style> | |
| 27 | + | ||
| 28 | + | <?php if ($pending !== false && $pending): ?> | |
| 29 | + | <h2><?= t('common.congrats') ?></h2> | |
| 30 | + | <p><?= t('common.you_have') ?> <?php echo (COUNT($pending) > 1) ? 'characters' : 'a character'; ?> ready to claim!</p> | |
| 31 | + | <?php foreach($pending as $character): ?> | |
| 32 | + | <table class="auction_char"> | |
| 33 | + | <tr class="yellow"> | |
| 34 | + | <td>Level</td> | |
| 35 | + | <td><?= t('common.vocation') ?></td> | |
| 36 | + | <td><?= t('auc.details') ?></td> | |
| 37 | + | <td>Price</td> | |
| 38 | + | </tr> | |
| 39 | + | <tr> | |
| 40 | + | <td><?php echo $character['level']; ?></td> | |
| 41 | + | <td><?php echo vocation_id_to_name($character['vocation']); ?></td> | |
| 42 | + | <td><a href="/auctionChar.php?action=view&zaid=<?php echo $character['zaid']; ?>">VIEW</a></td> | |
| 43 | + | <td><?php echo $character['price']; ?></td> | |
| 44 | + | </tr> | |
| 45 | + | <tr> | |
| 46 | + | <?php if ($loadOutfits): ?> | |
| 47 | + | <td class="outfitColumn"> | |
| 48 | + | <img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt="img"> | |
| 49 | + | </td> | |
| 50 | + | <?php endif; ?> | |
| 51 | + | <td colspan="3"> | |
| 52 | + | <p><?= t('auc.claim_name') ?></p> | |
| 53 | + | <form action="/auctionChar.php" method="POST"> | |
| 54 | + | <input type="hidden" name="action" value="claim"> | |
| 55 | + | <input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>"> | |
| 56 | + | <input type="text" name="name"> | |
| 57 | + | <input type="submit" value="<?= t('auc.claim_btn') ?>"> | |
| 58 | + | </form> | |
| 59 | + | </td> | |
| 60 | + | </tr> | |
| 61 | + | </table> | |
| 62 | + | <?php endforeach; ?> | |
| 63 | + | <h2><?= t('auc.ongoing') ?></h2> | |
| 64 | + | <?php endif; ?> | |
| 65 | + | ||
| 66 | + | <?php if ($is_admin): ?> | |
| 67 | + | <p>Admin: <a href="/admin/index.php?p=auction"><?= t('auc.history') ?></a></p> | |
| 68 | + | <?php endif; ?> | |
| 69 | + | ||
| 70 | + | <?php | |
| 71 | + | if (!function_exists('auc2_url')) { | |
| 72 | + | function auc2_url(array $overrides = array()) { | |
| 73 | + | global $filterVoc, $filterLevelMin, $filterLevelMax, $search, $sort, $page; | |
| 74 | + | $params = array_merge(array( | |
| 75 | + | 'voc' => $filterVoc ?: '', | |
| 76 | + | 'level_min' => $filterLevelMin ?: '', | |
| 77 | + | 'level_max' => $filterLevelMax ?: '', | |
| 78 | + | 'q' => $search, | |
| 79 | + | 'sort' => $sort, | |
| 80 | + | 'page' => $page, | |
| 81 | + | ), $overrides); | |
| 82 | + | $params = array_filter($params, static fn($v) => $v !== '' && $v !== 0 && $v !== null); | |
| 83 | + | return 'auctionChar.php?' . http_build_query($params); | |
| 84 | + | } | |
| 85 | + | } | |
| 86 | + | ?> | |
| 87 | + | ||
| 88 | + | <form action="auctionChar.php" method="get" class="auc2-filters"> | |
| 89 | + | <div class="auc2-field"> | |
| 90 | + | <label for="auc2_voc"><?= t('common.vocation') ?></label> | |
| 91 | + | <select id="auc2_voc" name="voc"> | |
| 92 | + | <option value="0"><?= t('vocation.any') ?></option> | |
| 93 | + | <?php foreach ($config['vocations'] as $id => $vocation): if ((int)$id === 0) continue; ?> | |
| 94 | + | <option value="<?= (int)$id ?>" <?= $filterVoc === (int)$id ? 'selected' : '' ?>><?= h($vocation['name']) ?></option> | |
| 95 | + | <?php endforeach; ?> | |
| 96 | + | </select> | |
| 97 | + | </div> | |
| 98 | + | <div class="auc2-field"> | |
| 99 | + | <label for="auc2_level_min"><?= t('auc.min_level') ?></label> | |
| 100 | + | <input type="number" id="auc2_level_min" name="level_min" min="0" value="<?= $filterLevelMin ?: '' ?>" style="width:80px;"> | |
| 101 | + | </div> | |
| 102 | + | <div class="auc2-field"> | |
| 103 | + | <label for="auc2_level_max">—</label> | |
| 104 | + | <input type="number" id="auc2_level_max" name="level_max" min="0" value="<?= $filterLevelMax ?: '' ?>" style="width:80px;"> | |
| 105 | + | </div> | |
| 106 | + | <div class="auc2-field"> | |
| 107 | + | <label for="auc2_sort"><?= t_default('auc.sort_by', 'Sort by') ?></label> | |
| 108 | + | <select id="auc2_sort" name="sort"> | |
| 109 | + | <option value="level_desc" <?= $sort === 'level_desc' ? 'selected' : '' ?>><?= t_default('auc.sort_level_desc', 'Level (high to low)') ?></option> | |
| 110 | + | <option value="level_asc" <?= $sort === 'level_asc' ? 'selected' : '' ?>><?= t_default('auc.sort_level_asc', 'Level (low to high)') ?></option> | |
| 111 | + | <option value="price_desc" <?= $sort === 'price_desc' ? 'selected' : '' ?>><?= t_default('auc.sort_price_desc', 'Price (high to low)') ?></option> | |
| 112 | + | <option value="price_asc" <?= $sort === 'price_asc' ? 'selected' : '' ?>><?= t_default('auc.sort_price_asc', 'Price (low to high)') ?></option> | |
| 113 | + | <option value="ending_soon" <?= $sort === 'ending_soon' ? 'selected' : '' ?>><?= t_default('auc.sort_ending_soon', 'Ending soon') ?></option> | |
| 114 | + | </select> | |
| 115 | + | </div> | |
| 116 | + | <div class="auc2-field" style="flex:1 1 200px;"> | |
| 117 | + | <label for="auc2_q"><?= t('common.search') ?></label> | |
| 118 | + | <input type="text" id="auc2_q" name="q" value="<?= h($search) ?>" placeholder="<?= h(t_default('auc.search_placeholder', 'Character or item name')) ?>"> | |
| 119 | + | </div> | |
| 120 | + | <div class="auc2-field"> | |
| 121 | + | <input type="submit" value="<?= t('common.search') ?>"> | |
| 122 | + | </div> | |
| 123 | + | </form> | |
| 124 | + | ||
| 125 | + | <div class="auc2-results-bar"> | |
| 126 | + | <?php if ($pageCount > 1): ?> | |
| 127 | + | <div class="auc2-pagination"> | |
| 128 | + | <?php for ($p = 1; $p <= $pageCount; $p++): ?> | |
| 129 | + | <?php if ($p === $page): ?> | |
| 130 | + | <span class="is-current"><?= $p ?></span> | |
| 131 | + | <?php else: ?> | |
| 132 | + | <a href="<?= h(auc2_url(['page' => $p])) ?>"><?= $p ?></a> | |
| 133 | + | <?php endif; ?> | |
| 134 | + | <?php endfor; ?> | |
| 135 | + | </div> | |
| 136 | + | <?php endif; ?> | |
| 137 | + | </div> | |
| 138 | + | ||
| 139 | + | <?php if (is_array($characters) && !empty($characters)): ?> | |
| 140 | + | <?php foreach ($characters as $character): ?> | |
| 141 | + | <article class="auc2-card"> | |
| 142 | + | <div class="auc2-card-header"> | |
| 143 | + | <div class="auc2-name"><a href="/auctionChar.php?action=view&zaid=<?= (int)$character['zaid'] ?>"><?= h(t('common.level')) ?> <?= (int)$character['level'] ?> <?= h(vocation_id_to_name($character['vocation'])) ?></a></div> | |
| 144 | + | <div> | |
| 145 | + | <?= t('common.level') ?>: <?= (int)$character['level'] ?> | |
| 146 | + | | <?= t('common.vocation') ?>: <?= h(vocation_id_to_name($character['vocation'])) ?> | |
| 147 | + | | <?= ((int)$character['sex'] === 1) ? t('common.male') : t('common.female') ?> | |
| 148 | + | | <a href="/auctionChar.php?action=view&zaid=<?= (int)$character['zaid'] ?>"><?= t('auc.details') ?></a> | |
| 149 | + | </div> | |
| 150 | + | </div> | |
| 151 | + | <div class="auc2-card-row"> | |
| 152 | + | <div class="auc2-col-outfit"> | |
| 153 | + | <?php if ($loadOutfits): ?> | |
| 154 | + | <img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt=""> | |
| 155 | + | <?php endif; ?> | |
| 156 | + | </div> | |
| 157 | + | <div class="auc2-col-items"> | |
| 158 | + | <?php $highlights = $highlightItems[(int)$character['player_id']] ?? array(); ?> | |
| 159 | + | <?php foreach ($highlights as $hi): ?> | |
| 160 | + | <div class="auc2-item-tile" title="<?= h($items[$hi['itemtype']] ?? ('#' . $hi['itemtype'])) ?>"> | |
| 161 | + | <img src="<?= h(znote_item_image_url((int)$hi['itemtype'])) ?>" alt=""> | |
| 162 | + | <?php if ($hi['count'] > 1): ?><span class="auc2-item-count"><?= (int)$hi['count'] ?></span><?php endif; ?> | |
| 163 | + | </div> | |
| 164 | + | <?php endforeach; ?> | |
| 165 | + | </div> | |
| 166 | + | <div class="auc2-col-data"> | |
| 167 | + | <div><span class="auc2-label"><?= t_default('auc.start', 'Auction Start:') ?></span> <?= h(getClock($character['time_begin'], true)) ?></div> | |
| 168 | + | <div><span class="auc2-label"><?= t_default('auc.end', 'Auction End:') ?></span> <?= h(getClock($character['time_end'], true)) ?></div> | |
| 169 | + | <div> | |
| 170 | + | <?php $ended = (time() > $character['time_end']); ?> | |
| 171 | + | <span class="auc2-label"><?= t_default('auc.type_label', 'Type:') ?></span> | |
| 172 | + | <?php if ($ended): ?> | |
| 173 | + | <?= t_default('auc.instant', 'Instant') ?> | |
| 174 | + | <?php else: ?> | |
| 175 | + | <?= t_default('auc.bidding', 'Bidding') ?> (<span data-auc2-countdown="<?= (int)$character['time_end'] ?>" data-auc2-ended-text="<?= h(t_default('auc.instant', 'Instant')) ?>"><?= h(toDuration($character['time_end'] - time())) ?></span>) | |
| 176 | + | <?php endif; ?> | |
| 177 | + | </div> | |
| 178 | + | <div><span class="auc2-label"><?= t_default('auc.min_bid', 'Minimum Bid:') ?></span> <span class="auc2-amount"><?= (int)$character['price'] ?><img class="auc2-coin" src="<?= theme_asset('icon-tibiacoin.png') ?>" alt=""></span></div> | |
| 179 | + | </div> | |
| 180 | + | <div class="auc2-col-bid"> | |
| 181 | + | <form action="/auctionChar.php" method="POST"> | |
| 182 | + | <input type="hidden" name="action" value="bid"> | |
| 183 | + | <input type="hidden" name="zaid" value="<?= (int)$character['zaid'] ?>"> | |
| 184 | + | <label><?= t_default('auc.my_bid_limit', 'My Bid Limit') ?></label> | |
| 185 | + | <input type="number" name="price" min="<?= (int)$character['price'] ?>" step="1" value="<?= (int)$character['price'] ?>"> | |
| 186 | + | <input type="submit" value="<?= t('auc.bid_btn') ?>"> | |
| 187 | + | </form> | |
| 188 | + | </div> | |
| 189 | + | </div> | |
| 190 | + | </article> | |
| 191 | + | <?php endforeach; ?> | |
| 192 | + | ||
| 193 | + | <?php if ($pageCount > 1): ?> | |
| 194 | + | <div class="auc2-pagination"> | |
| 195 | + | <?php for ($p = 1; $p <= $pageCount; $p++): ?> | |
| 196 | + | <?php if ($p === $page): ?> | |
| 197 | + | <span class="is-current"><?= $p ?></span> | |
| 198 | + | <?php else: ?> | |
| 199 | + | <a href="<?= h(auc2_url(['page' => $p])) ?>"><?= $p ?></a> | |
| 200 | + | <?php endif; ?> | |
| 201 | + | <?php endfor; ?> | |
| 202 | + | </div> | |
| 203 | + | <?php endif; ?> | |
| 204 | + | <?php else: ?> | |
| 205 | + | <p><?= t_default('auc.no_auctions', 'No characters are currently up for auction.') ?></p> | |
| 206 | + | <?php endif; ?> | |
| 207 | + | ||
| 208 | + | <p><a href="/auctionChar.php?action=create"><?= t('auc.add') ?></a>.</p> | |
| 209 | + | ||
| 210 | + | <script> | |
| 211 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 212 | + | // Auction timers tick down live client-side instead of only updating on | |
| 213 | + | // the next full page reload. | |
| 214 | + | document.querySelectorAll('[data-auc2-countdown]').forEach(function (el) { | |
| 215 | + | var end = parseInt(el.getAttribute('data-auc2-countdown'), 10); | |
| 216 | + | function tick() { | |
| 217 | + | var remaining = end - Math.floor(Date.now() / 1000); | |
| 218 | + | if (remaining <= 0) { | |
| 219 | + | el.textContent = el.getAttribute('data-auc2-ended-text') || ''; | |
| 220 | + | return; | |
| 221 | + | } | |
| 222 | + | var d = Math.floor(remaining / 86400); | |
| 223 | + | var h = Math.floor((remaining % 86400) / 3600); | |
| 224 | + | var m = Math.floor((remaining % 3600) / 60); | |
| 225 | + | var s = remaining % 60; | |
| 226 | + | var parts = []; | |
| 227 | + | if (d > 0) parts.push(d + (d === 1 ? ' day' : ' days')); | |
| 228 | + | if (d > 0 || h > 0) parts.push(h + (h === 1 ? ' hour' : ' hours')); | |
| 229 | + | if (m > 0) parts.push(m + (m === 1 ? ' minute' : ' minutes')); | |
| 230 | + | if (s > 0) parts.push(s + (s === 1 ? ' second' : ' seconds')); | |
| 231 | + | el.textContent = parts.join(', '); | |
| 232 | + | setTimeout(tick, 1000); | |
| 233 | + | } | |
| 234 | + | tick(); | |
| 235 | + | }); | |
| 236 | + | }); | |
| 237 | + | </script> |
| @@ -0,0 +1,1 @@ | |||
| 1 | + | <p><?= t('auc.storage_error') ?></p> |
| @@ -0,0 +1,184 @@ | |||
| 1 | + | <?php | |
| 2 | + | ?> | |
| 3 | + | <style> | |
| 4 | + | .auc2-detail-header { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 10px; } | |
| 5 | + | .auc2-detail-outfit { flex: 0 0 auto; } | |
| 6 | + | .auc2-detail-data { flex: 1 1 220px; font-size: 12px; line-height: 1.6; } | |
| 7 | + | .auc2-detail-data .auc2-label { color: #666; } | |
| 8 | + | .auc2-detail-bid { flex: 0 0 200px; border-left: 1px solid #ccc; padding-left: 14px; } | |
| 9 | + | .auc2-coin { width: 14px; height: 14px; vertical-align: middle; margin-left: 2px; } | |
| 10 | + | .auc2-amount { white-space: nowrap; } | |
| 11 | + | .auc2-section { margin-top: 18px; } | |
| 12 | + | .auc2-section h3 { font-size: 13px; margin: 0 0 8px; } | |
| 13 | + | .auc2-stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 24px; font-size: 12px; max-width: 520px; } | |
| 14 | + | .auc2-stats-grid > div { display: flex; justify-content: space-between; align-items: center; gap: 8px; border-bottom: 1px dotted #ccc; padding: 3px 0; } | |
| 15 | + | .auc2-skill-value { flex: 0 0 auto; white-space: nowrap; font-variant-numeric: tabular-nums; } | |
| 16 | + | .auc2-item-search { margin: 10px 0; } | |
| 17 | + | .auc2-item-grid { display: grid; grid-template-columns: repeat(auto-fill, 56px); gap: 8px; } | |
| 18 | + | .auc2-item-grid-tile { position: relative; width: 56px; height: 56px; border: 1px solid #ccc; display: flex; align-items: center; justify-content: center; } | |
| 19 | + | .auc2-item-grid-tile img { max-width: 40px; max-height: 40px; } | |
| 20 | + | .auc2-item-grid-tile .auc2-item-count { position: absolute; bottom: 2px; right: 2px; font-size: 10px; background: rgba(0,0,0,.6); color: #fff; padding: 0 3px; } | |
| 21 | + | .auc2-item-grid-tile[hidden] { display: none; } | |
| 22 | + | </style> | |
| 23 | + | ||
| 24 | + | <div class="auc2-detail-header"> | |
| 25 | + | <div class="auc2-detail-outfit"> | |
| 26 | + | <?php if ($loadOutfits): ?> | |
| 27 | + | <img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt=""> | |
| 28 | + | <?php endif; ?> | |
| 29 | + | </div> | |
| 30 | + | <div class="auc2-detail-data"> | |
| 31 | + | <div><?= t('common.level') ?>: <?= (int)$character['level'] ?> | <?= t('common.vocation') ?>: <?= h(vocation_id_to_name($character['vocation'])) ?> | <?= ((int)$character['sex'] === 1) ? t('common.male') : t('common.female') ?></div> | |
| 32 | + | <?php if ((int)$character['own'] === 1): ?> | |
| 33 | + | <div><span class="auc2-label"><?= t('common.name_label') ?></span> <a href="/characterprofile.php?name=<?php echo $character['name']; ?>"><?php echo $character['name']; ?></a></div> | |
| 34 | + | <div><span class="auc2-label"><?= t('auc.price') ?>:</span> <span class="auc2-amount"><?= (int)$character['price'] ?><img class="auc2-coin" src="<?= theme_asset('icon-tibiacoin.png') ?>" alt=""></span></div> | |
| 35 | + | <div><span class="auc2-label"><?= t('common.bid_label') ?></span> <?= (int)$character['bid'] ?></div> | |
| 36 | + | <div><span class="auc2-label"><?= t('auc.deposit') ?></span> <?= (int)$character['deposit'] ?></div> | |
| 37 | + | <?php else: ?> | |
| 38 | + | <div><span class="auc2-label"><?= t_default('auc.start', 'Auction Start:') ?></span> <?= h(getClock($character['time_begin'], true)) ?></div> | |
| 39 | + | <div><span class="auc2-label"><?= t_default('auc.end', 'Auction End:') ?></span> <?= h(getClock($character['time_end'], true)) ?></div> | |
| 40 | + | <div> | |
| 41 | + | <?php if ($bidding_period): ?> | |
| 42 | + | <span class="auc2-label"><?= t_default('auc.remaining_short', 'Remaining:') ?></span> | |
| 43 | + | <span data-auc2-countdown="<?= (int)$character['time_end'] ?>" data-auc2-ended-text="<?= h(t_default('auc.instant', 'Instant')) ?>"><?= h(toDuration((int)$character['time_end'] - time())) ?></span> | |
| 44 | + | <?php else: ?> | |
| 45 | + | <span class="auc2-label"><?= t_default('auc.type_label', 'Type:') ?></span> <?= t_default('auc.instant', 'Instant') ?> | |
| 46 | + | <?php endif; ?> | |
| 47 | + | </div> | |
| 48 | + | <div><span class="auc2-label"><?= t_default('auc.min_bid', 'Minimum Bid:') ?></span> <span class="auc2-amount"><?= (int)$character['price'] ?><img class="auc2-coin" src="<?= theme_asset('icon-tibiacoin.png') ?>" alt=""></span></div> | |
| 49 | + | <?php endif; ?> | |
| 50 | + | </div> | |
| 51 | + | <div class="auc2-detail-bid"> | |
| 52 | + | <?php if ((int)$character['own'] === 1): ?> | |
| 53 | + | <?php if (!$bidding_period): ?> | |
| 54 | + | <p><?= t('auc.bidding_ended') ?></p> | |
| 55 | + | <form action="/auctionChar.php" method="POST"> | |
| 56 | + | <input type="hidden" name="action" value="refund"> | |
| 57 | + | <input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>"> | |
| 58 | + | <input type="submit" value="<?= t('auc.reclaim_btn') ?>"> | |
| 59 | + | </form> | |
| 60 | + | <?php else: ?> | |
| 61 | + | <p><?= t('auc.bid_period') ?> <?php echo toDuration($character['time_end']-time()); ?>. <?= t('auc.reclaim_after_period') ?></p> | |
| 62 | + | <?php endif; ?> | |
| 63 | + | <?php else: ?> | |
| 64 | + | <?php if (is_array($account) && !empty($account)): ?> | |
| 65 | + | <p><?= t('common.you_have') ?> <strong><?php echo $account['points']; ?></strong> <?= t('auc.points_remaining') ?></p> | |
| 66 | + | <?php if ((int)$character['bidder_account_id'] === $this_account_id): ?> | |
| 67 | + | <p><strong><?= t('auc.so_far_good') ?></strong> | |
| 68 | + | <br><?= t('auc.highest_bid') ?> <?php echo (int)$character['price']-$step; ?> | |
| 69 | + | </p> | |
| 70 | + | <p><?= t('auc.yours_in') ?> | |
| 71 | + | <br><?php echo toDuration((int)$character['time_end']-time()); ?>. | |
| 72 | + | </p> | |
| 73 | + | <?php endif; ?> | |
| 74 | + | <form action="/auctionChar.php" method="POST"> | |
| 75 | + | <input type="hidden" name="action" value="bid"> | |
| 76 | + | <input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>"> | |
| 77 | + | <input type="number" name="price" min="<?php echo $character['price']; ?>" max="<?php echo $account['points']; ?>" step="5" value="<?php echo $character['price']; ?>" <?php if (!$bidding_period) echo 'disabled'; ?>> | |
| 78 | + | <?php if (!$bidding_period): /* Because above input is disabled */ ?> | |
| 79 | + | <input type="hidden" name="price" value="<?php echo $character['price']; ?>"> | |
| 80 | + | <?php endif; ?> | |
| 81 | + | <input type="submit" value="<?php echo ($bidding_period) ? t('auc.bid_btn') : t('auc.buy_btn'); ?>"> | |
| 82 | + | </form> | |
| 83 | + | <?php else: ?> | |
| 84 | + | <?php if ((int)$character['bidder_account_id'] === $this_account_id): ?> | |
| 85 | + | <p><strong><?= t('auc.so_far_good') ?></strong> | |
| 86 | + | <br><?= t('auc.highest_bid') ?> <?php echo (int)$character['price']-$step; ?> | |
| 87 | + | </p> | |
| 88 | + | <p><?= t('auc.yours_in') ?> | |
| 89 | + | <br><?php echo toDuration((int)$character['time_end']-time()); ?>. | |
| 90 | + | </p> | |
| 91 | + | <?php else: ?> | |
| 92 | + | <p><?= t('auc.cannot_afford') ?></p> | |
| 93 | + | <?php endif; ?> | |
| 94 | + | <?php endif; ?> | |
| 95 | + | <?php endif; ?> | |
| 96 | + | </div> | |
| 97 | + | </div> | |
| 98 | + | ||
| 99 | + | <?php | |
| 100 | + | $auc2Skills = array( | |
| 101 | + | array(t('char.magic_level'), (int)$character['magic']), | |
| 102 | + | array(t('skill.fist'), (int)$character['fist']), | |
| 103 | + | array(t('skill.club'), (int)$character['club']), | |
| 104 | + | array(t('skill.sword'), (int)$character['sword']), | |
| 105 | + | array(t('skill.axe'), (int)$character['axe']), | |
| 106 | + | array(t('skill.distance'), (int)$character['dist']), | |
| 107 | + | array(t('skill.shielding'), (int)$character['shielding']), | |
| 108 | + | array(t('skill.fishing'), (int)$character['fishing']), | |
| 109 | + | ); | |
| 110 | + | ?> | |
| 111 | + | <div class="auc2-section"> | |
| 112 | + | <h3><?= t_default('auc.tab_general', 'General') ?></h3> | |
| 113 | + | <div class="auc2-stats-grid"> | |
| 114 | + | <div><span><?= t('common.level') ?></span><span class="auc2-skill-value"><?= (int)$character['level'] ?></span></div> | |
| 115 | + | <?php foreach ($auc2Skills as $auc2Skill): [$auc2SkillLabel, $auc2SkillValue] = $auc2Skill; ?> | |
| 116 | + | <div> | |
| 117 | + | <span><?= h($auc2SkillLabel) ?></span> | |
| 118 | + | <span class="auc2-skill-value"><?= $auc2SkillValue ?></span> | |
| 119 | + | </div> | |
| 120 | + | <?php endforeach; ?> | |
| 121 | + | </div> | |
| 122 | + | </div> | |
| 123 | + | ||
| 124 | + | <?php | |
| 125 | + | $auc2AllItems = array(); | |
| 126 | + | foreach (($player_items ?: array()) as $item) { $auc2AllItems[] = $item; } | |
| 127 | + | foreach (($depot_items ?: array()) as $item) { $auc2AllItems[] = $item; } | |
| 128 | + | ?> | |
| 129 | + | <?php if (!empty($auc2AllItems)): ?> | |
| 130 | + | <div class="auc2-section"> | |
| 131 | + | <h3><?= t('auc.player_items') ?> & <?= t('auc.depot_items') ?></h3> | |
| 132 | + | <input type="text" class="auc2-item-search" id="auc2ItemSearch" placeholder="<?= h(t_default('auc.item_search_placeholder', 'Search item...')) ?>"> | |
| 133 | + | <div class="auc2-item-grid"> | |
| 134 | + | <?php foreach ($auc2AllItems as $item): ?> | |
| 135 | + | <div class="auc2-item-grid-tile" data-auc2-item-name="<?= h(strtolower((string)($items[$item['itemtype']] ?? ''))) ?>" title="<?= h(($items[$item['itemtype']] ?? ('#' . $item['itemtype'])) . ' (' . (int)$item['count'] . ')') ?>"> | |
| 136 | + | <img src="<?= h(znote_item_image_url((int)$item['itemtype'])) ?>" alt=""> | |
| 137 | + | <?php if ((int)$item['count'] > 1): ?><span class="auc2-item-count"><?= (int)$item['count'] ?></span><?php endif; ?> | |
| 138 | + | </div> | |
| 139 | + | <?php endforeach; ?> | |
| 140 | + | </div> | |
| 141 | + | </div> | |
| 142 | + | <?php endif; ?> | |
| 143 | + | ||
| 144 | + | <p><a href="/auctionChar.php"><?= t('auc.go_back_list') ?></a></p> | |
| 145 | + | ||
| 146 | + | <script> | |
| 147 | + | document.addEventListener('DOMContentLoaded', function () { | |
| 148 | + | var search = document.getElementById('auc2ItemSearch'); | |
| 149 | + | if (search) { | |
| 150 | + | search.addEventListener('keyup', function () { | |
| 151 | + | var needle = search.value.trim().toLowerCase(); | |
| 152 | + | document.querySelectorAll('.auc2-item-grid-tile').forEach(function (tile) { | |
| 153 | + | var name = tile.getAttribute('data-auc2-item-name') || ''; | |
| 154 | + | tile.hidden = needle !== '' && name.indexOf(needle) === -1; | |
| 155 | + | }); | |
| 156 | + | }); | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | // Auction timers tick down live client-side instead of only updating on | |
| 160 | + | // the next full page reload. | |
| 161 | + | document.querySelectorAll('[data-auc2-countdown]').forEach(function (el) { | |
| 162 | + | var end = parseInt(el.getAttribute('data-auc2-countdown'), 10); | |
| 163 | + | function tick() { | |
| 164 | + | var remaining = end - Math.floor(Date.now() / 1000); | |
| 165 | + | if (remaining <= 0) { | |
| 166 | + | el.textContent = el.getAttribute('data-auc2-ended-text') || ''; | |
| 167 | + | return; | |
| 168 | + | } | |
| 169 | + | var d = Math.floor(remaining / 86400); | |
| 170 | + | var h = Math.floor((remaining % 86400) / 3600); | |
| 171 | + | var m = Math.floor((remaining % 3600) / 60); | |
| 172 | + | var s = remaining % 60; | |
| 173 | + | var parts = []; | |
| 174 | + | if (d > 0) parts.push(d + (d === 1 ? ' day' : ' days')); | |
| 175 | + | if (d > 0 || h > 0) parts.push(h + (h === 1 ? ' hour' : ' hours')); | |
| 176 | + | if (m > 0) parts.push(m + (m === 1 ? ' minute' : ' minutes')); | |
| 177 | + | if (s > 0) parts.push(s + (s === 1 ? ' second' : ' seconds')); | |
| 178 | + | el.textContent = parts.join(', '); | |
| 179 | + | setTimeout(tick, 1000); | |
| 180 | + | } | |
| 181 | + | tick(); | |
| 182 | + | }); | |
| 183 | + | }); | |
| 184 | + | </script> |