On page load, at most three locales are loaded: user locale
from Accept-Language, the default locale (if they aren't the
same), and the template locale. Using function getIntlString(),
if the user locale doesn't have a string, the string is fetched
from the default locale (if they aren't the same), and then,
finally, if neither the user or the default locale have the string,
it is taken from the template (which only contains string labels).
The lang attribute is set when possible.
Reviewed-on: #26 (https://code.antopie.org/miraty/libreqr/pulls/26)
Co-authored-by: Denise <denisebitca@42l.fr>
Co-committed-by: Denise <denisebitca@42l.fr>
[80ee070168]
338 lines
11 KiB
PHP
Executable file
338 lines
11 KiB
PHP
Executable file
<?php // This file is part of LibreQR, which is distributed under the GNU AGPLv3+ license
|
|
|
|
use Endroid\QrCode\Builder\Builder;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelLow;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelMedium;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelQuartile;
|
|
use Endroid\QrCode\ErrorCorrectionLevel\ErrorCorrectionLevelHigh;
|
|
use Endroid\QrCode\Color\Color;
|
|
|
|
require "config.inc.php";
|
|
require "vendor/autoload.php";
|
|
|
|
define("LIBREQR_VERSION", "2.0.1");
|
|
|
|
// Defines the locale to be used
|
|
$locale = DEFAULT_LOCALE;
|
|
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
|
$clientLocales = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
|
$clientLocales = preg_replace("#[A-Z0-9]|q=|;|-|\.#", "", $clientLocales);
|
|
$clientLocales = explode(',', $clientLocales);
|
|
foreach (array_diff(scandir("locales"), array('..', '.')) as $key => $localeFile)
|
|
$availableLocales[$key] = basename($localeFile, ".php");
|
|
foreach ($clientLocales as $clientLocale) {
|
|
if (in_array($clientLocale, $availableLocales)) {
|
|
$locale = $clientLocale;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
require "locales/" . $locale . ".php";
|
|
$chosen_loc = $loc;
|
|
|
|
if ($locale != DEFAULT_LOCALE) {
|
|
require "locales/" . DEFAULT_LOCALE . ".php";
|
|
$default_loc = $loc;
|
|
} else
|
|
$default_loc = $chosen_loc;
|
|
|
|
require "locales/template.php";
|
|
$template_loc = $loc;
|
|
|
|
// Function to get a specific string from the locale file, fall back on default then template if missing
|
|
function getIntlString(
|
|
$string_label,
|
|
$raw = false
|
|
) {
|
|
global $chosen_loc, $default_loc, $template_loc, $locale;
|
|
|
|
if (array_key_exists($string_label, $chosen_loc))
|
|
return $chosen_loc[$string_label];
|
|
|
|
if ($locale != DEFAULT_LOCALE AND array_key_exists($string_label, $default_loc)) {
|
|
if ($raw)
|
|
return $default_loc[$string_label];
|
|
return "<span lang=\"" . DEFAULT_LOCALE . "\">" . $default_loc[$string_label] . "</span>";
|
|
}
|
|
|
|
if ($raw)
|
|
return $template_loc[$string_label];
|
|
return "<span lang=\"en\">" . $template_loc[$string_label] . "</span>";
|
|
}
|
|
|
|
$params = array(
|
|
"txt" => "",
|
|
"redundancy" => DEFAULT_REDUNDANCY,
|
|
"margin" => DEFAULT_MARGIN,
|
|
"size" => DEFAULT_SIZE,
|
|
"bgColor" => DEFAULT_BGCOLOR,
|
|
"fgColor" => DEFAULT_FGCOLOR,
|
|
);
|
|
|
|
$qrCodeAvailable = NULL;
|
|
|
|
if (
|
|
isset($_POST['txt'])
|
|
AND isset($_POST['redundancy'])
|
|
AND isset($_POST['margin'])
|
|
AND isset($_POST['size'])
|
|
AND isset($_POST['bgColor'])
|
|
AND isset($_POST['fgColor'])
|
|
) {
|
|
|
|
$qrCodeAvailable = true;
|
|
|
|
if (strlen($_POST['txt']) >= 1 AND strlen($_POST['txt']) <= 4096) {
|
|
$params['txt'] = $_POST['txt'];
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for txt");
|
|
}
|
|
|
|
if ($_POST['redundancy'] === "low" OR $_POST['redundancy'] === "medium" OR $_POST['redundancy'] === "quartile" OR $_POST['redundancy'] === "high") {
|
|
$params['redundancy'] = $_POST['redundancy'];
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for redundancy");
|
|
}
|
|
|
|
if (is_numeric($_POST['margin']) AND $_POST['margin'] >= 0 AND $_POST['margin'] <= 1024) {
|
|
$params['margin'] = $_POST['margin'];
|
|
} else if (empty($_POST['margin'])) {
|
|
$params['margin'] = NULL;
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for margin");
|
|
}
|
|
|
|
if (is_numeric($_POST['size']) AND $_POST['size'] >= 21 AND $_POST['size'] <= 4096) {
|
|
$params['size'] = $_POST['size'];
|
|
} else if (empty($_POST['size'])) {
|
|
$params['size'] = NULL;
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for size");
|
|
}
|
|
|
|
if (preg_match("/^#[abcdefABCDEF0-9]{6}$/", $_POST['bgColor'])) {
|
|
$params['bgColor'] = substr($_POST['bgColor'], -6);
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for bgColor");
|
|
}
|
|
|
|
if (preg_match("/^#[abcdefABCDEF0-9]{6}$/", $_POST['fgColor'])) {
|
|
$params['fgColor'] = substr($_POST['fgColor'], -6);
|
|
} else {
|
|
http_response_code(400);
|
|
exit("Wrong value for fgColor");
|
|
}
|
|
|
|
$validFormSubmitted = true;
|
|
|
|
$rgbBgColor = array(
|
|
'r' => hexdec(substr($params['bgColor'],0,2)),
|
|
'g' => hexdec(substr($params['bgColor'],2,2)),
|
|
'b' => hexdec(substr($params['bgColor'],4,2)),
|
|
);
|
|
|
|
$qrCode = Builder::create()
|
|
->data($params['txt']);
|
|
if (!is_null($params['margin']))
|
|
$qrCode->margin($params['margin']);
|
|
if (!is_null($params['size']))
|
|
$qrCode->size($params['size']);
|
|
|
|
if ($params['redundancy'] === "high")
|
|
$qrCode->errorCorrectionLevel(new ErrorCorrectionLevelHigh());
|
|
else if ($params['redundancy'] === "quartile")
|
|
$qrCode->errorCorrectionLevel(new ErrorCorrectionLevelQuartile());
|
|
else if ($params['redundancy'] === "medium")
|
|
$qrCode->errorCorrectionLevel(new ErrorCorrectionLevelMedium());
|
|
else if ($params['redundancy'] === "low")
|
|
$qrCode->errorCorrectionLevel(new ErrorCorrectionLevelLow());
|
|
|
|
$qrCode
|
|
->backgroundColor(new Color(
|
|
$rgbBgColor['r'],
|
|
$rgbBgColor['g'],
|
|
$rgbBgColor['b']
|
|
))
|
|
->foregroundColor(new Color(
|
|
hexdec(substr($params['fgColor'],0,2)),
|
|
hexdec(substr($params['fgColor'],2,2)),
|
|
hexdec(substr($params['fgColor'],4,2))
|
|
));
|
|
|
|
try {
|
|
$result = $qrCode->build();
|
|
} catch (Exception $ex) {
|
|
http_response_code(500);
|
|
$qrCodeAvailable = false;
|
|
error_log("LibreQR encountered an error while generating a QR code: " . $ex);
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="<?= $locale ?>">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>LibreQR · <?= getIntlString('subtitle') ?></title>
|
|
<meta name="description" content="<?= getIntlString('description', raw: true) ?>">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="color-scheme" content="dark light">
|
|
<meta name="application-name" content="LibreQR">
|
|
<meta name="referrer" content="no-referrer">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' data:; style-src 'self'; form-action 'self';">
|
|
<?php
|
|
require "themes/" . THEME . "/theme.php";
|
|
$colorScheme['theme'] = THEME;
|
|
|
|
$options = array('cache_dir' => 'css/', 'compress' => true);
|
|
$cssFileName = Less_Cache::Get(array("style.less" => ""), $options, $colorScheme);
|
|
?>
|
|
<link rel="stylesheet" media="screen" href="css/<?= $cssFileName ?>">
|
|
<?php
|
|
foreach($themeDimensionsIcons as $dimFav) // Set all icons dimensions
|
|
echo ' <link rel="icon" type="image/png" href="themes/' . THEME . '/icons/' . $dimFav . '.png" sizes="' . $dimFav . 'x' . $dimFav . '">' . "\n";
|
|
?>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<header>
|
|
<a id="linkTitles" href="./">
|
|
<div id="titles">
|
|
<h1>LibreQR</h1>
|
|
<h2><?= getIntlString('subtitle') ?></h2>
|
|
</div>
|
|
</a>
|
|
</header>
|
|
|
|
<form method="post" action="./#output">
|
|
|
|
<div class="param" id="txtParam">
|
|
<details>
|
|
<summary><label for="txt"><?= getIntlString('label_content') ?></label></summary>
|
|
<div class="helpText">
|
|
<?= getIntlString('help_content') ?>
|
|
</div>
|
|
</details>
|
|
<textarea rows="3" required="" id="txt" placeholder="<?= getIntlString('placeholder', raw: true) ?>" name="txt"><?= htmlspecialchars($params['txt']) ?></textarea>
|
|
</div>
|
|
|
|
<div id="sideParams">
|
|
|
|
<div class="param">
|
|
<details>
|
|
<summary><label for="redundancy"><?= getIntlString('label_redundancy') ?></label></summary>
|
|
<p class="helpText">
|
|
<?= getIntlString('help_redundancy') ?>
|
|
</p>
|
|
</details>
|
|
<select id="redundancy" name="redundancy">
|
|
<option <?php if ($params['redundancy'] === "low") echo 'selected="" '; ?>value="low">L - 7%</option>
|
|
<option <?php if ($params['redundancy'] === "medium") echo 'selected="" '; ?>value="medium">M - 15%</option>
|
|
<option <?php if ($params['redundancy'] === "quartile") echo 'selected="" '; ?>value="quartile">Q - 25%</option>
|
|
<option <?php if ($params['redundancy'] === "high") echo 'selected="" '; ?>value="high">H - 30%</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="param">
|
|
<details>
|
|
<summary><label for="margin"><?= getIntlString('label_margin') ?></label></summary>
|
|
<p class="helpText">
|
|
<?= getIntlString('help_margin') ?>
|
|
</p>
|
|
</details>
|
|
<input type="number" id="margin" placeholder="<?= DEFAULT_MARGIN ?>" name="margin" required="" min="0" max="1024" value="<?= htmlspecialchars($params['margin']) ?>">
|
|
</div>
|
|
|
|
<div class="param">
|
|
<details>
|
|
<summary><label for="size"><?= getIntlString('label_size') ?></label></summary>
|
|
<p class="helpText">
|
|
<?= getIntlString('help_size') ?>
|
|
</p>
|
|
</details>
|
|
<input type="number" id="size" placeholder="<?= DEFAULT_SIZE ?>" name="size" required="" min="21" max="4096" value="<?= htmlspecialchars($params['size']) ?>">
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="colors">
|
|
<div class="param">
|
|
<label for="bgColor"><?= getIntlString('label_bgColor') ?></label>
|
|
<input type="color" name="bgColor" id="bgColor" value="#<?= htmlspecialchars($params['bgColor']) ?>">
|
|
</div>
|
|
<div class="param">
|
|
<label for="fgColor"><?= getIntlString('label_fgColor') ?></label>
|
|
<input type="color" name="fgColor" id="fgColor" value="#<?= htmlspecialchars($params['fgColor']) ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="centered">
|
|
<input class="button" type="submit" value="<?= getIntlString('button_create', raw: true) ?>" />
|
|
</div>
|
|
|
|
</form>
|
|
|
|
<?php
|
|
|
|
if ($qrCodeAvailable) {
|
|
$dataUri = $result->getDataUri();
|
|
|
|
$qrSize = $params['size'] + 2 * $params['margin'];
|
|
|
|
?>
|
|
|
|
<section id="output">
|
|
<div class="centered" id="downloadQR">
|
|
<a href="<?= $dataUri ?>" class="button" download="<?= htmlspecialchars($params['txt']); ?>.png"><?= getIntlString('button_download') ?></a>
|
|
</div>
|
|
|
|
<div class="centered" id="showOnlyQR">
|
|
<a title="<?= getIntlString('title_showOnlyQR', raw: true) ?>" href="<?= $dataUri ?>"><img width="<?= $qrSize ?>" height="<?= $qrSize ?>" alt='<?= getIntlString('alt_QR_before', raw: true) ?><?= htmlspecialchars($params['txt']); ?><?= getIntlString('alt_QR_after', raw: true) ?>' id="qrCode"<?php
|
|
|
|
// Compute the difference between the QR code and theme background colors
|
|
$diffLight = abs($rgbBgColor['r']-hexdec(substr($colorScheme['bg-light'],-6,2))) + abs($rgbBgColor['g']-hexdec(substr($colorScheme['bg-light'],-4,2))) + abs($rgbBgColor['b']-hexdec(substr($colorScheme['bg-light'],-2,2)));
|
|
$diffDark = abs($rgbBgColor['r']-hexdec(substr($colorScheme['bg-dark'],-6,2))) + abs($rgbBgColor['g']-hexdec(substr($colorScheme['bg-dark'],-4,2))) + abs($rgbBgColor['b']-hexdec(substr($colorScheme['bg-dark'],-2,2)));
|
|
|
|
// Determine whether a CSS corner is needed to let the user see the margin of the QR code
|
|
$contrastThreshold = 64;
|
|
if ($diffLight < $contrastThreshold)
|
|
echo " class='needLightContrast'";
|
|
if ($diffDark < $contrastThreshold)
|
|
echo " class='needDarkContrast'";
|
|
?> src="<?= $dataUri ?>"></a>
|
|
</div>
|
|
</section>
|
|
|
|
<?php
|
|
} else if ($qrCodeAvailable === false) {
|
|
echo " <p><strong>" . getIntlString('error_generation') . "</strong></p></body></html>";
|
|
}
|
|
?>
|
|
|
|
<footer>
|
|
|
|
<section id="info" class="metaText">
|
|
<?= getIntlString('metaText_qr') ?>
|
|
</section>
|
|
|
|
<?php if (CUSTOM_TEXT_ENABLED) { ?>
|
|
<section class="metaText">
|
|
<?= CUSTOM_TEXT ?>
|
|
</section>
|
|
<?php } ?>
|
|
|
|
<section class="metaText">
|
|
<small><?= getIntlString('metaText_legal') ?></small>
|
|
</section>
|
|
|
|
</footer>
|
|
|
|
</body>
|
|
</html>
|