Files
QRForge-selfhosted/src/lib/DynamicQrcode/DynamicQrcode.php
T
dillard f7d2de355e Fase 3 v1: new qr types, svg export, clipboard, frame text, batch CSV
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>
2026-07-09 00:03:01 +02:00

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();
}
}
?>