Volker,
ich habe das Script zur Initialisierung vom
TinyMCE Editor nochmals überarbeitet, denn es fehlte die Möglichkeit
mehrere Bilder gleichzeitig hochzuladen, was jetzt über den Button und auch das Menü funktioniert.
Das solltest Du vielleicht auch noch in der
ZIP zum Download der Anwendung anpassen, denn für größere Galerien ist das Hochladen einzelner Bilder sonst sehr umständlich.
Hier das neue Script (einfach in der Datei 'dashboard.php' tauschen):
Code: Alles auswählen
<script>
tinymce.init({
selector: 'textarea#content',
height: 600,
menubar: true,
promotion: false, // entfernt den "Get all features" / Upgrade-Button in der Menüleiste
branding: false, // blendet "Powered by Tiny" im Statusbalken aus
// + Vorschau-Plugin (preview) ergänzt
// (Media & Table hattest du schon – ich lasse sie drin)
plugins: 'link image code lists emoticons textcolor media table preview', // + preview
// + Vorschau-Button in die Toolbar aufgenommen
// Galerie-Button weiterhin auskommentiert (nicht gelöscht)
toolbar: [
'undo redo | fontfamily | styleselect fontselect | fontsizeinput | fontsizeselect bold italic underline | emoticons',
'alignleft aligncenter alignright | bullist numlist | forecolor backcolor | link image media table | preview | code | bordercolorbutton', // + preview
// 'gallerybutton', // (ausgeblendet, aber weiterhin registriert)
].join(' '),
language: 'de',
relative_urls: false,
remove_script_host: false,
automatic_uploads: true,
images_upload_url: 'upload_image.php',
file_picker_types: 'image',
images_upload_credentials: true,
image_advtab: true,
valid_elements: '*[*]',
// + iframes für Media-Einbettungen (YouTube/Vimeo etc.) zugelassen
extended_valid_elements:
'img[class|src|alt|title|width|height|style|data-*],a[href|target|title|class|style],div[class|style],span[class|style],iframe[src|frameborder|allowfullscreen|width|height|style]', // + iframe
// + Live-Preview von eingebetteten Medien aktivieren
media_live_embeds: true, // + neu
// (Optional) Wenn die Vorschau so aussehen soll wie deine Website,
// kannst du deine Frontend-CSS einbinden:
// content_css: ['/css/site.css'], // + optional
// + Mehrfach-Upload für Bilder:
// - Datei-Dialog erlaubt mehrere Bilder (input.multiple = true)
// - Alle Bilder werden hochgeladen und nacheinander im Editor eingefügt
// Hinweis: Der Image-Dialog hat nur ein Quellenfeld; bei Mehrfachauswahl fügen wir direkt in den Editor ein.
file_picker_callback: function (cb, value, meta) {
if (meta.filetype === 'image') {
var input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.multiple = true; // + Mehrfachauswahl erlauben
input.onchange = function () {
var files = Array.from(this.files || []);
if (!files.length) return;
// Hilfsfunktion: einzelnes Bild hochladen -> URL zurückgeben
var uploadOne = function (file) {
var formData = new FormData();
formData.append('file', file);
return fetch('upload_image.php', { method: 'POST', body: formData, credentials: 'include' }) // + Credentials mitgeben wie oben konfiguriert
.then(function (r) { return r.json(); })
.then(function (data) {
if (!data || !data.location) throw new Error('Antwort ohne URL');
return data.location;
});
};
// Alle Dateien hochladen
Promise.all(files.map(uploadOne))
.then(function (urls) {
if (urls.length === 1) {
// Standardverhalten für Einzelbild beibehalten
cb(urls[0]); // setzt die Quelle im Image-Dialog
} else {
// Bei Mehrfachauswahl direkt Inhalte einfügen
var ed = tinymce.activeEditor; // + aktuelle Editorinstanz
urls.forEach(function (u) {
ed.insertContent('<p><img src="' + u + '" alt="" /></p>'); // + jedes Bild separat einfügen
});
// Optional: Dialog schließen (falls geöffnet)
// tinymce.activeEditor.windowManager.close(); // nur aktivieren, wenn gewünscht
}
})
.catch(function (err) {
alert('Upload-Fehler: ' + err);
});
};
input.click();
}
},
content_style: `
img { max-width: 100%; height: auto; border-radius: 6px; }
.left { float: left; margin: 0 10px 10px 0; }
.right { float: right; margin: 0 0 10px 10px; }
.center { display: block; margin: 0 auto 10px auto; }
.shadow { box-shadow: 0 2px 8px rgba(0,0,0,0.3); }
.rounded { border-radius: 10px; }
.image-row { display: flex; flex-wrap: wrap; justify-content: center; }
.image-row img { width: 100%; height: auto; border-radius: 6px; }
`,
style_formats: [
{ title: 'Bild links', selector: 'img', classes: 'left' },
{ title: 'Bild rechts', selector: 'img', classes: 'right' },
{ title: 'Bild zentriert', selector: 'img', classes: 'center' },
{ title: 'Abgerundet', selector: 'img', classes: 'rounded' },
{ title: 'Schatten', selector: 'img', classes: 'shadow' }
],
setup: function (editor) {
// Galerie-Button bleibt registriert, ist aber in der Toolbar auskommentiert
editor.ui.registry.addButton('gallerybutton', {
text: '📷 Galerie',
tooltip: 'Mehrere Bilder nebeneinander anordnen',
onAction: function () {
const selected = editor.selection.getContent({ format: 'html' });
if (!selected || !selected.includes('<img')) {
alert('Bitte wähle mehrere Bilder aus oder füge sie zuerst ein.');
return;
}
const columns = prompt('Wie viele Spalten möchtest du? (z. B. 2, 3, 4)', '3');
const gap = prompt('Welchen Abstand zwischen den Bildern (in Pixel)?', '10');
if (!columns || isNaN(columns)) return alert('Ungültige Spaltenzahl!');
if (!gap || isNaN(gap)) return alert('Ungültiger Abstand!');
const wrapped = `
<div class="image-row" style="gap:${gap}px;">
${selected}
</div>
<style>
.image-row a, .image-row img {
flex: 1 1 calc(${100 / columns}% - ${gap}px);
max-width: calc(${100 / columns}% - ${gap}px);
}
@media(max-width:768px){
.image-row a, .image-row img { flex-basis: calc(50% - ${gap}px); max-width: calc(50% - ${gap}px); }
}
@media(max-width:480px){
.image-row a, .image-row img { flex-basis: 100%; max-width: 100%; }
}
</style><p></p>`;
editor.selection.setContent(wrapped);
}
});
// Farbbutton
editor.ui.registry.addButton('bordercolorbutton', {
text: '🎨 Rahmenfarbe',
tooltip: 'Rahmenfarbe für ausgewähltes Bild setzen',
onAction: function () {
const node = editor.selection.getNode();
if (node.nodeName !== 'IMG') {
alert('Bitte zuerst ein Bild auswählen.');
return;
}
const picker = document.createElement('input');
picker.type = 'color';
picker.style.position = 'fixed';
picker.style.zIndex = 99999;
picker.style.left = '50%';
picker.style.top = '50%';
picker.style.transform = 'translate(-50%, -50%)';
picker.style.width = '100px';
picker.style.height = '60px';
document.body.appendChild(picker);
picker.addEventListener('input', function () {
const color = this.value;
let currentStyle = node.getAttribute('style') || '';
currentStyle = currentStyle.replace(/border:[^;]+;?/gi, '');
node.setAttribute('style', currentStyle + `border: 4px solid ${color};`);
});
picker.addEventListener('blur', function () { document.body.removeChild(picker); });
picker.focus();
}
});
}
});
</script>