add plugin web2print

This commit is contained in:
isUnknown 2026-03-09 10:14:02 +01:00
parent 1f024a7e71
commit d3b9220931
17 changed files with 596 additions and 2 deletions

View file

@ -0,0 +1,20 @@
label: web2print
icon: print
sections:
web2printSection:
type: fields
fields:
printFormat:
label: Format
type: select
options:
- A4
- A5
width: 1/4
generatePdfBtn:
type: web2print
width: 1/4
generatedPdfs:
label: PDF générés
type: files
width: 1/4

View file

@ -0,0 +1,36 @@
<template>
<k-button @click="getPdf()" variant="filled">Générer</k-button>
</template>
<script setup>
const { htmlPageString, cssPath, printFormat, pageId } = defineProps({
htmlPageString: String,
cssPath: String,
printFormat: String,
pageId: String,
});
async function getPdf() {
const html = htmlPageString;
const requestConfig = {
method: "POST",
body: JSON.stringify({
html,
cssPath,
printFormat,
pageId,
}),
};
const response = await fetch("/web2print.json", requestConfig);
const json = await response.json();
console.log(json);
if (json.success) {
// Recharger la page pour afficher le nouveau PDF
window.panel.view.reload();
}
}
</script>

View file

@ -0,0 +1,7 @@
import Web2PrintBtn from "./components/Web2PrintBtn.vue";
window.panel.plugin("studio-variable/web2print", {
fields: {
web2print: Web2PrintBtn,
},
});

View file

@ -0,0 +1,189 @@
<?php
// Fonction pour résoudre les @import CSS récursivement
function resolveCssImports($cssPath) {
if (!file_exists($cssPath)) {
return '';
}
$css = file_get_contents($cssPath);
$baseDir = dirname($cssPath);
// Trouver tous les @import
$css = preg_replace_callback('/@import\s+url\(["\']?([^"\')]+)["\']?\);?/', function($matches) use ($baseDir) {
$importPath = $matches[1];
$fullPath = $baseDir . '/' . $importPath;
return resolveCssImports($fullPath);
}, $css);
return $css;
}
return [
'pattern' => '/web2print.json',
'method' => 'POST',
'action' => function () {
header('Content-Type: application/json');
$jsonRequest = file_get_contents('php://input');
$body = json_decode($jsonRequest);
if (!$body || !isset($body->html)) {
http_response_code(400);
return json_encode(['error' => 'Missing html parameter']);
}
if (!isset($body->pageId) || empty($body->pageId)) {
http_response_code(400);
return json_encode(['error' => 'Missing pageId parameter']);
}
// Récupérer le cssPath depuis le body ou utiliser la valeur par défaut
$cssPath = isset($body->cssPath) && !empty($body->cssPath)
? $body->cssPath
: 'assets/css/style.css';
// Récupérer le format d'impression
$printFormat = isset($body->printFormat) && !empty($body->printFormat)
? $body->printFormat
: 'A4';
// Récupérer la page Kirby
$page = kirby()->page($body->pageId);
if (!$page) {
http_response_code(404);
return json_encode(['error' => 'Page not found']);
}
$ch = curl_init('https://web2print.studio-variable.com/generate');
$html = $body->html;
// Nettoyer le HTML pour la génération PDF
$dom = new DOMDocument();
@$dom->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
// Supprimer tous les scripts
$scripts = $dom->getElementsByTagName('script');
while ($scripts->length > 0) {
$scripts->item(0)->parentNode->removeChild($scripts->item(0));
}
// Supprimer les attributs Alpine.js (x-data, x-show, @click, etc.)
$xpath = new DOMXPath($dom);
$elements = $xpath->query('//*[@*[starts-with(name(), "x-") or starts-with(name(), "@") or starts-with(name(), ":")]]');
foreach ($elements as $element) {
$attributesToRemove = [];
foreach ($element->attributes as $attr) {
if (strpos($attr->name, 'x-') === 0 || strpos($attr->name, '@') === 0 || strpos($attr->name, ':') === 0) {
$attributesToRemove[] = $attr->name;
}
}
foreach ($attributesToRemove as $attrName) {
$element->removeAttribute($attrName);
}
}
// Supprimer les liens CSS et les remplacer par un style inline
$head = $dom->getElementsByTagName('head')->item(0);
if ($head) {
// Supprimer tous les <link rel="stylesheet">
$links = $dom->getElementsByTagName('link');
$linksToRemove = [];
foreach ($links as $link) {
if ($link->getAttribute('rel') === 'stylesheet') {
$linksToRemove[] = $link;
}
}
foreach ($linksToRemove as $link) {
$link->parentNode->removeChild($link);
}
// Charger et résoudre le CSS
$fullCssPath = kirby()->root() . '/' . $cssPath;
$resolvedCss = resolveCssImports($fullCssPath);
// Ajouter les règles @page pour le format d'impression
$pageRules = "\n\n@page {\n size: " . $printFormat . ";\n}\n";
$resolvedCss .= $pageRules;
// Créer une balise <style> et l'ajouter au <head>
$styleTag = $dom->createElement('style');
$styleTag->appendChild($dom->createTextNode($resolvedCss));
$head->appendChild($styleTag);
}
// Remplacer les URLs localhost par les URLs de production
$html = $dom->saveHTML();
$siteUrl = kirby()->site()->url();
$html = str_replace('http://localhost:8888', $siteUrl, $html);
$data = json_encode(['html' => $html]);
$apiKey = kirby()->option('web2printApiKey');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-API-Key: ' . $apiKey
],
CURLOPT_POSTFIELDS => $data
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
http_response_code(500);
return json_encode(['error' => 'cURL error: ' . $error]);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
try {
// Générer un nom de fichier unique avec timestamp et format
$timestamp = date('Y-m-d_H-i-s');
$filename = "export_{$printFormat}_{$timestamp}.pdf";
// Créer un fichier temporaire
$tmpFile = tempnam(sys_get_temp_dir(), 'pdf_');
file_put_contents($tmpFile, $response);
// Créer le fichier dans Kirby
$file = $page->createFile([
'source' => $tmpFile,
'filename' => $filename
]);
// Supprimer le fichier temporaire
unlink($tmpFile);
return json_encode([
'success' => true,
'message' => 'PDF généré avec succès',
'filename' => $filename
]);
} catch (\Exception $e) {
http_response_code(500);
return json_encode([
'error' => 'Failed to save PDF: ' . $e->getMessage()
]);
}
} else {
http_response_code($httpCode);
$error = json_decode($response, true);
return json_encode($error ?: [
'error' => 'Unknown error',
'httpCode' => $httpCode,
'response' => $response
]);
}
}
];