Naudojant CKEditor’ių pastebėjau, kad nėra paprasto plugin’o tam, kad į redaguojamą tekstą įterpti paveikslėlį iš kompiuterio, arba, tiksliau sakant, įkelti lokalų paveikslėlį į serverį ir iškarto jį atvaizduoti editoriuje. Tokiem dalykam autoriai siūlo naudoti taipogi jų produktą – CKFinder, bet: jis yra mokamas, jis yra griozdiškas daiktas, leidžiantis valdyti failus serveryje. Žodžiu, visas didžiulis failų menedžeris, o norisi kažkokio paprasto sprendimo.

Todėl sugalvojau, kad būtų neprošal parašyti savo pluginą (sakyčiau, plauginuką) tokiom reikmėm. Viskas prasidėjo nuo CKEditor 4.0 API dokumentacijos skaitymo. Api tikrai lengvas, intuityviai suprantamas, dokumentacija parašyta gan struktūrizuotai, bei parodyti keli pavyzdžiai. Šiame straipsniuke per daug nesigilinsime į šio WYSIWYG editoriaus nuostabųjį APĮ, o iškarto prie reikalo.

CKEDITOR.plugins.add( 'imgupload', {
    icons: 'imgupload',
    init: function( editor ) {
        editor.addCommand( 'uploadImage', {
            exec: function( editor ) {   
                var imagePostUrl = '/kelias/iki/skripto/'; // kelias iki skripto, kuris įkels failą į serverį ir išves rezultato JSON'ą
                $("#uploadFrame").remove();
                $('<iframe id="uploadFrame" />').appendTo("body");
                
                var postFrame = '<form action="'+imagePostUrl+'" method="POST" enctype="multipart/form-data" id="uploadForm"><input type="file" name="user_image" id="browseBtn" /><input type="hidden" name="upload" value="1" /></form>';
                var parseUpload = function() {
                	// gauname JSON atsakymą
                    var resp = $.parseJSON($('#uploadFrame').contents().find('#response').html());
                    if (!resp) return;
                    
                    // čia jį apdorojame
                    // SVARBU: šioje vietoje reikėtų atsižvelgti į gražinamą struktūrą ir
                    // parse'inti ją kaip dera. Pavyzdžiui, jeigu struktūra tokia:
                    // {upload: {error: klaidos_pranešimas/false, path: 'http://kelias/iki/ikelto/failo.jpg'}}
                    // tai ją galima sukramtyti kad ir taip:
                    if (resp.upload.error == false) {
                        editor.insertHtml('<img src="'+resp.upload.path+'" />');
                        editor.fire('key', {keyCode: 0});
                    } else {
                        alert(resp.upload.error);
                    }
                    $.unblockUI(); // Paslepiame "Uploading..." pranešimą, šiuo atveju - atblokuojame ekraną
                    $("#uploadFrame").remove();
                };
                $("#uploadFrame").contents().find("body").append(postFrame);
                $("#uploadFrame").on("load", parseUpload);
                $('#uploadFrame').contents().find('#browseBtn').trigger('click');
                $('#uploadFrame').contents().find('#browseBtn').change(function() {
                	// Parodome "Uploading..." pranešimą, šiuo atveju - užblokuojame ekraną
                    $.blockUI({ css: { 
                            border: 'none', 
                            padding: '70px 20px', 
                            backgroundColor: '#000', 
                            '-webkit-border-radius': '10px', 
                            '-moz-border-radius': '10px', 
                            'border-radius': '10px',
                            opacity: .7, 
                            'font-size': '2em',
                            'line-height': '1.5em',
                            color: '#fff' 
                        },
                        message: 'Please wait, uploading and processing your image...'
                    }); 
                    // ... ir patvirtiname užpildytą formą
                    $(this).parent("form").submit();
                });

            }
        });
        
        // pridedame mūsų Upload mygtuką į editoriaus toolbar'o grupę "insert"
        editor.ui.addButton('Image upload', {
            label: 'Upload image',
            command: 'uploadImage',
            toolbar: 'insert',
            icon: this.path + 'icons/imgupload.png'
        });
    }
});

Kaip galima pastebėti plika akimi, skriptas labai paprastas. Iš komentarų galima suprasti kas kaip veikia:

  1. Paspaudus mūsų pridėtą “Upload image” mygtuką toolbar’ę, sukuriamas nematomas iframe, į kurį pridedama įkelimo forma.
  2. Imituojamas tos formos mygtuko “Browse” paspaudimas, kas vartotojui sudaro iliuziją, jog šis langas buvo iškviestas “Upload image” mygtuko paspaudimu.
  3. Pasirinkus failą formą automatiškai submit‘inama. Įkelimo skriptas patikrina ir įkelia failą į serverį.
  4. Išvedamas rezultatas JSON pavidalu, kurį gauname 13 kodo eilutėje ir apdorojame.
  5. Gautą kelią iki paveikslėlio įdedame į editoriaus langą bei sužadiname key eventą, kad būtų iškviestos funkcijos, priskirtos prie šio įvykio (jeigu tokių yra).

Paveikslėlių įkelimas į serverį yra atskira problema, todėl jos dabar neaptarinėsime. Svarbu, kad paveikslėlis būtų patikrintas (ar tai tikrai paveikslėlis, ar jis neviršyja leistino dydžio ir pan.), jeigu reikia pakeista (pvz. sumažinta) jo rezoliucija (pasinaudojant tokiom bibliotekom kaip, pavyzdžiui, Imagick arba GD ar pan.), įkeltas į iš išorės pasiekiamą vietą. Tada sukonstruojamas rezultato JSON’as, kuris ir gražinamas skriptui.

Šiame pavyzdyje taip pat naudojamas jQuery su BlockUI pluginu. Gatavą CKEditor image upload plugin’ą galima parsisiųsti iš čia.