serverinfo.php

main 248 lines · 8 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php
2/**
3 * Title: Server Information
4 * Icon: fa-server
5 * Group: Server Info
6 * Order: 10
7 * Description: Upload config.lua, stages.xml, items.xml, spells.xml and your monster folder.
8 */
9
10if (!defined('ACP_ROOT')) {
11 http_response_code(403);
12 die('Direct access denied.');
13}
14
15$sources = serverdata_sources();
16
17if ($_SERVER['REQUEST_METHOD'] === 'POST') {
18
19 $do = (string)($_POST['do'] ?? '');
20 $key = (string)($_POST['src'] ?? '');
21
22 if (!isset($sources[$key])) {
23 acp_flash_error(t('acp.srv.unknown_source'));
24 acp_redirect('serverinfo');
25 }
26
27 $label = $sources[$key]['label'];
28
29 if ($do === 'clear') {
30 serverdata_clear($key);
31 acp_log('serverdata.clear', $key);
32 acp_flash_success(t('acp.srv.removed', ['label' => $label, 'page' => $sources[$key]['page']]));
33 acp_redirect('serverinfo');
34 }
35
36 if ($do === 'rebuild') {
37 $error = null;
38 if (serverdata_rebuild($key, $error)) {
39 $count = serverdata_count($key, serverdata_load($key));
40 acp_log('serverdata.rebuild', $key, ['records' => $count]);
41 acp_flash_success(t('acp.srv.rebuilt', ['label' => $label, 'count' => number_format($count)]));
42 if ($error !== null) {
43 acp_flash_info($error);
44 }
45 } else {
46 acp_flash_error($label . ': ' . (string)$error);
47 }
48 acp_redirect('serverinfo');
49 }
50
51 if ($do === 'upload') {
52 $file = $_FILES['payload'] ?? null;
53
54 if (!is_array($file) || ($file['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
55 acp_flash_error(t('acp.srv.select_file'));
56 acp_redirect('serverinfo');
57 }
58
59 if (($file['error'] ?? UPLOAD_ERR_OK) !== UPLOAD_ERR_OK) {
60 $reasons = [
61 UPLOAD_ERR_INI_SIZE => t('acp.srv.err_ini_size'),
62 UPLOAD_ERR_FORM_SIZE => t('acp.srv.err_form_size'),
63 UPLOAD_ERR_PARTIAL => t('acp.srv.err_partial'),
64 UPLOAD_ERR_NO_TMP_DIR => t('acp.srv.err_no_tmp_dir'),
65 UPLOAD_ERR_CANT_WRITE => t('acp.srv.err_cant_write'),
66 UPLOAD_ERR_EXTENSION => t('acp.srv.err_extension'),
67 ];
68 acp_flash_error($reasons[(int)$file['error']] ?? t('acp.srv.upload_failed'));
69 acp_redirect('serverinfo');
70 }
71
72 if (!is_uploaded_file((string)$file['tmp_name'])) {
73 acp_flash_error(t('acp.srv.upload_unverified'));
74 acp_redirect('serverinfo');
75 }
76
77 $error = null;
78 if (serverdata_publish_upload($key, (string)$file['tmp_name'], (string)$file['name'], $error)) {
79 $count = serverdata_count($key, serverdata_load($key));
80 acp_log('serverdata.upload', $key, [
81 'file' => basename((string)$file['name']),
82 'records' => $count,
83 ]);
84 acp_flash_success(t('acp.srv.uploaded', [
85 'label' => $label,
86 'count' => number_format($count),
87 'page' => $sources[$key]['page'],
88 ]));
89 if ($error !== null) {
90 acp_flash_info($error);
91 }
92 } else {
93 acp_flash_error($label . ': ' . (string)$error);
94 }
95
96 acp_redirect('serverinfo');
97 }
98}
99
100$status = serverdata_status();
101$itemsPage = !empty($config['items']);
102$maxUpload = min(
103 (int)serverdata_ini_bytes((string)ini_get('upload_max_filesize')),
104 (int)serverdata_ini_bytes((string)ini_get('post_max_size'))
105);
106?>
107
108<div class="acp-toolbar">
109 <div>
110 <?php
111 $ready = 0;
112 foreach ($status as $row) {
113 if ($row['cached']) {
114 $ready++;
115 }
116 }
117 ?>
118 <span class="acp-pill acp-pill--<?= $ready === count($status) ? 'green' : 'grey' ?>">
119 <?= t('acp.srv.sources_published', ['ready' => (int)$ready, 'total' => count($status)]) ?>
120 </span>
121 </div>
122 <div class="acp-actions is-tight">
123 <a class="acp-btn acp-btn--ghost acp-btn--sm" href="<?= h(acp_site('serverinfo.php')) ?>" target="_blank">
124 <i class="fa fa-external-link"></i> <?= t('acp.srv.public_page') ?>
125 </a>
126 </div>
127</div>
128
129<?php if (!$itemsPage): ?>
130 <div class="acp-flash acp-flash--info">
131 <i class="fa fa-info-circle"></i>
132 <span>
133 <?= t('acp.srv.items_off', [
134 'items' => '<code>items</code>',
135 'settings' => '<a href="' . h(acp_url('settings')) . '">' . t('acp.srv.settings_link') . '</a>',
136 ]) ?>
137 </span>
138 </div>
139<?php endif; ?>
140
141<div class="acp-grid acp-grid--2">
142 <?php
143 $editorModules = array('config' => 'config_editor', 'items' => 'items_editor', 'creatures' => 'creatures_editor');
144 ?>
145 <?php foreach ($status as $key => $row): ?>
146 <section class="acp-card">
147 <header class="acp-card-head">
148 <h2><?= h($row['label']) ?></h2>
149 <p><?= h($row['help']) ?></p>
150 <?php if ($row['cached'] && isset($editorModules[$key])): ?>
151 <a class="acp-btn acp-btn--ghost acp-btn--sm" href="<?= h(acp_url($editorModules[$key])) ?>">
152 <i class="fa fa-pencil"></i> <?= t_default('acp.srv.edit_entries', 'Edit entries') ?>
153 </a>
154 <?php endif; ?>
155 </header>
156 <div class="acp-card-body">
157
158 <p>
159 <?php if ($row['cached']): ?>
160 <span class="acp-pill acp-pill--green"><?= t('acp.srv.published', ['count' => number_format((int)$row['count'])]) ?></span>
161 <?php else: ?>
162 <span class="acp-pill acp-pill--grey"><?= t('acp.srv.nothing_published') ?></span>
163 <?php endif; ?>
164 <?php if ($row['upload_size']): ?>
165 <span class="acp-pill acp-pill--blue"><?= h(serverdata_human_size((int)$row['upload_size'])) ?></span>
166 <?php endif; ?>
167 <?php if ($key === 'creatures' && (int)$row['files'] > 1): ?>
168 <span class="acp-pill acp-pill--blue"><?= t('acp.srv.xml_files', ['count' => number_format((int)$row['files'])]) ?></span>
169 <?php endif; ?>
170 </p>
171
172 <table class="acp-table">
173 <tbody>
174 <tr>
175 <td><?= t('acp.srv.col_source') ?></td>
176 <td><code><?= h($row['path_label']) ?></code></td>
177 </tr>
178 <?php if ($row['keeps_file']): ?>
179 <tr>
180 <td><?= t('acp.srv.col_uploaded') ?></td>
181 <td><?= $row['upload_date'] ? h(date('Y-m-d H:i', (int)$row['upload_date'])) : '&mdash;' ?></td>
182 </tr>
183 <?php endif; ?>
184 <tr>
185 <td><?= t('acp.srv.col_published') ?></td>
186 <td><?= $row['cache_date'] ? h(date('Y-m-d H:i', (int)$row['cache_date'])) : '&mdash;' ?></td>
187 </tr>
188 <tr>
189 <td><?= t('acp.srv.col_public_page') ?></td>
190 <td><a href="<?= h(acp_site($row['page'])) ?>" target="_blank"><?= h($row['page']) ?></a></td>
191 </tr>
192 </tbody>
193 </table>
194
195 <form method="post" enctype="multipart/form-data">
196 <?= acp_csrf_field() ?>
197 <input type="hidden" name="do" value="upload">
198 <input type="hidden" name="src" value="<?= h($key) ?>">
199
200 <div class="acp-field">
201 <label class="acp-label" for="src_<?= h($key) ?>"><?= t('acp.srv.upload_label', ['accept' => h($row['accept'])]) ?></label>
202 <input class="acp-input" type="file" id="src_<?= h($key) ?>" name="payload" accept="<?= h($row['accept']) ?>" required>
203 </div>
204
205 <div class="acp-actions">
206 <button class="acp-btn acp-btn--green" type="submit">
207 <i class="fa fa-upload"></i> <?= t('acp.srv.upload_publish') ?>
208 </button>
209 <?php if ($row['uploaded'] && $row['keeps_file']): ?>
210 <button class="acp-btn acp-btn--ghost" type="submit" form="rebuild_<?= h($key) ?>">
211 <i class="fa fa-refresh"></i> <?= t('acp.srv.reread') ?>
212 </button>
213 <?php endif; ?>
214 <?php if ($row['uploaded'] || $row['cached']): ?>
215 <button class="acp-btn acp-btn--red" type="submit" form="clear_<?= h($key) ?>">
216 <i class="fa fa-trash"></i> <?= t('acp.srv.remove') ?>
217 </button>
218 <?php endif; ?>
219 </div>
220 </form>
221
222 <?php if ($row['uploaded'] && $row['keeps_file']): ?>
223 <form method="post" id="rebuild_<?= h($key) ?>">
224 <?= acp_csrf_field() ?>
225 <input type="hidden" name="do" value="rebuild">
226 <input type="hidden" name="src" value="<?= h($key) ?>">
227 </form>
228 <?php endif; ?>
229 <?php if ($row['uploaded'] || $row['cached']): ?>
230 <form method="post" id="clear_<?= h($key) ?>" onsubmit="return confirm('<?= h(t('acp.srv.confirm_remove', ['label' => $row['label'], 'page' => $row['page']])) ?>');">
231 <?= acp_csrf_field() ?>
232 <input type="hidden" name="do" value="clear">
233 <input type="hidden" name="src" value="<?= h($key) ?>">
234 </form>
235 <?php endif; ?>
236
237 </div>
238 </section>
239 <?php endforeach; ?>
240</div>
241
242<p class="acp-hint">
243 <?= t('acp.srv.storage_hint', [
244 'path' => '<code>' . h(ZNOTE_SERVERDATA_DIR) . '</code>',
245 'size' => h(serverdata_human_size($maxUpload)),
246 ]) ?>
247</p>
248
Top