{% extends 'base.html.twig' %}
{% block title %}Guides{% endblock %}
{% block body %}
<div class="container-guide">
<div class="sidebar">
<ul>
<li>
<h3>Nos guides techniques</h3>
</li>
</ul>
<br>
<ul>
{% for category in categories %}
{% set has_visible_subcategories = false %}
{% for subcategory in category.getSousCategories() %}
{% set has_public_articles_in_subcategory = false %}
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() %}
{% set has_public_articles_in_subcategory = true %}
{% endif %}
{% endfor %}
{% if has_public_articles_in_subcategory or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
{% set has_visible_subcategories = true %}
{% endif %}
{% endfor %}
{% if has_visible_subcategories %}
<li class="category-item" id="category_{{ category.id }}">
{{ category.getTitre() }}
<ul>
{% for subcategory in category.getSousCategories() %}
{% set has_public_articles_in_subcategory = false %}
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() %}
{% set has_public_articles_in_subcategory = true %}
{% endif %}
{% endfor %}
{% if has_public_articles_in_subcategory or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
<li class="subcategory-item" id="subcategory_{{ subcategory.id }}">
<span class="arrow-down">→</span> {{ subcategory.getTitre() }}
<ul style="display: none;">
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
<li class="link-item">
<a href="{{ path('detail_guide', {article:article.id}) }}">
{{ article.titre }}
{% if article.isPrive() %}
{% if is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
{% if article.isPrive() == true %}
🛡️
{% endif %}
{% endif %}
{% endif %}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
<div class="main-content">
{% if article %}
<h3 class="article-title" id="articleTitle">{{article.titre}}</h3>
<div class="article-content" id="articleContent">{{article.contenu|raw}}</div>
<div class="article-date" id="articleDate">Publié le : {{ article.createdAt|date('d/m/Y', 'Europe/Paris') }}</div>
<br>
<button onclick="#" class="btn-download-pdf">Télécharger ce guide</button>
{% endif %}
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const subcategories = document.querySelectorAll('.subcategory-item');
subcategories.forEach(function(subcategory) {
const arrow = subcategory.querySelector('.arrow-down');
const subcategoryList = subcategory.querySelector('ul');
subcategory.addEventListener('click', function() {
if (subcategoryList.style.display === 'none' || subcategoryList.style.display === '') {
subcategoryList.style.display = 'block';
arrow.innerHTML = '↓'; // Change la flèche vers le bas
} else {
subcategoryList.style.display = 'none';
arrow.innerHTML = '→'; // Change la flèche vers la droite
}
});
});
});
document.addEventListener('DOMContentLoaded', function() {
const btnDownloadPdf = document.querySelector('.btn-download-pdf');
btnDownloadPdf.addEventListener('click', function() {
// Sélection du contenu de la div .main-content
const contenuHtml = document.querySelector('.main-content').innerHTML;
// Envoi du contenu au serveur Symfony pour la génération du PDF
fetch('{{ path('telecharger_pdf') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ contenuHtml: contenuHtml }),
})
.then(response => {
if (!response.ok) {
throw new Error('Erreur lors de la génération du PDF');
}
return response.blob();
})
.then(blob => {
// Création d'un lien temporaire pour télécharger le PDF
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'contenu.pdf';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch(error => {
console.error('Erreur:', error);
});
});
});
</script>
{% endblock %}
j'ai un balise img qui se met en place automatiquement lors de mon uploadhttps://forum.phpfrance.com/styles/PHPfrance/theme/phpfrance-blanc-logo.png <img src="https://forum.phpfrance.com/styles/PHPfrance/theme/phpfrance-logo.png" alt="PHPfrance" style="max-width:256px; margin-top:-20px;" />
http://127.0.0.1:8000/guide/assets/images/66057c8f3296b.png
un code html généré par le php :<img src="assets/images/66057c8f3296b.png" alt="" width="501" height="200">
Voici une code html qui s'affiche correctement (l'ajout d'un "/" avant le assets) : <img src="/assets/images/66057c8f3296b.png" alt="" width="501" height="200">

<?php
/***************************************************
* Only these origins are allowed to upload images *
***************************************************/
$accepted_origins = array("http://127.0.0.1:8000");
/*********************************************
* Change this line to set the upload folder *
*********************************************/
$imageFolder = "assets/images/";
$name = uniqid();
if (isset($_SERVER['HTTP_ORIGIN'])) {
// same-origin requests won't set an origin. If the origin is set, it must be valid.
if (in_array($_SERVER['HTTP_ORIGIN'], $accepted_origins)) {
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
} else {
header("HTTP/1.1 403 Origin Denied");
return;
}
}
// Don't attempt to process the upload on an OPTIONS request
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("Access-Control-Allow-Methods: POST, OPTIONS");
return;
}
reset($_FILES);
$temp = current($_FILES);
if (is_uploaded_file($temp['tmp_name'])) {
/*
If your script needs to receive cookies, set images_upload_credentials : true in
the configuration and enable the following two headers.
*/
// header('Access-Control-Allow-Credentials: true');
// header('P3P: CP="There is no P3P policy."');
// Sanitize input
if (preg_match("/([^\w\s\d\-_~,;:\[\]\(\).])|([\.]{2,})/", $temp['name'])) {
header("HTTP/1.1 400 Invalid file name.");
return;
}
// Verify extension
if (!in_array(strtolower(pathinfo($temp['name'], PATHINFO_EXTENSION)), array("gif", "jpg", "png"))) {
header("HTTP/1.1 400 Invalid extension.");
return;
}
// Accept upload if there was no origin, or if it is an accepted origin
// assets/images/
$extension = pathinfo($temp['name'], PATHINFO_EXTENSION);
$filetowrite = $imageFolder . $name . "." . $extension;
move_uploaded_file($temp['tmp_name'], $filetowrite);
// Determine the base URL
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? "https://" : "http://";
$baseurl = $protocol . $_SERVER["HTTP_HOST"] . rtrim(dirname($_SERVER['REQUEST_URI']), "/") . "/";
// Respond to the successful upload with JSON.
// Use a location key to specify the path to the saved image resource.
// { location : '/your/uploaded/image/file'}
echo json_encode(array('location' => $filetowrite));
} else {
// Notify editor that the upload failed
header("HTTP/1.1 500 Server Error");
}
articleForm2.html.twig :{% extends '@EasyAdmin/crud/form_theme.html.twig' %}
{% block ea_text_editor_widget %}
<textarea id="{{ id }}" name="Article[contenu]" class="trix-content tinymce" role="textbox">{{ data }}</textarea>
<script src="{{ asset('assets/bundles/tinymce/ext/tinymce/tinymce.min.js') }}"></script>
<script src="{{ asset('bundles/tinymce/ext/tinymce-webcomponent.js') }}" type="module"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.9.3/html2pdf.bundle.min.js"></script>
<script>
tinymce.init({
selector: '.tinymce',
width: '1000px',
plugins: 'anchor autolink charmap codesample emoticons image link lists media searchreplace table visualblocks wordcount checklist mediaembed casechange export formatpainter pageembed linkchecker a11ychecker tinymcespellchecker permanentpen powerpaste advtable advcode editimage tinycomments tableofcontents footnotes mergetags autocorrect typography inlinecss',
toolbar: 'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | link image media table mergetags | addcomment showcomments | spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist indent outdent | emoticons charmap | removeformat | downloadPdf',
tinycomments_mode: 'embedded',
images_upload_handler: (blobInfo, progress) => new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', 'upload.php');
xhr.upload.onprogress = (e) => {
progress(e.loaded / e.total * 100);
};
xhr.onload = () => {
if (xhr.status === 403) {
reject({
message: 'HTTP Error: ' + xhr.status,
remove: true
});
return;
}
if (xhr.status < 200 || xhr.status >= 300) {
console.log(xhr);
reject('HTTP Error: ' + xhr.status + ' ' + xhr.statusText);
return;
}
const json = JSON.parse(xhr.responseText);
if (!json || typeof json.location != 'string') {
reject('Invalid JSON: ' + xhr.responseText);
return;
}
resolve(json.location);
};
xhr.onerror = () => {
reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
};
const formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.filename());
xhr.send(formData);
}),
setup: function (editor) {
editor.on('init', function () {
editor.insertContent('<img src="/img/gtitop.png" alt="Votre image" style="max-width: 100%;">');
});
editor.ui.registry.addButton('downloadPdf', {
icon: 'export',
tooltip: 'Télécharger en PDF',
onAction: function () {
const content = tinymce.activeEditor.getContent();
const element = document.createElement('div');
element.innerHTML = content;
html2pdf().from(element).set({
margin: 1,
filename: 'document.pdf',
image: { type: 'jpeg', quality: 1 },
html2canvas: { scale: 2, logging: true },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }
}).save();
}
});
}
});
</script>
{% endblock %}
<img src="assets/images/66057c8f3296b.png" alt="" width="501" height="200">
Par hasard, j'ai rajouté un "/" avant le assets dans mon html et j'ai remarqué que l'image s'affichait{% extends 'base.html.twig' %}
{% block title %}Guides{% endblock %}
{% block body %}
<div class="container-guide">
<div class="sidebar">
<ul>
<li>
<h3>Nos guides techniques</h3>
</li>
</ul>
<br>
<ul>
{% for category in categories %}
{% set has_visible_subcategories = false %}
{% for subcategory in category.getSousCategories() %}
{% set has_public_articles_in_subcategory = false %}
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() %}
{% set has_public_articles_in_subcategory = true %}
{% endif %}
{% endfor %}
{% if has_public_articles_in_subcategory or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
{% set has_visible_subcategories = true %}
{% endif %}
{% endfor %}
{% if has_visible_subcategories %}
<li class="category-item" id="category_{{ category.id }}">
{{ category.getTitre() }}
<ul>
{% for subcategory in category.getSousCategories() %}
{% set has_public_articles_in_subcategory = false %}
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() %}
{% set has_public_articles_in_subcategory = true %}
{% endif %}
{% endfor %}
{% if has_public_articles_in_subcategory or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
<li class="subcategory-item" id="subcategory_{{ subcategory.id }}">
<span class="arrow-down">→</span> {{ subcategory.getTitre() }}
<ul style="display: none;">
{% for article in subcategory.getArticles() %}
{% if not article.isPrive() or is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
<li class="link-item">
<a href="{{ path('detail_guide', {article:article.id}) }}">
{{ article.titre }}
{% if article.isPrive() %}
{% if is_granted('ROLE_USER') or is_granted('ROLE_ADMIN') %}
{% if article.isPrive() == true %}
🛡️
{% endif %}
{% endif %}
{% endif %}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
<div class="main-content">
{% if article %}
<h3 class="article-title" id="articleTitle">{{article.titre}}</h3>
<div class="article-content" id="articleContent">{{article.contenu|raw}}</div>
<div class="article-date" id="articleDate">Publié le : {{ article.createdAt|date('d/m/Y', 'Europe/Paris') }}</div>
<br>
<button onclick="#" class="btn-download-pdf">Télécharger ce guide</button>
{% endif %}
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const subcategories = document.querySelectorAll('.subcategory-item');
subcategories.forEach(function(subcategory) {
const arrow = subcategory.querySelector('.arrow-down');
const subcategoryList = subcategory.querySelector('ul');
subcategory.addEventListener('click', function() {
if (subcategoryList.style.display === 'none' || subcategoryList.style.display === '') {
subcategoryList.style.display = 'block';
arrow.innerHTML = '↓'; // Change la flèche vers le bas
} else {
subcategoryList.style.display = 'none';
arrow.innerHTML = '→'; // Change la flèche vers la droite
}
});
});
});
document.addEventListener('DOMContentLoaded', function() {
const btnDownloadPdf = document.querySelector('.btn-download-pdf');
btnDownloadPdf.addEventListener('click', function() {
// Sélection du contenu de la div .main-content
const contenuHtml = document.querySelector('.main-content').innerHTML;
// Envoi du contenu au serveur Symfony pour la génération du PDF
fetch('{{ path('telecharger_pdf') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ contenuHtml: contenuHtml }),
})
.then(response => {
if (!response.ok) {
throw new Error('Erreur lors de la génération du PDF');
}
return response.blob();
})
.then(blob => {
// Création d'un lien temporaire pour télécharger le PDF
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'contenu.pdf';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch(error => {
console.error('Erreur:', error);
});
});
});
</script>
{% endblock %}
Voici la page : 