1
0
mirror of https://github.com/24eme/signaturepdf.git synced 2023-08-25 09:33:08 +02:00

Merge branch 'sharing'

This commit is contained in:
Vincent LAURENT 2022-04-01 18:00:18 +02:00
commit e2327a0edc
10 changed files with 699 additions and 44 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ package-lock.json
package.json
node_modules
tests/downloads/
config/config.ini

View File

@ -10,13 +10,13 @@ RUN apt update && apt install -y gettext-base librsvg2-bin pdftk imagemagick pot
COPY . /usr/local/signaturepdf
RUN chown -R www-data:www-data /usr/local/signaturepdf && chmod 750 -R /usr/local/signaturepdf && \
envsubst < /usr/local/signaturepdf/php.ini > /usr/local/etc/php/conf.d/uploads.ini && \
envsubst < /usr/local/signaturepdf/apache.conf > /etc/apache2/sites-available/signaturepdf.conf && \
envsubst < /usr/local/signaturepdf/config/php.ini > /usr/local/etc/php/conf.d/uploads.ini && \
envsubst < /usr/local/signaturepdf/config/apache.conf > /etc/apache2/sites-available/signaturepdf.conf && \
a2enmod rewrite && a2ensite signaturepdf
WORKDIR /usr/local/signaturepdf
CMD envsubst < /usr/local/signaturepdf/apache.conf > /etc/apache2/sites-available/signaturepdf.conf && \
envsubst < /usr/local/signaturepdf/php.ini > /usr/local/etc/php/conf.d/uploads.ini && \
CMD envsubst < /usr/local/signaturepdf/config/apache.conf > /etc/apache2/sites-available/signaturepdf.conf && \
envsubst < /usr/local/signaturepdf/config/php.ini > /usr/local/etc/php/conf.d/uploads.ini && \
chown -R www-data:www-data /usr/local/signaturepdf && chmod 750 -R /usr/local/signaturepdf && \
apache2-foreground

153
app.php
View File

@ -10,6 +10,11 @@ $f3->set('XFRAME', null); // Allow use in an iframe
$f3->set('ROOT', __DIR__);
$f3->set('UI', $f3->get('ROOT')."/templates/");
$f3->set('UPLOADS', sys_get_temp_dir()."/");
$f3->config(__DIR__.'/config/config.ini');
if($f3->get('PDF_STORAGE_PATH') && !preg_match('|/$|', $f3->get('PDF_STORAGE_PATH'))) {
$f3->set('PDF_STORAGE_PATH', $f3->get('PDF_STORAGE_PATH').'/');
}
function convertPHPSizeToBytes($sSize)
{
@ -49,9 +54,23 @@ $f3->route('GET /signature',
$f3->set('maxSize', min(array(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize')))));
$f3->set('maxPage', ini_get('max_file_uploads') - 1);
if(!$f3->get('PDF_STORAGE_PATH')) {
$f3->set('noSharingMode', true);
}
echo View::instance()->render('signature.html.php');
}
);
$f3->route('GET /signature/@hash',
function($f3, $param) {
$f3->set('hash', $param['hash']);
$f3->set('maxSize', min(array(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize')))));
$f3->set('maxPage', ini_get('max_file_uploads') - 1);
echo View::instance()->render('signature.html.php');
}
);
$f3->route('GET /organization',
function($f3) {
$f3->set('maxSize', min(array(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize')))));
@ -106,7 +125,6 @@ $f3->route('POST /sign',
unlink($tmpfile);
$svgFiles = "";
$files = Web::instance()->receive(function($file,$formFieldName){
if($formFieldName == "pdf" && strpos(Web::instance()->mime($file['tmp_name'], true), 'application/pdf') !== 0) {
$f3->error(403);
@ -139,7 +157,7 @@ $f3->route('POST /sign',
}
shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $tmpfile.'.svg.pdf', $svgFiles));
shell_exec(sprintf("pdftk %s multibackground %s output %s", $tmpfile.'.svg.pdf', $tmpfile.".pdf", $tmpfile.'_signe.pdf'));
shell_exec(sprintf("pdftk %s multistamp %s output %s", $tmpfile.".pdf", $tmpfile.'.svg.pdf', $tmpfile.'_signe.pdf'));
Web::instance()->send($tmpfile.'_signe.pdf', null, 0, TRUE, $filename);
@ -150,6 +168,61 @@ $f3->route('POST /sign',
}
);
$f3->route('POST /share',
function($f3) {
$hash = substr(hash('sha512', uniqid().rand()), 0, 20);
$sharingFolder = $f3->get('PDF_STORAGE_PATH').$hash."/";
$f3->set('UPLOADS', $sharingFolder);
if (!is_dir($f3->get('PDF_STORAGE_PATH'))) {
$f3->error(500, 'Sharing folder doesn\'t exist');
}
if (!is_writable($f3->get('PDF_STORAGE_PATH'))) {
$f3->error(500, 'Sharing folder is not writable');
}
mkdir($sharingFolder);
$filename = "original.pdf";
$tmpfile = tempnam($sharingFolder, date('YmdHis'));
$svgFiles = "";
$files = Web::instance()->receive(function($file,$formFieldName){
if($formFieldName == "pdf" && strpos(Web::instance()->mime($file['tmp_name'], true), 'application/pdf') !== 0) {
$f3->error(403);
}
if($formFieldName == "svg" && strpos(Web::instance()->mime($file['tmp_name'], true), 'image/svg+xml') !== 0) {
$f3->error(403);
}
return true;
}, false, function($fileBaseName, $formFieldName) use ($tmpfile, $filename, $sharingFolder, &$svgFiles) {
if($formFieldName == "pdf") {
file_put_contents($sharingFolder."filename.txt", $fileBaseName);
return $filename;
}
if($formFieldName == "svg") {
$svgFiles .= " ".$tmpfile."_".$fileBaseName;
return basename($tmpfile."_".$fileBaseName);
}
});
if(!count($files)) {
$f3->error(403);
}
if(!$svgFiles) {
$f3->error(403);
}
shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $tmpfile.'.svg.pdf', $svgFiles));
if(!$f3->get('DEBUG')) {
array_map('unlink', explode(' ', trim($svgFiles)));
}
$f3->reroute('/signature/'.$hash."#informations");
}
);
$f3->route('POST /organize',
function($f3) {
$filename = null;
@ -184,4 +257,80 @@ $f3->route('POST /organize',
}
);
$f3->route('GET /signature/@hash/pdf',
function($f3) {
$sharingFolder = $f3->get('PDF_STORAGE_PATH').$f3->get('PARAMS.hash');
$files = scandir($sharingFolder);
$originalFile = $sharingFolder.'/original.pdf';
$finalFile = $sharingFolder.'/'.$f3->get('PARAMS.hash').'.pdf';
$filename = $f3->get('PARAMS.hash').'.pdf';
if(file_exists($sharingFolder."/filename.txt")) {
$filename = file_get_contents($sharingFolder."/filename.txt");
}
$layers = [];
foreach($files as $file) {
if(strpos($file, 'svg.pdf') !== false) {
$layers[] = $sharingFolder.'/'.$file;
}
}
if (!$layers) {
Web::instance()->send($originalFile, null, 0, TRUE, $filename);
}
$bufferFile = str_replace('.pdf', '_tmp.pdf', $originalFile);
shell_exec(sprintf("cp %s %s", $originalFile, $finalFile));
foreach($layers as $layer) {
shell_exec(sprintf("pdftk %1\$s multistamp %2\$s output %3\$s && mv %3\$s %1\$s", $finalFile, $layer, $bufferFile));
}
Web::instance()->send($finalFile, null, 0, TRUE, $filename);
}
);
$f3->route('POST /signature/@hash/save',
function($f3) {
$sharingFolder = $f3->get('PDF_STORAGE_PATH').$f3->get('PARAMS.hash').'/';
$f3->set('UPLOADS', $sharingFolder);
$tmpfile = tempnam($sharingFolder, date('YmdHis'));
unlink($tmpfile);
$svgFiles = "";
$files = Web::instance()->receive(function($file,$formFieldName){
if($formFieldName == "svg" && strpos(Web::instance()->mime($file['tmp_name'], true), 'image/svg+xml') !== 0) {
$f3->error(403);
}
return true;
}, false, function($fileBaseName, $formFieldName) use ($f3, $tmpfile, &$svgFiles) {
if($formFieldName == "svg") {
$svgFiles .= " ".$tmpfile."_".$fileBaseName;
return basename($tmpfile."_".$fileBaseName);
}
});
if(!$svgFiles) {
$f3->error(403);
}
shell_exec(sprintf("rsvg-convert -f pdf -o %s %s", $tmpfile.'.svg.pdf', $svgFiles));
if(!$f3->get('DEBUG')) {
array_map('unlink', explode(' ', trim($svgFiles)));
}
$f3->reroute('/signature/'.$f3->get('PARAMS.hash')."#signed");
}
);
$f3->route('GET /signature/@hash/nblayers',
function($f3) {
$files = scandir($f3->get('PDF_STORAGE_PATH').$f3->get('PARAMS.hash'));
$nbLayers = 0;
foreach($files as $file) {
if(strpos($file, 'svg.pdf') !== false) {
$nbLayers++;
}
}
echo $nbLayers;
}
);
return $f3;

View File

@ -0,0 +1,5 @@
[globals]
# Path to which stored pdf to activate the mode of sharing a signature to several.
# To deactivate this mode, simply do not configure it or leave it empty
PDF_STORAGE_PATH=/path/to/folder

View File

@ -23,12 +23,19 @@ var loadPDF = async function(pdfBlob, filename) {
let url = await URL.createObjectURL(pdfBlob);
text_document_name.querySelector('span').innerText = filename;
text_document_name.setAttribute('title', filename);
let dataTransfer = new DataTransfer();
dataTransfer.items.add(new File([pdfBlob], filename, {
type: 'application/pdf'
}));
if(document.getElementById('input_pdf')) {
document.getElementById('input_pdf').files = dataTransfer.files;
}
if(document.getElementById('input_pdf_share')) {
document.getElementById('input_pdf_share').files = dataTransfer.files;
}
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
@ -51,7 +58,7 @@ var loadPDF = async function(pdfBlob, filename) {
var pageIndex = page.pageNumber - 1;
document.getElementById('form_pdf').insertAdjacentHTML('beforeend', '<input name="svg[' + pageIndex + ']" id="data-svg-' + pageIndex + '" type="hidden" value="" />');
document.getElementById('form_block').insertAdjacentHTML('beforeend', '<input name="svg[' + pageIndex + ']" id="data-svg-' + pageIndex + '" type="hidden" value="" />');
document.getElementById('container-pages').insertAdjacentHTML('beforeend', '<div class="position-relative mt-1 ms-1 me-1 d-inline-block" id="canvas-container-' + pageIndex +'"><canvas id="canvas-pdf-'+pageIndex+'" class="shadow-sm canvas-pdf"></canvas><div class="position-absolute top-0 start-0"><canvas id="canvas-edition-'+pageIndex+'"></canvas></div></div>');
var canvasPDF = document.getElementById('canvas-pdf-' + pageIndex);
@ -474,8 +481,21 @@ var addObjectInCanvas = function(canvas, item) {
};
var createAndAddSvgInCanvas = function(canvas, item, x, y, height = null) {
save.removeAttribute('disabled');
save_mobile.removeAttribute('disabled');
if(document.getElementById('save')) {
document.getElementById('save').removeAttribute('disabled');
}
if(document.getElementById('save_mobile')) {
document.getElementById('save_mobile').removeAttribute('disabled');
}
if(document.getElementById('btn_download')) {
document.getElementById('btn_download').classList.remove('btn-outline-dark');
document.getElementById('btn_download').classList.add('btn-outline-secondary');
}
if(document.getElementById('btn_share')) {
document.getElementById('btn_share').classList.remove('btn-outline-dark');
document.getElementById('btn_share').classList.add('btn-outline-secondary');
}
if(!height) {
height = 100;
@ -728,6 +748,7 @@ var createEventsListener = function() {
event.preventDefault();
});
if(document.getElementById('save')) {
document.getElementById('save').addEventListener('click', function(event) {
var dataTransfer = new DataTransfer();
canvasEditions.forEach(function(canvasEdition, index) {
@ -737,6 +758,19 @@ var createEventsListener = function() {
})
document.getElementById('input_svg').files = dataTransfer.files;
});
}
if(document.getElementById('save_share')) {
document.getElementById('save_share').addEventListener('click', function(event) {
var dataTransfer = new DataTransfer();
canvasEditions.forEach(function(canvasEdition, index) {
dataTransfer.items.add(new File([canvasEdition.toSVG()], index+'.svg', {
type: 'image/svg+xml'
}));
})
document.getElementById('input_svg_share').files = dataTransfer.files;
});
}
document.getElementById('save_mobile').addEventListener('click', function(event) {
document.getElementById('save').click();
@ -853,6 +887,13 @@ var createEventsListener = function() {
document.getElementById('btn-zoom-increase').addEventListener('click', function() {
zoomChange(1)
});
if(hash) {
updateNbLayers();
setInterval(function() {
updateNbLayers();
}, 10000);
}
};
var createSignaturePad = function() {
@ -905,6 +946,30 @@ async function uploadFromUrl(url) {
document.getElementById('input_pdf_upload').dispatchEvent(new Event("change"));
}
var modalSharing = function() {
if(window.location.hash == '#informations') {
let modalInformationsEl = document.getElementById('modal-share-informations');
let modalInformations = bootstrap.Modal.getOrCreateInstance(modalInformationsEl);
modalInformations.show();
modalInformationsEl.addEventListener('hidden.bs.modal', function (event) {
if(window.location.hash) {
history.pushState({}, '', window.location.href.replace(/#.*$/, ''));
}
})
}
if(window.location.hash == '#signed') {
let modalSignedEl = document.getElementById('modal-signed');
let modalSigned = bootstrap.Modal.getOrCreateInstance(modalSignedEl);
modalSigned.show();
modalSignedEl.addEventListener('hidden.bs.modal', function (event) {
if(window.location.hash) {
history.pushState({}, '', window.location.href.replace(/#.*$/, ''));
}
})
}
}
var pageUpload = async function() {
document.getElementById('input_pdf_upload').value = '';
document.getElementById('page-upload').classList.remove('d-none');
@ -927,9 +992,26 @@ var pageUpload = async function() {
});
}
var updateNbLayers = function() {
const xhr = new XMLHttpRequest();
xhr.open('GET', '/signature/'+hash+'/nblayers', true);
xhr.onload = function() {
if (xhr.status == 200) {
let nblayers = xhr.response;
document.querySelectorAll('.nblayers').forEach(function(item) {
item.innerHTML = nblayers;
});
document.querySelector('#nblayers_text').classList.remove('d-none');
if(!nblayers) {
document.querySelector('#nblayers_text').classList.add('d-none');
}
}
};
xhr.send();
};
var pageSignature = async function(url) {
let filename = url.replace('/pdf/', '');
document.title = filename + ' - ' + document.title;
modalSharing();
document.getElementById('page-upload').classList.add('d-none');
document.getElementById('page-signature').classList.remove('d-none');
fabric.Textbox.prototype._wordJoiners = /[]/;
@ -946,7 +1028,24 @@ var pageSignature = async function(url) {
fontCaveat = font;
});
let pdfBlob = await getPDFBlobFromCache(url);
let pdfBlob = null;
let filename = url.replace('/pdf/', '');
if(hash) {
var response = await fetch(url);
if(response.status != 200) {
return;
}
pdfBlob = await response.blob();
if(response.headers.get('Content-Disposition').match(/attachment; filename="/)) {
filename = response.headers.get('Content-Disposition').replace(/^[^"]*"/, "").replace(/"[^"]*$/, "");
}
} else {
pdfBlob = await getPDFBlobFromCache(url);
}
document.title = filename + ' - ' + document.title;
if(!pdfBlob) {
document.location = '/signature';
return;
@ -961,6 +1060,14 @@ var pageSignature = async function(url) {
};
(function () {
if(hash) {
pageSignature('/signature/'+hash+'/pdf');
window.addEventListener('hashchange', function() {
window.location.reload();
})
return;
}
if(window.location.hash && window.location.hash.match(/^\#http/)) {
let hashUrl = window.location.hash.replace(/^\#/, '');
pageUpload();

View File

@ -1,9 +1,10 @@
@font-face {
font-family: "bootstrap-icons";
src: url("./fonts/bootstrap-icons.woff2?856008caa5eb66df68595e734e59580d") format("woff2"),
url("./fonts/bootstrap-icons.woff?856008caa5eb66df68595e734e59580d") format("woff");
src: url("./fonts/bootstrap-icons.woff2?524846017b983fc8ded9325d94ed40f3") format("woff2"),
url("./fonts/bootstrap-icons.woff?524846017b983fc8ded9325d94ed40f3") format("woff");
}
.bi::before,
[class^="bi-"]::before,
[class*=" bi-"]::before {
display: inline-block;
@ -18,6 +19,7 @@ url("./fonts/bootstrap-icons.woff?856008caa5eb66df68595e734e59580d") format("wof
-moz-osx-font-smoothing: grayscale;
}
.bi-123::before { content: "\f67f"; }
.bi-alarm-fill::before { content: "\f101"; }
.bi-alarm::before { content: "\f102"; }
.bi-align-bottom::before { content: "\f103"; }
@ -1388,3 +1390,315 @@ url("./fonts/bootstrap-icons.woff?856008caa5eb66df68595e734e59580d") format("wof
.bi-translate::before { content: "\f658"; }
.bi-x-lg::before { content: "\f659"; }
.bi-safe::before { content: "\f65a"; }
.bi-apple::before { content: "\f65b"; }
.bi-microsoft::before { content: "\f65d"; }
.bi-windows::before { content: "\f65e"; }
.bi-behance::before { content: "\f65c"; }
.bi-dribbble::before { content: "\f65f"; }
.bi-line::before { content: "\f660"; }
.bi-medium::before { content: "\f661"; }
.bi-paypal::before { content: "\f662"; }
.bi-pinterest::before { content: "\f663"; }
.bi-signal::before { content: "\f664"; }
.bi-snapchat::before { content: "\f665"; }
.bi-spotify::before { content: "\f666"; }
.bi-stack-overflow::before { content: "\f667"; }
.bi-strava::before { content: "\f668"; }
.bi-wordpress::before { content: "\f669"; }
.bi-vimeo::before { content: "\f66a"; }
.bi-activity::before { content: "\f66b"; }
.bi-easel2-fill::before { content: "\f66c"; }
.bi-easel2::before { content: "\f66d"; }
.bi-easel3-fill::before { content: "\f66e"; }
.bi-easel3::before { content: "\f66f"; }
.bi-fan::before { content: "\f670"; }
.bi-fingerprint::before { content: "\f671"; }
.bi-graph-down-arrow::before { content: "\f672"; }
.bi-graph-up-arrow::before { content: "\f673"; }
.bi-hypnotize::before { content: "\f674"; }
.bi-magic::before { content: "\f675"; }
.bi-person-rolodex::before { content: "\f676"; }
.bi-person-video::before { content: "\f677"; }
.bi-person-video2::before { content: "\f678"; }
.bi-person-video3::before { content: "\f679"; }
.bi-person-workspace::before { content: "\f67a"; }
.bi-radioactive::before { content: "\f67b"; }
.bi-webcam-fill::before { content: "\f67c"; }
.bi-webcam::before { content: "\f67d"; }
.bi-yin-yang::before { content: "\f67e"; }
.bi-bandaid-fill::before { content: "\f680"; }
.bi-bandaid::before { content: "\f681"; }
.bi-bluetooth::before { content: "\f682"; }
.bi-body-text::before { content: "\f683"; }
.bi-boombox::before { content: "\f684"; }
.bi-boxes::before { content: "\f685"; }
.bi-dpad-fill::before { content: "\f686"; }
.bi-dpad::before { content: "\f687"; }
.bi-ear-fill::before { content: "\f688"; }
.bi-ear::before { content: "\f689"; }
.bi-envelope-check-1::before { content: "\f68a"; }
.bi-envelope-check-fill::before { content: "\f68b"; }
.bi-envelope-check::before { content: "\f68c"; }
.bi-envelope-dash-1::before { content: "\f68d"; }
.bi-envelope-dash-fill::before { content: "\f68e"; }
.bi-envelope-dash::before { content: "\f68f"; }
.bi-envelope-exclamation-1::before { content: "\f690"; }
.bi-envelope-exclamation-fill::before { content: "\f691"; }
.bi-envelope-exclamation::before { content: "\f692"; }
.bi-envelope-plus-fill::before { content: "\f693"; }
.bi-envelope-plus::before { content: "\f694"; }
.bi-envelope-slash-1::before { content: "\f695"; }
.bi-envelope-slash-fill::before { content: "\f696"; }
.bi-envelope-slash::before { content: "\f697"; }
.bi-envelope-x-1::before { content: "\f698"; }
.bi-envelope-x-fill::before { content: "\f699"; }
.bi-envelope-x::before { content: "\f69a"; }
.bi-explicit-fill::before { content: "\f69b"; }
.bi-explicit::before { content: "\f69c"; }
.bi-git::before { content: "\f69d"; }
.bi-infinity::before { content: "\f69e"; }
.bi-list-columns-reverse::before { content: "\f69f"; }
.bi-list-columns::before { content: "\f6a0"; }
.bi-meta::before { content: "\f6a1"; }
.bi-mortorboard-fill::before { content: "\f6a2"; }
.bi-mortorboard::before { content: "\f6a3"; }
.bi-nintendo-switch::before { content: "\f6a4"; }
.bi-pc-display-horizontal::before { content: "\f6a5"; }
.bi-pc-display::before { content: "\f6a6"; }
.bi-pc-horizontal::before { content: "\f6a7"; }
.bi-pc::before { content: "\f6a8"; }
.bi-playstation::before { content: "\f6a9"; }
.bi-plus-slash-minus::before { content: "\f6aa"; }
.bi-projector-fill::before { content: "\f6ab"; }
.bi-projector::before { content: "\f6ac"; }
.bi-qr-code-scan::before { content: "\f6ad"; }
.bi-qr-code::before { content: "\f6ae"; }
.bi-quora::before { content: "\f6af"; }
.bi-quote::before { content: "\f6b0"; }
.bi-robot::before { content: "\f6b1"; }
.bi-send-check-fill::before { content: "\f6b2"; }
.bi-send-check::before { content: "\f6b3"; }
.bi-send-dash-fill::before { content: "\f6b4"; }
.bi-send-dash::before { content: "\f6b5"; }
.bi-send-exclamation-1::before { content: "\f6b6"; }
.bi-send-exclamation-fill::before { content: "\f6b7"; }
.bi-send-exclamation::before { content: "\f6b8"; }
.bi-send-fill::before { content: "\f6b9"; }
.bi-send-plus-fill::before { content: "\f6ba"; }
.bi-send-plus::before { content: "\f6bb"; }
.bi-send-slash-fill::before { content: "\f6bc"; }
.bi-send-slash::before { content: "\f6bd"; }
.bi-send-x-fill::before { content: "\f6be"; }
.bi-send-x::before { content: "\f6bf"; }
.bi-send::before { content: "\f6c0"; }
.bi-steam::before { content: "\f6c1"; }
.bi-terminal-dash-1::before { content: "\f6c2"; }
.bi-terminal-dash::before { content: "\f6c3"; }
.bi-terminal-plus::before { content: "\f6c4"; }
.bi-terminal-split::before { content: "\f6c5"; }
.bi-ticket-detailed-fill::before { content: "\f6c6"; }
.bi-ticket-detailed::before { content: "\f6c7"; }
.bi-ticket-fill::before { content: "\f6c8"; }
.bi-ticket-perforated-fill::before { content: "\f6c9"; }
.bi-ticket-perforated::before { content: "\f6ca"; }
.bi-ticket::before { content: "\f6cb"; }
.bi-tiktok::before { content: "\f6cc"; }
.bi-window-dash::before { content: "\f6cd"; }
.bi-window-desktop::before { content: "\f6ce"; }
.bi-window-fullscreen::before { content: "\f6cf"; }
.bi-window-plus::before { content: "\f6d0"; }
.bi-window-split::before { content: "\f6d1"; }
.bi-window-stack::before { content: "\f6d2"; }
.bi-window-x::before { content: "\f6d3"; }
.bi-xbox::before { content: "\f6d4"; }
.bi-ethernet::before { content: "\f6d5"; }
.bi-hdmi-fill::before { content: "\f6d6"; }
.bi-hdmi::before { content: "\f6d7"; }
.bi-usb-c-fill::before { content: "\f6d8"; }
.bi-usb-c::before { content: "\f6d9"; }
.bi-usb-fill::before { content: "\f6da"; }
.bi-usb-plug-fill::before { content: "\f6db"; }
.bi-usb-plug::before { content: "\f6dc"; }
.bi-usb-symbol::before { content: "\f6dd"; }
.bi-usb::before { content: "\f6de"; }
.bi-boombox-fill::before { content: "\f6df"; }
.bi-displayport-1::before { content: "\f6e0"; }
.bi-displayport::before { content: "\f6e1"; }
.bi-gpu-card::before { content: "\f6e2"; }
.bi-memory::before { content: "\f6e3"; }
.bi-modem-fill::before { content: "\f6e4"; }
.bi-modem::before { content: "\f6e5"; }
.bi-motherboard-fill::before { content: "\f6e6"; }
.bi-motherboard::before { content: "\f6e7"; }
.bi-optical-audio-fill::before { content: "\f6e8"; }
.bi-optical-audio::before { content: "\f6e9"; }
.bi-pci-card::before { content: "\f6ea"; }
.bi-router-fill::before { content: "\f6eb"; }
.bi-router::before { content: "\f6ec"; }
.bi-ssd-fill::before { content: "\f6ed"; }
.bi-ssd::before { content: "\f6ee"; }
.bi-thunderbolt-fill::before { content: "\f6ef"; }
.bi-thunderbolt::before { content: "\f6f0"; }
.bi-usb-drive-fill::before { content: "\f6f1"; }
.bi-usb-drive::before { content: "\f6f2"; }
.bi-usb-micro-fill::before { content: "\f6f3"; }
.bi-usb-micro::before { content: "\f6f4"; }
.bi-usb-mini-fill::before { content: "\f6f5"; }
.bi-usb-mini::before { content: "\f6f6"; }
.bi-cloud-haze2::before { content: "\f6f7"; }
.bi-device-hdd-fill::before { content: "\f6f8"; }
.bi-device-hdd::before { content: "\f6f9"; }
.bi-device-ssd-fill::before { content: "\f6fa"; }
.bi-device-ssd::before { content: "\f6fb"; }
.bi-displayport-fill::before { content: "\f6fc"; }
.bi-mortarboard-fill::before { content: "\f6fd"; }
.bi-mortarboard::before { content: "\f6fe"; }
.bi-terminal-x::before { content: "\f6ff"; }
.bi-arrow-through-heart-fill::before { content: "\f700"; }
.bi-arrow-through-heart::before { content: "\f701"; }
.bi-badge-sd-fill::before { content: "\f702"; }
.bi-badge-sd::before { content: "\f703"; }
.bi-bag-heart-fill::before { content: "\f704"; }
.bi-bag-heart::before { content: "\f705"; }
.bi-balloon-fill::before { content: "\f706"; }
.bi-balloon-heart-fill::before { content: "\f707"; }
.bi-balloon-heart::before { content: "\f708"; }
.bi-balloon::before { content: "\f709"; }
.bi-box2-fill::before { content: "\f70a"; }
.bi-box2-heart-fill::before { content: "\f70b"; }
.bi-box2-heart::before { content: "\f70c"; }
.bi-box2::before { content: "\f70d"; }
.bi-braces-asterisk::before { content: "\f70e"; }
.bi-calendar-heart-fill::before { content: "\f70f"; }
.bi-calendar-heart::before { content: "\f710"; }
.bi-calendar2-heart-fill::before { content: "\f711"; }
.bi-calendar2-heart::before { content: "\f712"; }
.bi-chat-heart-fill::before { content: "\f713"; }
.bi-chat-heart::before { content: "\f714"; }
.bi-chat-left-heart-fill::before { content: "\f715"; }
.bi-chat-left-heart::before { content: "\f716"; }
.bi-chat-right-heart-fill::before { content: "\f717"; }
.bi-chat-right-heart::before { content: "\f718"; }
.bi-chat-square-heart-fill::before { content: "\f719"; }
.bi-chat-square-heart::before { content: "\f71a"; }
.bi-clipboard-check-fill::before { content: "\f71b"; }
.bi-clipboard-data-fill::before { content: "\f71c"; }
.bi-clipboard-fill::before { content: "\f71d"; }
.bi-clipboard-heart-fill::before { content: "\f71e"; }
.bi-clipboard-heart::before { content: "\f71f"; }
.bi-clipboard-minus-fill::before { content: "\f720"; }
.bi-clipboard-plus-fill::before { content: "\f721"; }
.bi-clipboard-pulse::before { content: "\f722"; }
.bi-clipboard-x-fill::before { content: "\f723"; }
.bi-clipboard2-check-fill::before { content: "\f724"; }
.bi-clipboard2-check::before { content: "\f725"; }
.bi-clipboard2-data-fill::before { content: "\f726"; }
.bi-clipboard2-data::before { content: "\f727"; }
.bi-clipboard2-fill::before { content: "\f728"; }
.bi-clipboard2-heart-fill::before { content: "\f729"; }
.bi-clipboard2-heart::before { content: "\f72a"; }
.bi-clipboard2-minus-fill::before { content: "\f72b"; }
.bi-clipboard2-minus::before { content: "\f72c"; }
.bi-clipboard2-plus-fill::before { content: "\f72d"; }
.bi-clipboard2-plus::before { content: "\f72e"; }
.bi-clipboard2-pulse-fill::before { content: "\f72f"; }
.bi-clipboard2-pulse::before { content: "\f730"; }
.bi-clipboard2-x-fill::before { content: "\f731"; }
.bi-clipboard2-x::before { content: "\f732"; }
.bi-clipboard2::before { content: "\f733"; }
.bi-emoji-kiss-fill::before { content: "\f734"; }
.bi-emoji-kiss::before { content: "\f735"; }
.bi-envelope-heart-fill::before { content: "\f736"; }
.bi-envelope-heart::before { content: "\f737"; }
.bi-envelope-open-heart-fill::before { content: "\f738"; }
.bi-envelope-open-heart::before { content: "\f739"; }
.bi-envelope-paper-fill::before { content: "\f73a"; }
.bi-envelope-paper-heart-fill::before { content: "\f73b"; }
.bi-envelope-paper-heart::before { content: "\f73c"; }
.bi-envelope-paper::before { content: "\f73d"; }
.bi-filetype-aac::before { content: "\f73e"; }
.bi-filetype-ai::before { content: "\f73f"; }
.bi-filetype-bmp::before { content: "\f740"; }
.bi-filetype-cs::before { content: "\f741"; }
.bi-filetype-css::before { content: "\f742"; }
.bi-filetype-csv::before { content: "\f743"; }
.bi-filetype-doc::before { content: "\f744"; }
.bi-filetype-docx::before { content: "\f745"; }
.bi-filetype-exe::before { content: "\f746"; }
.bi-filetype-gif::before { content: "\f747"; }
.bi-filetype-heic::before { content: "\f748"; }
.bi-filetype-html::before { content: "\f749"; }
.bi-filetype-java::before { content: "\f74a"; }
.bi-filetype-jpg::before { content: "\f74b"; }
.bi-filetype-js::before { content: "\f74c"; }
.bi-filetype-jsx::before { content: "\f74d"; }
.bi-filetype-key::before { content: "\f74e"; }
.bi-filetype-m4p::before { content: "\f74f"; }
.bi-filetype-md::before { content: "\f750"; }
.bi-filetype-mdx::before { content: "\f751"; }
.bi-filetype-mov::before { content: "\f752"; }
.bi-filetype-mp3::before { content: "\f753"; }
.bi-filetype-mp4::before { content: "\f754"; }
.bi-filetype-otf::before { content: "\f755"; }
.bi-filetype-pdf::before { content: "\f756"; }
.bi-filetype-php::before { content: "\f757"; }
.bi-filetype-png::before { content: "\f758"; }
.bi-filetype-ppt-1::before { content: "\f759"; }
.bi-filetype-ppt::before { content: "\f75a"; }
.bi-filetype-psd::before { content: "\f75b"; }
.bi-filetype-py::before { content: "\f75c"; }
.bi-filetype-raw::before { content: "\f75d"; }
.bi-filetype-rb::before { content: "\f75e"; }
.bi-filetype-sass::before { content: "\f75f"; }
.bi-filetype-scss::before { content: "\f760"; }
.bi-filetype-sh::before { content: "\f761"; }
.bi-filetype-svg::before { content: "\f762"; }
.bi-filetype-tiff::before { content: "\f763"; }
.bi-filetype-tsx::before { content: "\f764"; }
.bi-filetype-ttf::before { content: "\f765"; }
.bi-filetype-txt::before { content: "\f766"; }
.bi-filetype-wav::before { content: "\f767"; }
.bi-filetype-woff::before { content: "\f768"; }
.bi-filetype-xls-1::before { content: "\f769"; }
.bi-filetype-xls::before { content: "\f76a"; }
.bi-filetype-xml::before { content: "\f76b"; }
.bi-filetype-yml::before { content: "\f76c"; }
.bi-heart-arrow::before { content: "\f76d"; }
.bi-heart-pulse-fill::before { content: "\f76e"; }
.bi-heart-pulse::before { content: "\f76f"; }
.bi-heartbreak-fill::before { content: "\f770"; }
.bi-heartbreak::before { content: "\f771"; }
.bi-hearts::before { content: "\f772"; }
.bi-hospital-fill::before { content: "\f773"; }
.bi-hospital::before { content: "\f774"; }
.bi-house-heart-fill::before { content: "\f775"; }
.bi-house-heart::before { content: "\f776"; }
.bi-incognito::before { content: "\f777"; }
.bi-magnet-fill::before { content: "\f778"; }
.bi-magnet::before { content: "\f779"; }
.bi-person-heart::before { content: "\f77a"; }
.bi-person-hearts::before { content: "\f77b"; }
.bi-phone-flip::before { content: "\f77c"; }
.bi-plugin::before { content: "\f77d"; }
.bi-postage-fill::before { content: "\f77e"; }
.bi-postage-heart-fill::before { content: "\f77f"; }
.bi-postage-heart::before { content: "\f780"; }
.bi-postage::before { content: "\f781"; }
.bi-postcard-fill::before { content: "\f782"; }
.bi-postcard-heart-fill::before { content: "\f783"; }
.bi-postcard-heart::before { content: "\f784"; }
.bi-postcard::before { content: "\f785"; }
.bi-search-heart-fill::before { content: "\f786"; }
.bi-search-heart::before { content: "\f787"; }
.bi-sliders2-vertical::before { content: "\f788"; }
.bi-sliders2::before { content: "\f789"; }
.bi-trash3-fill::before { content: "\f78a"; }
.bi-trash3::before { content: "\f78b"; }
.bi-valentine::before { content: "\f78c"; }
.bi-valentine2::before { content: "\f78d"; }
.bi-wrench-adjustable-circle-fill::before { content: "\f78e"; }
.bi-wrench-adjustable-circle::before { content: "\f78f"; }
.bi-wrench-adjustable::before { content: "\f790"; }
.bi-filetype-json::before { content: "\f791"; }
.bi-filetype-pptx::before { content: "\f792"; }
.bi-filetype-xlsx::before { content: "\f793"; }

View File

@ -13,6 +13,11 @@
<title>Organiser un PDF</title>
</head>
<body class="bg-light">
<noscript>
<div class="alert alert-danger text-center" role="alert">
<i class="bi bi-exclamation-triangle"></i> Site non fonctionnel sans JavaScript activé
</div>
</noscript>
<div id="page-upload">
<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold"><i class="bi bi-ui-checks-grid"></i> Organiser un PDF</h1>

View File

@ -6,11 +6,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/vendor/bootstrap.min.css?5.1.1" rel="stylesheet">
<link href="/vendor/bootstrap-icons.css?1.5.0" rel="stylesheet">
<link href="/vendor/bootstrap-icons.css?1.8.1" rel="stylesheet">
<link href="/css/app.css" rel="stylesheet">
<title>Signature PDF</title>
</head>
<body class="bg-light">
<noscript>
<div class="alert alert-danger text-center" role="alert">
<i class="bi bi-exclamation-triangle"></i> Site non fonctionnel sans JavaScript activé
</div>
</noscript>
<div id="page-upload">
<div class="px-4 py-5 my-5 text-center">
<h1 class="display-5 fw-bold"><i class="bi bi-vector-pen"></i> Signer un PDF</h1>
@ -34,10 +39,11 @@
<div style="height: 55px;" class="d-md-none"></div>
<div class="offcanvas offcanvas-end show d-none d-md-block shadow-sm" data-bs-backdrop="false" data-bs-scroll="true" data-bs-keyboard="false" tabindex="-1" id="sidebarTools" aria-labelledby="sidebarToolsLabel">
<div class="offcanvas-header mb-0 pb-0">
<h5 id="sidebarToolsLabel">Signature du PDF</h5>
<h5 class="mb-1 d-block w-100" id="sidebarToolsLabel">Signature du PDF <?php if(isset($hash)): ?><span class="small float-end"><i class="bi bi-people-fill"></i> <span class="nblayers">0</span></span><?php else: ?><span class="float-end opacity-25 small"><i class="bi bi-person-workspace"></i></span></span><?php endif; ?></h5>
<button type="button" class="btn-close text-reset d-md-none" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<div class="offcanvas-body pt-0">
<p id="text_document_name" class="text-muted" style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden;" title="Lettre de demande de renouvellement aux SCOP_signe.pdf"><i class="bi bi-files"></i> <span>Lettre de demande de renouvellement aux SCOP_signe.pdf</span></p>
<div class="form-check form-switch mb-2 small d-none">
<input class="form-check-input" type="checkbox" id="add-lock-checkbox" disabled="disabled">
<label style="cursor: pointer;" class="form-check-label" for="add-lock-checkbox"> Garder la séléction active</label>
@ -45,17 +51,17 @@
<div id="svg_list_signature" class="list-item-add"></div>
<div class="d-grid gap-2 mb-2 list-item-add">
<input type="radio" class="btn-check" id="radio_svg_signature_add" name="svg_2_add" autocomplete="off" value="signature">
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="signature" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_signature_add" id="label_svg_signature_add"><i class="bi bi-vector-pen"></i> Signature <small class="text-muted float-end">Ajouter</small></label>
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="signature" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_signature_add" id="label_svg_signature_add"><i class="bi bi-vector-pen"></i> Signature <small class="text-muted float-end">Créer</small></label>
</div>
<div id="svg_list_initials" class="list-item-add"></div>
<div class="d-grid gap-2 mb-2 list-item-add">
<input type="radio" class="btn-check" id="radio_svg_initials_add" name="svg_2_add" autocomplete="off" value="intials">
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="initials" data-modalnav="#nav-type-tab" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_initials_add" id="label_svg_initials_add"><i class="bi bi-type"></i> Paraphe <small class="text-muted float-end">Ajouter</small></label>
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="initials" data-modalnav="#nav-type-tab" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_initials_add" id="label_svg_initials_add"><i class="bi bi-type"></i> Paraphe <small class="text-muted float-end">Créer</small></label>
</div>
<div id="svg_list_rubber_stamber" class="list-item-add"></div>
<div class="d-grid gap-2 mb-2 list-item-add">
<input type="radio" class="btn-check" id="radio_svg_rubber_stamber_add" name="svg_2_add" autocomplete="off" value="rubber_stamber">
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="rubber_stamber" data-modalnav="#nav-import-tab" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_rubber_stamber_add" id="label_svg_rubber_stamber_add"><i class="bi bi-card-text"></i> Tampon <small class="text-muted float-end">Ajouter</small></label>
<label data-bs-toggle="modal" data-bs-target="#modalAddSvg" data-type="rubber_stamber" data-modalnav="#nav-import-tab" class="btn btn-outline-secondary text-black text-start btn-add-svg-type" for="radio_svg_rubber_stamber_add" id="label_svg_rubber_stamber_add"><i class="bi bi-card-text"></i> Tampon <small class="text-muted float-end">Créer</small></label>
</div>
<div class="d-grid gap-2 mb-2 list-item-add">
<input type="radio" class="btn-check" id="radio_svg_text" data-svg="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGV4dGFyZWEtdCIgdmlld0JveD0iMCAwIDE2IDE2Ij48cGF0aCBkPSJNMS41IDIuNUExLjUgMS41IDAgMCAxIDMgMWgxMGExLjUgMS41IDAgMCAxIDEuNSAxLjV2My41NjNhMiAyIDAgMCAxIDAgMy44NzRWMTMuNUExLjUgMS41IDAgMCAxIDEzIDE1SDNhMS41IDEuNSAwIDAgMS0xLjUtMS41VjkuOTM3YTIgMiAwIDAgMSAwLTMuODc0VjIuNXptMSAzLjU2M2EyIDIgMCAwIDEgMCAzLjg3NFYxMy41YS41LjUgMCAwIDAgLjUuNWgxMGEuNS41IDAgMCAwIC41LS41VjkuOTM3YTIgMiAwIDAgMSAwLTMuODc0VjIuNUEuNS41IDAgMCAwIDEzIDJIM2EuNS41IDAgMCAwLS41LjV2My41NjN6TTIgN2ExIDEgMCAxIDAgMCAyIDEgMSAwIDAgMCAwLTJ6bTEyIDBhMSAxIDAgMSAwIDAgMiAxIDEgMCAwIDAgMC0yeiIvPjxwYXRoIGQ9Ik0xMS40MzQgNEg0LjU2Nkw0LjUgNS45OTRoLjM4NmMuMjEtMS4yNTIuNjEyLTEuNDQ2IDIuMTczLTEuNDk1bC4zNDMtLjAxMXY2LjM0M2MwIC41MzctLjExNi42NjUtMS4wNDkuNzQ4VjEyaDMuMjk0di0uNDIxYy0uOTM4LS4wODMtMS4wNTQtLjIxLTEuMDU0LS43NDhWNC40ODhsLjM0OC4wMWMxLjU2LjA1IDEuOTYzLjI0NCAyLjE3MyAxLjQ5NmguMzg2TDExLjQzNCA0eiIvPjwvc3ZnPgo=" name="svg_2_add" autocomplete="off" value="text">
@ -68,16 +74,36 @@
<div id="svg_list" class="d-grid gap-2 mt-2 mb-2 list-item-add"></div>
<div class="d-grid gap-2 mt-2">
<button type="button" id="btn-add-svg" class="btn btn-sm btn-light" data-bs-toggle="modal" data-bs-target="#modalAddSvg"><i class="bi bi-plus-circle"></i> Ajouter un élément</button>
<button type="button" id="btn-add-svg" class="btn btn-sm btn-light" data-bs-toggle="modal" data-bs-target="#modalAddSvg"><i class="bi bi-plus-circle"></i> Créer un élément</button>
</div>
<form class="position-absolute bottom-0 pb-2 ps-0 pe-4 w-100 d-none d-sm-none d-md-block" id="form_pdf" action="/sign" method="post" enctype="multipart/form-data">
<input id="input_pdf" name="pdf" type="file" class="d-none" />
<input id="input_svg" name="svg[]" type="file" class="d-none" />
<div class="d-grid gap-2 mt-2">
<button class="btn btn-primary" disabled="disabled" type="submit" id="save"><i class="bi bi-download"></i> Télécharger le PDF Signé</button>
<div id="form_block" class="position-absolute bottom-0 pb-2 ps-0 pe-4 w-100 d-none d-sm-none d-md-block">
<?php if(!isset($hash)): ?>
<?php if(!isset($noSharingMode)): ?>
<form id="form_sharing" action="/share" method="post" enctype="multipart/form-data">
<input id="input_pdf_share" name="pdf" type="file" class="d-none" />
<input id="input_svg_share" name="svg[]" type="file" class="d-none" />
<div class="d-grid gap-2 mb-1">
<button class="btn btn-outline-dark w-100" type="submit" id="save_share"><i class="bi bi-share"></i> Partager pour signer <i class="bi bi-people-fill"></i> à plusieurs </button>
</div>
</form>
<?php endif; ?>
<form id="form_pdf" action="/sign" method="post" enctype="multipart/form-data">
<input id="input_pdf" name="pdf" type="file" class="d-none" />
<input id="input_svg" name="svg[]" type="file" class="d-none" />
<button class="btn btn-primary w-100 mt-2" disabled="disabled" type="submit" id="save"><i class="bi bi-download"></i> Télécharger le PDF signé</button>
</form>
<?php elseif(!isset($noSharingMode)): ?>
<p id="nblayers_text" class="small d-none mb-2 opacity-50">Vous êtes <span class="badge rounded-pill bg-dark text-white"><span class="nblayers">0</span> <i class="bi bi-people-fill"></i></span> à avoir signé ce PDF</p>
<div class="btn-group w-100 mb-1">
<a id="btn_download" class="btn btn-outline-dark w-100" href="/signature/<?php echo $hash ?>/pdf"><i class="bi bi-download"></i> Télécharger le PDF</a>
<button class="btn btn-outline-dark" type="button" id="btn_share" data-bs-toggle="modal" data-bs-target="#modal-share-informations"><i class="bi bi-share"></i></button>
</div>
<form id="form_pdf" action="/signature/<?php echo $hash ?>/save" method="post" enctype="multipart/form-data">
<input id="input_svg" name="svg[]" type="file" class="d-none" />
<button class="btn btn-primary w-100 mt-2" disabled="disabled" type="submit" id="save"><i class="bi bi-cloud-upload"></i> Transmettre ma signature</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
<div class="position-fixed top-0 start-0 bg-white w-100 p-2 shadow-sm d-md-none">
@ -98,7 +124,7 @@
<button id="btn-zoom-increase" class="btn btn-secondary"><i class="bi bi-plus"></i></button>
</div>
<div class="d-grid gap-2">
<button class="btn btn-primary" disabled="disabled" type="submit" id="save_mobile"><i class="bi bi-download"></i> Télécharger le PDF Signé</button>
<button class="btn btn-primary" disabled="disabled" type="submit" id="save_mobile"><i class="bi bi-download"></i> Télécharger le PDF signé</button>
</div>
</div>
@ -140,6 +166,50 @@
</div>
</div>
</div>
<?php if(isset($hash)): ?>
<div id="modal-share-informations" class="modal" tabindex="-1">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-people-fill"></i> Signer ce PDF à plusieurs</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Plusieurs personnes peuvent signer ce PDF en même temps sur cette page.</p>
<div class="input-group mb-3">
<span class="input-group-text">Lien à partager</span>
<input id="input-share-link" type="text" onclick="this.select(); this.setSelectionRange(0, 99999);" readonly="readonly" class="form-control bg-light font-monospace" value="">
<button onclick="navigator.clipboard.writeText(document.getElementById('input-share-link').value); this.innerText = 'Copié !';" autofocus="autofocus" class="btn btn-primary" type="button" id="btn-copy-share-link"><i class="bi bi-clipboard"></i> Copier</button>
<script>document.querySelector('#input-share-link').value = document.location.href.replace(/#.*/, '');</script>
</div>
<p>C'est aussi depuis cette page qu'il est possible de télécharger le PDF signé par tout le monde.</p>
</div>
<div class="modal-footer text-start">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php if(isset($hash)): ?>
<div id="modal-signed" class="modal" tabindex="-1">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-file-earmark-check"></i> PDF signé</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="mb-1"><i class="bi bi-check-circle text-success"></i> Votre signature a bien été prise en compte !</p>
</div>
<div class="modal-footer text-center d-block">
<a class="btn btn-outline-dark" href="/signature/<?php echo $hash ?>/pdf"><i class="bi bi-download"></i> Télécharger le PDF</a>
</div>
</div>
</div>
</div>
<?php endif; ?>
<span id="is_mobile" class="d-md-none"></span>
<script src="/vendor/bootstrap.min.js?5.1.3"></script>
@ -150,6 +220,10 @@
<script>
var maxSize = <?php echo $maxSize ?>;
var maxPage = <?php echo $maxPage ?>;
var hash = null;
<?php if(isset($hash)): ?>
hash = "<?php echo $hash ?>";
<?php endif; ?>
</script>
<script src="/js/signature.js?202203261059"></script>
</body>