refactor: 🚀 v2

This commit is contained in:
draconigen 2025-02-26 21:01:47 +01:00
parent a4dceb0da5
commit 026aea638f
4 changed files with 171 additions and 102 deletions

View File

@ -16,12 +16,18 @@ header, main, footer {
margin: 0 auto; margin: 0 auto;
} }
section {
border: 1px solid #666;
padding: 15px;
margin: 10px 0;
}
.height-100-75 {
height: calc(100% - 75px);
}
#bookmark { #bookmark {
background-color: #333; background-color: #333;
} }
.zfont {
font-size: 0;
}
#img { #img {
z-index: -1; z-index: -1;

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

227
index.php
View File

@ -21,79 +21,87 @@ $alerts = [
'danger' => [] 'danger' => []
]; ];
$data = ['img' => '', 'name' => '', 'url' => '', 'desc' => ''];
try { try {
$data = Storage::get($id); $data = Storage::get($id);
} }
catch(Exception $ex) { catch(Exception $ex) {
$alerts['danger'][] = '❌ Error reading database'; $alerts['danger'][] = '❌ Error reading database';
} }
if (empty($data)) {
$data = [
'img' => '',
'name' => '',
'url' => '',
'desc' => ''
];
}
$img = $data['img']; $gallery = [];
// echo '<pre>'; print_r($img); echo '</pre>'; if (file_exists("uppies/$id/gallery")) {
$name = $data['name']; foreach(scandir("uppies/$id/gallery") as $g) {
$url = $data['url']; if (in_array($g, ['.', '..', '.thumbs', '.DS_Store']))
$desc = $data['desc']; continue;
$gallery[] = $g;
}
}
if ($_SERVER["REQUEST_METHOD"] === 'POST' && isset($_GET['upload'])) { if ($_SERVER["REQUEST_METHOD"] === 'POST' && isset($_GET['upload'])) {
http_response_code(202); $upload = htmlspecialchars($_GET['upload']);
http_response_code(202); # 202 is abused for errors, because uikit requires 2xx responses
$targetDir = "uppies/"; $targetDir = "uppies/${id}/${upload}/";
$file = $_FILES["files"]; $file = $_FILES["files"];
$targetFile = $targetDir . basename($file["name"][0]); $fileName = basename($file["name"][0]);
$imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));
// Create the uploads directory if it doesn't exist // Create the uploads directory if it doesn't exist
if (!is_dir($targetDir)) { if (!is_dir($targetDir)) {
mkdir($targetDir, 0775, true); mkdir($targetDir, 0775, true);
} }
// Check if the file is actually an image // check if the file is actually an image
$check = getimagesize($file["tmp_name"][0]); if (getimagesize($file["tmp_name"][0]) === false) {
if ($check === false) {
exit("❌ Error: File is not a valid image."); exit("❌ Error: File is not a valid image.");
} }
// Allow only specific image file formats // allow only specific image extensions
$allowedTypes = ["jpg", "jpeg", "png", "gif"]; if (!in_array(strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), ["psd", "tiff", "jpg", "jpeg", "png", "gif"])) {
if (!in_array($imageFileType, $allowedTypes)) {
exit("❌ Error: Only JPG, JPEG, PNG and GIF files allowed."); exit("❌ Error: Only JPG, JPEG, PNG and GIF files allowed.");
} }
// Limit file size (e.g., 5MB) // limit file size
if ($file["size"][0] > 100 * 1024 * 1024) { if ($file["size"][0] > 200 * 1024 * 1024) {
exit("❌ Error: File too large (max. 100 MB)"); exit("❌ Error: File too large (max. 200 MB)");
} }
// generate destination file name
$finalPath = $targetDir . $id . '.' . $imageFileType;
// Move uploaded file to the target directory // Move uploaded file to the target directory
if (move_uploaded_file($file["tmp_name"][0], $finalPath)) { if (move_uploaded_file($file["tmp_name"][0], $targetDir . $fileName)) {
$data['img'] = $targetDir . $fileName;
try { try {
Storage::set_img($id, $id . '.' . $imageFileType); Storage::set($id, $data);
} }
catch(Exception $ex) { catch(Exception $ex) {
exit("❌ Database error."); exit("❌ Database error.");
} }
http_response_code(200); http_response_code(200);
exit($finalPath); exit(($upload === 'avatar'? $targetDir : '') . $fileName);
}
else {
exit("❌ Write Error.");
} }
exit("❌ General error saving the file."); exit("❌ General error.");
} }
else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset($_POST['name']) && isset($_POST['url'])) { else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset($_POST['name']) && isset($_POST['url'])) {
$name = htmlspecialchars(trim($_POST['name'])); $data['name'] = htmlspecialchars(trim($_POST['name']));
$url = htmlspecialchars(trim($_POST['url'])); $data['url'] = htmlspecialchars(trim($_POST['url']));
$desc = htmlspecialchars(trim($_POST['desc'])); $data['desc'] = htmlspecialchars(trim($_POST['desc']));
if (!empty($name) && !empty($desc) && !empty($url)) { if (!empty($data['name']) && !empty($data['desc']) && !empty($data['url'])) {
try { try {
Storage::set_data($id, $name, $url, $desc); Storage::set($id, $data);
Telegram::report("EF Conbook Artist Credits Submission\nname: $name\nurl: $url\ntext:\n$desc"); Telegram::report("EF Conbook Artist Credits Submission\nname: ". $data['name'] ."\nurl: ". $data['url'] ."\ntext:\n". $data['desc']);
$alerts['success'][] = '✅ Entry saved'; $alerts['success'][] = '✅ Entry saved';
} }
catch(Exception $ex) { catch(Exception $ex) {
@ -116,14 +124,14 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset
<meta name="author" content="The Eurofurence Conbook Team" /> <meta name="author" content="The Eurofurence Conbook Team" />
<meta name="rating" content="general" /> <meta name="rating" content="general" />
<link rel="shortcut icon" href="favicon.png"> <link rel="shortcut icon" href="favicon.ico">
<meta property="og:image" content="img/ogp.jpg" /> <meta property="og:image" content="img/ogp.jpg" />
<meta property="og:image:secure_url" content="img/ogp.jpg" /> <meta property="og:image:secure_url" content="img/ogp.jpg" />
<meta property="og:image:type" content="image/jpeg" /> <meta property="og:image:type" content="image/jpeg" />
<meta property="og:image:width" content="344" /> <meta property="og:image:width" content="344" />
<meta property="og:image:height" content="247" /> <meta property="og:image:height" content="247" />
<meta property="og:image:alt" content="A dog on a cloud." /> <meta property="og:image:alt" content="Conbook Art Credits" />
<meta property="og:title" content="Conbook Art Credits" /> <meta property="og:title" content="Conbook Art Credits" />
<meta property="og:description" content="Submit Artist Credits to the Eurofurence Conbook Team" /> <meta property="og:description" content="Submit Artist Credits to the Eurofurence Conbook Team" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -143,55 +151,75 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset
</head> </head>
<body> <body>
<header> <header>
<h1>Conbook Artist Credit Form</h1> <h1>Conbook Art Submission Form</h1>
<p> <p>
Thank you for submitting art to the Eurofurence Conbook. In the event of your art making an appearance in the book, your are eligible to an entry in the artist credits.<br /> A place for you to submit art to the Eurofurence Conbook and provide crediting information.<br />
Edit your entry below before <strong>the end of July</strong> to make sure you are credited appropriately. <span uk-icon="clock"></span> Deadline: <strong>End of July</strong>!
</p> </p>
<p><span uk-icon="image"></span> Please make sure your images are large enough for DIN A4 printing. Don't worry about cropping or aspect ratio, we will adjust them into the layout ourselves.</p>
<p>Bookmark this page or save the following url to be able to edit your entry later:</p>
<div class="uk-width-1-1 uk-grid-collapse" uk-grid>
<div class="uk-width-3-4@m">
<input type="text" id="bookmark" disabled value="<?= $bookmark ?>" class="uk-input" />
</div>
<div class="uk-width-1-4@m">
<button type="button" id="copyUrl" class="uk-button uk-button-primary uk-width-1-1" title="✅ Copied">Copy to Clipboard</button>
</div>
</div>
</header> </header>
<main class="uk-margin"> <main>
<hr /> <section>
<p>Please make sure your profile image is large enough for print. Don't worry about cropping or aspect ratio, it will be adjusted into the layout by us.</p> <h3>Personal Link</h3>
<section class="uk-child-width-1-2@m" uk-grid> <p>Bookmark or save it to edit your entry at a later time. Do not share it, unless with people you want to enable editing your information:</p>
<div> <div class="uk-width-1-1 uk-grid-collapse" uk-grid>
<div class="js-upload uk-placeholder uk-margin-top uk-position-relative"> <div class="uk-width-3-4@m">
<div id="img"></div> <input type="text" id="bookmark" disabled value="<?= $bookmark ?>" class="uk-input" />
<!-- <img id="img" src="" alt="No image uploaded yet" /> --> </div>
<span uk-icon="icon: cloud-upload"></span> <div class="uk-width-1-4@m">
<span class="uk-text-middle">Upload Your Profile Image<br />PNG, JPG or GIF<br />min. 500 x 500 @ 300 dpi<br /></span> <button type="button" id="copyUrl" class="uk-button uk-button-primary uk-width-1-1" title="✅ Copied">Copy to Clipboard</button>
<div uk-form-custom> </div>
<input type="file"> </div>
<span class="uk-link">SELECT FILE</span> </section>
<section>
<progress id="js-progressbar" class="uk-progress" value="0" max="100" hidden></progress>
<div class="uk-child-width-1-2@m uk-grid-small uk-grid-match" uk-grid>
<div>
<h3>Your Credits Entry</h3>
<div class="upload-avatar uk-placeholder uk-position-relative">
<div id="img"></div>
<span uk-icon="icon: cloud-upload"></span>
<span class="uk-text-middle">Upload Your Profile Image<br />PNG, JPG or GIF<br />min. 500 x 500 @ 300 dpi<br /></span>
<div uk-form-custom>
<input type="file">
<span class="uk-link">SELECT FILE</span>
</div>
</div>
<form action="" method="POST">
<input type="text" name="name" id="name" maxlength="80" value="<?= $data['name'] ?>" placeholder="Your nickname (max. 80 characters)" class="uk-input" required />
<input type="text" name="url" id="url" maxlength="256" value="<?= $data['url'] ?>" placeholder="Your gallery link / homepage (max. 256 characters)" class="uk-input uk-margin-top" required />
<input type="text" name="desc" id="desc" maxlength="400" value="<?= $data['desc'] ?>" placeholder="Your description (max. 400 characters)" class="uk-input uk-margin-top" required />
<input type="submit" value="Save" class="uk-button uk-button-primary uk-margin-top" />
</form>
</div>
<div>
<h3>Your Contribution</h3>
<div class="height-100-75 upload-gallery uk-placeholder uk-text-center">
<span uk-icon="icon: cloud-upload"></span>
<span class="uk-text-middle">Drop Your Contribution Here<br />PNG, PSD, TIFF, JPG or GIF<br />highest possible resolution, ideally 300 dpi<br /></span>
<div uk-form-custom>
<input type="file">
<span class="uk-link">SELECT FILE</span>
</div>
<ul id="gallery" class="uk-text-left">
<?php foreach ($gallery as $g) {
// echo "<li>$g <a href=\"$bookmark&delete=$g\">[delete]</a></li>";
echo "<li>$g </li>";
}
?>
</ul>
</div> </div>
</div> </div>
<progress id="js-progressbar" class="uk-progress" value="0" max="100" hidden></progress>
</div>
<div>
<form action="" method="POST">
<input type="text" name="name" id="name" maxlength="80" value="<?= $name ?>" placeholder="Your nickname (max. 80 characters)" class="uk-input uk-margin-top" />
<input type="text" name="url" id="url" maxlength="256" value="<?= $url ?>" placeholder="Your gallery link / homepage (max. 256 characters)" class="uk-input uk-margin-top" />
<input type="text" name="desc" id="desc" maxlength="400" value="<?= $desc ?>" placeholder="Your description (max. 400 characters)" class="uk-input uk-margin-top" />
<input type="submit" value="Save" class="uk-button uk-button-primary uk-margin-top" />
</form>
</div> </div>
</section> </section>
</main> </main>
<footer> <footer>
<hr /> <p>Your input is being stored until September, then wiped. If you need assistance, please contact <a href="https://help.eurofurence.org/contact/conbook" target="_blank">The Conbook Department</a>.</p>
<p>If you need assistance, please contact <a href="https://help.eurofurence.org/contact/conbook" target="_blank">The Conbook Department</a>.</p>
</footer> </footer>
<script> <script>
@ -215,17 +243,17 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset
?> ?>
</script> </script>
<?php if ($img !== '') { ?> <?php if ($data['img'] !== '') { ?>
<script> <script>
document.getElementById('img').style.backgroundImage = 'url(uppies/<?= $img ?>?' + new Date().getTime() + ')'; document.getElementById('img').style.backgroundImage = 'url(<?= $data['img'] ?>?' + new Date().getTime() + ')';
</script> </script>
<?php } ?> <?php } ?>
<script> <script>
var bar = document.getElementById('js-progressbar'); var bar = document.getElementById('js-progressbar');
UIkit.upload('.js-upload', { UIkit.upload('.upload-avatar', {
url: '<?= $bookmark ?>&upload', url: '<?= $bookmark ?>&upload=avatar',
loadStart: function (e) { loadStart: function (e) {
bar.removeAttribute('hidden'); bar.removeAttribute('hidden');
@ -245,7 +273,6 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset
completeAll: function (r) { completeAll: function (r) {
if (r.status == 200) { if (r.status == 200) {
console.log(r);
document.getElementById('img').style.backgroundImage = 'url(' + r.responseText + '?' + new Date().getTime() + ')'; document.getElementById('img').style.backgroundImage = 'url(' + r.responseText + '?' + new Date().getTime() + ')';
UIkit.notification('✅ Image saved', 'success'); UIkit.notification('✅ Image saved', 'success');
} }
@ -258,6 +285,50 @@ else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['desc']) && isset
}, 1000); }, 1000);
} }
}); });
UIkit.upload('.upload-gallery', {
url: '<?= $bookmark ?>&upload=gallery',
multiple: true,
loadStart: function (e) {
bar.removeAttribute('hidden');
bar.max = e.total;
bar.value = e.loaded;
},
progress: function (e) {
bar.max = e.total;
bar.value = e.loaded;
},
loadEnd: function (e) {
bar.max = e.total;
bar.value = e.loaded;
},
completeAll: function (r) {
if (r.status == 200) {
const li = document.createElement('li');
li.innerText = r.responseText + ' ';
// const a = document.createElement('a');
// a.href="<?= $bookmark ?>&delete=" + r.responseText;
// a.innerText = '[delete]';
// li.appendChild(a);
document.getElementById('gallery').appendChild(li);
UIkit.notification('✅ Image saved', 'success');
}
else {
UIkit.notification(r.responseText, 'danger');
}
setTimeout(function () {
bar.setAttribute('hidden', 'hidden');
}, 1000);
}
});
</script> </script>
</body> </body>
</html> </html>

View File

@ -21,10 +21,7 @@ class Storage {
$db->exec("CREATE TABLE cache ( $db->exec("CREATE TABLE cache (
id TEXT NOT NULL UNIQUE, id TEXT NOT NULL UNIQUE,
mod DATATIME DEFAULT (DATETIME('now', 'localtime')), mod DATATIME DEFAULT (DATETIME('now', 'localtime')),
img TEXT, data TEXT,
name TEXT,
url TEXT,
desc TEXT,
PRIMARY KEY(id) PRIMARY KEY(id)
);"); );");
} }
@ -38,7 +35,7 @@ class Storage {
*/ */
static public function get(string $id): array | bool { static public function get(string $id): array | bool {
$db = Storage::init(); $db = Storage::init();
$stmt = $db->prepare("SELECT name, url, desc, img FROM cache WHERE id=?;"); $stmt = $db->prepare("SELECT data FROM cache WHERE id=?;");
if (!$stmt || !$stmt->bindValue(1, $id, SQLITE3_TEXT)) { if (!$stmt || !$stmt->bindValue(1, $id, SQLITE3_TEXT)) {
throw new Exception($db->lastErrorMsg()); throw new Exception($db->lastErrorMsg());
return false; return false;
@ -49,9 +46,9 @@ class Storage {
return false; return false;
} }
while ($row = $cur->fetchArray()) { while ($row = $cur->fetchArray()) {
return ['img' => $row['img'], 'name' => $row['name'], 'url' => $row['url'], 'desc' => $row['desc']]; return json_decode($row['data'], true);
} }
return ['img' => '', 'name' => '', 'url' => '', 'desc' => '']; return [];
} }
/** /**
@ -61,7 +58,7 @@ class Storage {
static public function getAll(): array { static public function getAll(): array {
$db = Storage::init(); $db = Storage::init();
$ret = []; $ret = [];
$stmt = $db->prepare("SELECT id, img, name, url, desc FROM cache;"); $stmt = $db->prepare("SELECT id, data FROM cache;");
if (!$stmt) { if (!$stmt) {
throw new Exception($db->lastErrorMsg()); throw new Exception($db->lastErrorMsg());
return $ret; return $ret;
@ -72,7 +69,7 @@ class Storage {
return $ret; return $ret;
} }
while ($row = $cur->fetchArray()) { while ($row = $cur->fetchArray()) {
$ret[$row['id']] = [$row['img'], $row['name'], $row['url'], $row['desc']]; $ret[$row['id']] = json_decode($row['data'], true);
} }
return $ret; return $ret;
} }
@ -83,20 +80,15 @@ class Storage {
* @param array $data JSON-serializable data array to write to database. * @param array $data JSON-serializable data array to write to database.
* @return bool Success indicator. * @return bool Success indicator.
*/ */
static public function set_data(string $id, string $name, string $url, string $desc): bool { static public function set(string $id, array $data): bool {
$db = Storage::init(); $db = Storage::init();
$stmt = $db->prepare("INSERT INTO cache (id, name, url, desc) VALUES(?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name=excluded.name, url=excluded.url, desc=excluded.desc, mod=excluded.mod;"); $jdata = json_encode($data);
if (!$stmt || !$stmt->bindValue(1, $id, SQLITE3_TEXT) || !$stmt->bindValue(2, $name, SQLITE3_TEXT) || !$stmt->bindValue(3, $url, SQLITE3_TEXT)|| !$stmt->bindValue(4, $desc, SQLITE3_TEXT) || !$stmt->execute()) { if ($jdata === false) {
throw new Exception($db->lastErrorMsg()); throw new Exception(json_last_error_msg());
return false; return false;
} }
return true; $stmt = $db->prepare("INSERT INTO cache (id, data) VALUES(?, ?) ON CONFLICT(id) DO UPDATE SET data=excluded.data, mod=excluded.mod;");
} if (!$stmt || !$stmt->bindValue(1, $id, SQLITE3_TEXT) || !$stmt->bindValue(2, $jdata, SQLITE3_TEXT) || !$stmt->execute()) {
static public function set_img(string $id, string $img): bool {
$db = Storage::init();
$stmt = $db->prepare("INSERT INTO cache (id, img) VALUES(?, ?) ON CONFLICT(id) DO UPDATE SET img=excluded.img, mod=excluded.mod;");
if (!$stmt || !$stmt->bindValue(1, $id, SQLITE3_TEXT) || !$stmt->bindValue(2, $img, SQLITE3_TEXT) || !$stmt->execute()) {
throw new Exception($db->lastErrorMsg()); throw new Exception($db->lastErrorMsg());
return false; return false;
} }