f7d2de355e
New static qr types: - App Link: Android intent:// links (with package + optional browser fallback) or a generic custom-scheme URI. iOS Universal Links need no special encoding (they're just plain https:// URLs). - Bluetooth: device name + MAC address. Purely informational, since unlike WIFI:/vCard there's no OS-native "scan to pair" convention. SVG export: already worked (format whitelist/dropdown existed since Fase 1), verified rather than reimplemented. Copy-to-clipboard button next to the download button on both qr list tables, using the Clipboard API against a fetched blob. Optional frame text label rendered below the qr code via GD after generation (raster formats only, no-op for svg/eps). Batch CSV upload (batch_qrcode.php): filename,link rows create dynamic qr codes with sane defaults, downloadable as a zip. Required refactoring Qrcode-intchil.php's generation path (previously always redirected/exited via failure()/success(), which can't run in a loop) into a private renderAndStore() that throws instead, shared by addQrcode() and the new addQrcodeBatch(). Qrcode.php's addQrcodeBatch() is a separate, deliberately duplicated implementation instead, since its generation logic is small enough that duplication carries less risk than refactoring the working external-API code path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
207 lines
6.4 KiB
PHP
207 lines
6.4 KiB
PHP
<?php
|
|
require_once 'config/config.php';
|
|
|
|
if (QRCODE_GENERATOR === "external-api.qrserver.com") {
|
|
require_once BASE_PATH . '/lib/Qrcode/Qrcode.php';
|
|
}
|
|
|
|
if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
|
|
require_once BASE_PATH . '/lib/Qrcode/Qrcode-intchil.php';
|
|
}
|
|
|
|
|
|
class DynamicQrcode {
|
|
private Qrcode $qrcode_instance;
|
|
/**
|
|
*
|
|
*/
|
|
public function __construct() {
|
|
$this->qrcode_instance = new Qrcode("dynamic");
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function __destruct() {
|
|
}
|
|
|
|
/**
|
|
* Set friendly columns names to order tables entries
|
|
* N.B. This function is called to generate the "list all" table
|
|
*/
|
|
public function setOrderingValues()
|
|
{
|
|
$ordering = [
|
|
'id' => 'ID',
|
|
'id_owner' => 'Owner',
|
|
'filename' => 'File Name',
|
|
'identifier' => 'Identifier',
|
|
'link' => 'Link',
|
|
'qrcode' => 'Qr Code',
|
|
'created_at' => 'Created at',
|
|
'updated_at' => 'Updated at'
|
|
];
|
|
|
|
return $ordering;
|
|
}
|
|
|
|
public function getQrcode($id) {
|
|
return $this->qrcode_instance->getQrcode($id);
|
|
}
|
|
|
|
/**
|
|
* Add qr code
|
|
* Check out http://goqr.me/api/ for more information
|
|
* We save the file obtained with the chosen name and in the selected folder
|
|
* We save into db the url of qrcode image
|
|
*/
|
|
public function addQrcode($input_data) {
|
|
$this->validateLink($input_data['link'] ?? '');
|
|
|
|
if($input_data['id_owner'] != "")
|
|
$data_to_db['id_owner'] = $input_data['id_owner'];
|
|
else
|
|
$data_to_db['id_owner'] = NULL;
|
|
|
|
$data_to_db['filename'] = htmlspecialchars($input_data['filename'], ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['created_at'] = date('Y-m-d H:i:s');
|
|
$data_to_db['link'] = htmlspecialchars($input_data['link'], ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['created_by'] = $_SESSION['user_id'];
|
|
$data_to_db['format'] = $input_data['format'];
|
|
$data_to_db['identifier'] = randomString(rand(5,8));
|
|
$data_to_db['qrcode'] = $data_to_db['filename'].'.'.$data_to_db['format'];
|
|
|
|
$data_to_qrcode = READ_PATH.$data_to_db['identifier'];
|
|
|
|
$this->qrcode_instance->addQrcode($input_data, $data_to_db, $data_to_qrcode);
|
|
}
|
|
|
|
/**
|
|
* Batch-safe variant used by batch_qrcode.php: creates one dynamic qr code from a
|
|
* CSV row (filename + link) with sane defaults, returning a result array
|
|
* (['ok' => bool, 'id'|'error' => ...]) instead of redirecting/exiting.
|
|
*/
|
|
public function addQrcodeBatchRow($filename, $link, $id_owner) {
|
|
$filename = trim((string) $filename);
|
|
$link = trim((string) $link);
|
|
|
|
if ($filename === '') {
|
|
return ['ok' => false, 'error' => 'Filename is required.'];
|
|
}
|
|
|
|
if ($link === '' || strlen($link) > 500) {
|
|
return ['ok' => false, 'error' => 'Link is required and must be at most 500 characters.'];
|
|
}
|
|
|
|
$data_to_db['id_owner'] = $id_owner !== '' ? $id_owner : NULL;
|
|
$data_to_db['filename'] = htmlspecialchars($filename, ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['created_at'] = date('Y-m-d H:i:s');
|
|
$data_to_db['link'] = htmlspecialchars($link, ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['created_by'] = $_SESSION['user_id'];
|
|
$data_to_db['format'] = 'png';
|
|
$data_to_db['identifier'] = randomString(rand(5, 8));
|
|
$data_to_db['qrcode'] = $data_to_db['filename'].'.'.$data_to_db['format'];
|
|
|
|
$data_to_qrcode = READ_PATH.$data_to_db['identifier'];
|
|
|
|
$input_data = [
|
|
'level' => 'L',
|
|
'size' => 200,
|
|
'foreground' => '#000000',
|
|
'background' => '#ffffff',
|
|
'frame_text' => '',
|
|
];
|
|
|
|
return $this->qrcode_instance->addQrcodeBatch($input_data, $data_to_db, $data_to_qrcode);
|
|
}
|
|
|
|
/**
|
|
* Edit qr code
|
|
*
|
|
*/
|
|
public function editQrcode($input_data) {
|
|
$this->validateLink($input_data['link'] ?? '');
|
|
|
|
if($input_data['id_owner'] != "")
|
|
$data_to_db['id_owner'] = $input_data['id_owner'];
|
|
else
|
|
$data_to_db['id_owner'] = NULL;
|
|
$data_to_db['filename'] = htmlspecialchars($input_data['filename'], ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['created_at'] = date('Y-m-d H:i:s');
|
|
$data_to_db['link'] = htmlspecialchars($input_data['link'], ENT_QUOTES, 'UTF-8');
|
|
$data_to_db['state'] = $input_data['state'];
|
|
|
|
$this->qrcode_instance->editQrcode($input_data, $data_to_db);
|
|
}
|
|
|
|
|
|
/**
|
|
* Delete qr code
|
|
*
|
|
*/
|
|
public function deleteQrcode($id, $async = false) {
|
|
if($_SESSION['type'] === "super") {
|
|
$this->qrcode_instance->deleteQrcode($id, $async);
|
|
} else if ($_SESSION['type'] === "admin") {
|
|
$qrcode = $this->getQrcode($id);
|
|
|
|
if(!isset($qrcode["id_owner"]))
|
|
$this->failure("You cannot delete this qrcode");
|
|
|
|
require_once BASE_PATH . '/lib/Users/Users.php';
|
|
$users = new Users();
|
|
$user = $users->getUser($_SESSION['user_id']);
|
|
|
|
if($user["id"] === $qrcode["id_owner"])
|
|
$this->qrcode_instance->deleteQrcode($id, $async);
|
|
else
|
|
$this->failure("You cannot delete this qrcode because it's of another user");
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Server-side validatie van de redirect-link (verplicht, max. 500 tekens per kolomdefinitie).
|
|
*/
|
|
private function validateLink($link) {
|
|
$link = trim((string) $link);
|
|
|
|
if ($link === '' || strlen($link) > 500) {
|
|
$this->failure('Link is required and must be at most 500 characters.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flash message Failure process
|
|
*/
|
|
private function failure($message) {
|
|
$_SESSION['failure'] = $message;
|
|
header('Location: dynamic_qrcodes.php');
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Flash message Success process
|
|
*/
|
|
private function success($message) {
|
|
$_SESSION['success'] = $message;
|
|
header('Location: dynamic_qrcodes.php');
|
|
exit();
|
|
}
|
|
|
|
/**
|
|
* Flash message Info process
|
|
*/
|
|
private function info($message) {
|
|
$_SESSION['info'] = $message;
|
|
header('Location: dynamic_qrcodes.php');
|
|
exit();
|
|
}
|
|
|
|
public function debug($data) {
|
|
echo '<pre>' . var_export($data, true) . '</pre>';
|
|
exit();
|
|
}
|
|
}
|
|
?>
|