Dateiuploadfenster in tinyMCE4 aufrufen, ohne HTML der Seite zu ändern

Heute wollte ich in tinyMCE4 einen Dateiupload einbauen, der direkt nach Klick auf das Ordnersymbol (im Bild neben dem Eingabefeld von Quelle, rechts) das jeweilige Dateiuploadfenster des Browsers/Systems aufruft und Dateien hochläd.

Bild einfügen Dialog von tinyMCE4

D. h. ohne Zwischenschritte (wie weitere Popups oder externe Bibliotheken) soll nach dem Klick auf das Ordner-mit-Lupe-Symbol direkt dass hier angezeigt werden:

Dateiuploaddialog von Firefox auf Ubuntu Linux

Als zusätzliche Hürde kam hinzu, dass ich den Quellcode der Seite ungern verändern wollte (sonst hätte ich u. A. Code für die Validierung der dort vorhandenen Formulare umschreiben müssen). Dieser Fall ist sicherlich nicht besonders häufig, aber vielleicht geht es ja auch anderen so. Glücklicherweise gibt es eine sehr einfache Lösung via AJAX. So habe ich das ganze umgesetzt:

  1. Bevor es losgeht wird das jquery-iframe-transport-plugin benötigt, da XHR1 normalerweise keine Dateiuploads unterstützt, muss dieser Umweg gegangen werden. Einfach runterladen und einbinden
  2. Im Initialisierungscode von tinyMCE ($(„textarea“).tinymce({…}); kommt ein file_browser_callback hinzu, damit der Button überhaupt angezeigt wird, Code siehe unten
  3. Dann hole ich mir ein Formular mit dem <input type=“file“>-Button via $.get (HTML-Quellcode des Formulars weiter unten) und füge es an die aktuelle Seite an; falls es bereits existiert, spare ich den Ajax-Call ein und es geht weiter zu 3. (Übrigens: AJAX ist für die 3 Zeilen HTML-Code natürlich Overkill, man kann sie auch einfach per JavaScript direkt einfügen – allerdings werde ich in Zukunft vermutlich doch noch ein bisschen mehr serverseitige Logik hinter dem Formular brauchen, daher hab ich das so gelöst)
  4. Dann triggere ich einen Klick auf den versteckten Upload-Button, was den Dateiuploaddialog aufruft
  5. Sobald eine Datei ausgewählt wurde, wird das Formular an ein serverseitiges Skript geschickt, es gibt ggf. noch ein bisschen Errorhandling – ich lasse Fehlercodes und sonstige Daten per JSON zurückliefern, kann man natürlich auch anders machen
  6. Per win.document.getElementById(field_name).value = $(„#fupload“).val(); wird der Dateiname noch in das tinyMCE-Formularfeld gesetzt – ggf. ist es notwendig stattdessen einen Dateinamen zu verwenden, der vom serverseitigen Skript zurückgeliefert wird, je nach Situation
  7. Am Schluss wird das temporäre Formular wieder aus dem Dokument gelöscht und hinterlässt keine Spuren. (Falls jemand den Dateiuploaddialog abbricht, findet dieser Schritt nicht statt)

Der tinyMCE-Initialisierungscode

file_browser_callback: function(field_name, url, type, win) {
   if($("#fuploadform").length) {
      $("#fupload").click();
   } else { 
      $.get('uploadform.html', function(data) {
         $("body").append(data);
         $("#fupload").click();
         $("#fupload").on("change", function() {
            $.ajax({
               type: "POST",
               url: 'fileupload.php',
               files: $("#fupload", this),
               dataType: 'json',
               iframe: true,
               success: function(data) {
                  if(data.error=='none') win.document.getElementById(field_name).value = data.filename;
                  else alert('Ein Fehler ist beim Upload aufgetreten: '+data.error);
               }
            });
            $("#fuploadform").remove();
         });
      });
   }
}

Das HTML-Formular in uploadform.html

Nicht vergessen, den enctype zu setzen, sonst wird das mit dem Upload nix. 😉 Und natürlich per display: none; dafür sorgen, dass das temporäre Formular nicht angezeigt wird. Ggf. kann es auch noch sinnvoll sein mit dem accept-Attribut die möglichen MIME-Types zu setzen, habe ich hier nicht gemacht.

<form id="fuploadform" enctype="multipart/form-data" method="post">
   <input id="fupload" style="display: none;" name="fupload" type="file" />
</form>

Zu guter letzt noch die fileupload.php

(Ich verwende ein anderes serverseitiges Skript, als hier dargestellt, aber es wäre für dieses Beispiel zu kompliziert, daher zitiere ich einfach mal aus teilen der PHP-Dokumentation) – natürlich könnte man noch weitere Fehler hier abfangen (Datei zu groß, falscher Dateityp, usw. usf.).

<?php 
/* Verzeichnis wo die Datei hinsoll hier einstellen */
$uploaddir = '/var/www/uploads/';

$uploadfile = $uploaddir . basename($_FILES['fupload']['name']);

if (move_uploaded_file($_FILES['fupload']['tmp_name'], $uploadfile)) {
    $error = 'none';
} else {
    $error = 'Möglicherweise eine Dateiupload-Attacke';
}

print(json_encode(array('error', $error)));

Ich hoffe, dieses Beispiel hilft dem ein oder anderen weiter, wie immer freue ich mich über Kommentare.

Kommentar verfassen