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>
This commit is contained in:
2026-07-09 00:03:01 +02:00
parent b5ef4ac0cb
commit f7d2de355e
14 changed files with 698 additions and 21 deletions
+13 -1
View File
@@ -44,6 +44,12 @@
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#twofa" role="tab" aria-controls="custom-tabs-four-settings" aria-selected="false">2FA <i class="fa fa-key"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#applink" role="tab" aria-controls="custom-tabs-four-settings" aria-selected="false">App Link <i class="fas fa-mobile-alt"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="pill" href="#bluetooth" role="tab" aria-controls="custom-tabs-four-settings" aria-selected="false">Bluetooth <i class="fab fa-bluetooth-b"></i></a>
</li>
</ul>
</div>
<div class="card-body">
@@ -88,7 +94,13 @@
<?php include BASE_PATH . '/forms/static/bitcoin.php'; ?>
</div>
<div class="tab-pane fade" id="twofa" role="tabpanel" aria-labelledby="custom-tabs-four-profile-tab">
<?php include BASE_PATH . '/forms/static/2fa.php'; ?>
<?php include BASE_PATH . '/forms/static/2fa.php'; ?>
</div>
<div class="tab-pane fade" id="applink" role="tabpanel" aria-labelledby="custom-tabs-four-profile-tab">
<?php include BASE_PATH . '/forms/static/applink.php'; ?>
</div>
<div class="tab-pane fade" id="bluetooth" role="tabpanel" aria-labelledby="custom-tabs-four-profile-tab">
<?php include BASE_PATH . '/forms/static/bluetooth.php'; ?>
</div>
</div>
</div>
+8
View File
@@ -88,6 +88,14 @@ if (QRCODE_GENERATOR === "internal-chillerlan.qrcode") {
<option value="eps">EPS</option>
</select>
</div>
<div class="col-sm-4">
<div class="form-group">
<label for="frame_text">Frame text</label>
<input type="text" name="frame_text" value="" placeholder="e.g. Scan me" maxlength="60" class="form-control" id="frame_text">
<small class="form-text text-muted">Optional label rendered below the code. Only applies to PNG/JPEG/GIF, not SVG/EPS.</small>
</div>
</div>
</div>
</div>
+73
View File
@@ -0,0 +1,73 @@
<form class="form" action="static_qrcode.php?type=applink" method="post" id="static_form" enctype="multipart/form-data">
<?php echo csrf_field(); ?>
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
<!-- Input forms -->
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-6 col-md-3">
<div class="form-group">
<label>Platform *</label>
<select name="platform" id="applink-platform" class="form-control">
<option value="android" selected>Android (intent link)</option>
<option value="generic">Generic (custom scheme)</option>
</select>
</div>
</div>
<div class="col-6 col-md-3">
<div class="form-group">
<label>Scheme *</label>
<input type="text" name="scheme" value="" placeholder="myapp" class="form-control">
</div>
</div>
<div class="col-6 col-md-3">
<div class="form-group">
<label>Path *</label>
<input type="text" name="path" value="" placeholder="open?ref=123" class="form-control">
</div>
</div>
<div class="col-6 col-md-3" id="applink-package-group">
<div class="form-group">
<label>Android package *</label>
<input type="text" name="package" value="" placeholder="com.example.app" class="form-control">
</div>
</div>
<div class="col-6 col-md-3" id="applink-fallback-group">
<div class="form-group">
<label>Fallback URL</label>
<input type="text" name="fallback_url" value="" placeholder="https://play.google.com/store/apps/details?id=..." class="form-control">
</div>
</div>
</div>
</div>
<script>
(function () {
var platformSelect = document.getElementById('applink-platform');
var packageGroup = document.getElementById('applink-package-group');
var fallbackGroup = document.getElementById('applink-fallback-group');
function updateVisibility() {
var isAndroid = platformSelect.value === 'android';
packageGroup.style.display = isAndroid ? '' : 'none';
fallbackGroup.style.display = isAndroid ? '' : 'none';
}
platformSelect.addEventListener('change', updateVisibility);
updateVisibility();
})();
</script>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-6 col-md-3">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
+37
View File
@@ -0,0 +1,37 @@
<form class="form" action="static_qrcode.php?type=bluetooth" method="post" id="static_form" enctype="multipart/form-data">
<?php echo csrf_field(); ?>
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
<!-- Input forms -->
<div class="col-sm-12 mb-2">
<small class="form-text text-muted mb-2">
There is no OS-native "scan to pair" standard for Bluetooth like there is for Wifi, so this
just encodes the device name and address for reference - whoever scans it still pairs
manually via their Bluetooth settings.
</small>
<div class="row">
<div class="col-6 col-md-3">
<div class="form-group">
<label>Device name *</label>
<input type="text" name="device_name" value="" placeholder="" class="form-control">
</div>
</div>
<div class="col-6 col-md-3">
<div class="form-group">
<label>MAC address *</label>
<input type="text" name="mac_address" value="" placeholder="AA:BB:CC:DD:EE:FF" class="form-control">
</div>
</div>
</div>
</div>
<div class="col-sm-12 mb-2">
<div class="row">
<div class="col-6 col-md-3">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
+3
View File
@@ -89,6 +89,9 @@
<?php endif; ?>
<!-- DOWNLOAD -->
<a href="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>&download=1" class="btn btn-primary"><i class="fa fa-download"></i></a>
<!-- COPY TO CLIPBOARD -->
<button type="button" class="btn btn-secondary copy-qr-btn" data-qr-src="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>" title="Copy image to clipboard"><i class="fa fa-copy"></i></button>
</td>
</tr>
<?php endforeach; ?>
+3
View File
@@ -85,6 +85,9 @@
<?php endif; ?>
<!-- DOWNLOAD -->
<a href="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>&download=1" class="btn btn-primary"><i class="fa fa-download"></i></a>
<!-- COPY TO CLIPBOARD -->
<button type="button" class="btn btn-secondary copy-qr-btn" data-qr-src="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>" title="Copy image to clipboard"><i class="fa fa-copy"></i></button>
</td>
</tr>
<?php endforeach; ?>