1

I'm building a custom CKFinder plugin for use on an in-house CMS and am having some trouble with 'undefined' variables.

What I'm doing is pulling copyright information for uploaded images from a database - the details of which are retrieved from a JSON file. I've managed to do that, but my issue seems to revolve around calling variables that are within a function.

Here is my code:

function fileShare( data ) {

var fileName = data.file.getUrl();

var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
        var json = JSON.parse(this.responseText);

        var copyright = '';
        var watermark = '';

        for ( var i = 0; i < json.image.length; i++) {
            if(json.image[i].image_path == fileName) {

                copyright += json.image[i].copyright;

                if(json.image[i].watermark == 1) {
                    watermark += ' checked';
                }
            }
        }
        return false;
    }
};

xmlhttp.open("GET", "copyright.json", true);
xmlhttp.send();

// Dialog Box Content
finder.request( 'dialog', {
    name: 'CopyrightDialog',
    title: 'Copyright Information',
    template: '<p>Type the name of the copyright holder:</p>' +
    '<input type="text" name="copyright" value="' + copyright + '" placeholder="Image Credit...">' +
    '<p>Protect the image with a watermark?</p>' +
    '<label><input type="checkbox" name="watermark" value="watermark"' + watermark + '>Enable Watermark?</label>',
    buttons: [ 'ok', 'cancel' ]
} ); }

If you look at the final few lines of the code, I am attempting to call copyright and watermark but am obviously having issues with that due to them being defined within a function.

I've removed var from both, without success, so any support would be much appreciated.

Chris Mann
  • 77
  • 6
  • Define them outside the function, then reassign them inside the function. Or, return the data and use it there. – Carcigenicate Jul 20 '17 at 00:43
  • 1
    Almost sounds like a duplicate of https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call – elclanrs Jul 20 '17 at 00:43
  • I've since moved `var copyright = '';` and `var watermark = '';` to the top of my script (without the leading `var`). Placing `alert(copyright)` after the `xmlhttp.send();` line confirms the variable is being brought forward and the code after that works as intended. However, upon removing the `alert`, nothing. It's as though the variable value isn't bring brought forward. – Chris Mann Jul 20 '17 at 01:14
  • 1
    @elclanrs Agreed. @chris-mann needs to move the `finder.request` call to inside the `onreadystatechange` callback. – Jeff M Jul 20 '17 at 01:14
  • @JeffM Your suggestion seems to resolve the issue, to an extent. When an image in CKFinder is selected and a call for the copyright information is made, it works as intended. However, when the dialog box is closed and the process repeated for a different image, the content hasn't been updated. The `finder.request` is now immediately before `return false`. I believe I should be looking to move the `xmlhttp.open` and `xmlhttp.send` lines as well, but not sure where to. – Chris Mann Jul 20 '17 at 01:27

2 Answers2

3

The problem with dialog not updating is due to CKFinder's template caching mechanism. The first use of template will cache it.

The proper way to pass data to a dialog's template is by templateModel property.

function makeDialog( copyright, watermark ) {
    finder.request( 'dialog', {
        name: 'CopyrightDialog',
        title: 'Copyright Information',
        template: '<p>Type the name of the copyright holder:</p>' +
        '<input type="text" name="copyright" value="{{= it.copyright }}" placeholder="Image Credit...">' +
        '<p>Protect the image with a watermark?</p>' +
        '<label><input type="checkbox" name="watermark" value="watermark" {{= it.watermark }}>Enable Watermark?</label>',
        buttons: [ 'ok', 'cancel' ],
        templateModel: new Backbone.Model( {
            copyright: copyright,
            watermark: watermark
        } )
    } );
}

You should call this above function in your xmlhttp.onreadystatechange handler after copyright and watermark are created.

Also check dialog guide in docs.

jodator
  • 2,445
  • 16
  • 29
  • Brilliant - that's done the trick. I have a little tidying up to do with another section of the script, but shall post the finished version in due course. I have no plans to maintain this as an official plugin and am very much a novice at Javascript, so people are urged to use with caution. – Chris Mann Jul 20 '17 at 13:26
1

Having asked the people behind CKFinder whether there was any precedent for a plugin such as this, their response was 'no' so I'm happy to share my code.

As a starting point, I dipped into their 'Custom Dialog' sample code on Github and worked from there.

https://github.com/ckfinder/ckfinder-docs-samples/blob/master/CustomDialog/CustomDialog.js

My final result, posted below, adds an extra button to the main toolbar of CKFinder once an image has been selected. Once clicked, it gathers information from a JSON file - which is generated by a database - and displays this in a dialog box.

From here, I am able to edit information - in my case, copyright and watermark - and save directly to the database through an AJAX call to a PHP script.

Here is the Javascript code I have used. Again, I'm a novice at JS so if anybody is able to provide any improvements please feel free to do so.

CKFinder.define( [ 'jquery', 'backbone' ], function( jQuery, Backbone ) {
    'use strict';

    return {
        init: function( finder ) {

            // Use the white icon as default
            var icon = 'copyright-white.svg';

            // If the .ui-alt-icon class is present, use the black icon as an alternative
            if ( jQuery( 'body' ).hasClass( 'ui-alt-icon' ) ) {
                icon = 'copyright-black.svg';
            }
            this.addCss( '.ui-icon-share:after { background-image: url(' + this.path + 'icon/' + icon + '); }' );

            // Add a button to the "Main" toolbar.
            finder.on( 'toolbar:reset:Main:file', function( evt ) {
                var file = evt.data.file;
                evt.data.toolbar.push( {
                    name: 'Copyright',
                    label: 'Copyright',
                    priority: 105,
                    icon: 'share',
                    action: function() {
                        finder.request( 'imageCopyright', { file: file } );
                    }
                } );
            } );

            // Find the individual properties of the selected image
            function imageCopyright( data ) {

                // Retrieve the URL of the image (data was passed in finder.request)
                var path = data.file.getUrl();

                // Define credit and watermark
                var credit    = '';
                var watermark = '';

                // Use an AJAX call to retrieve the copyright information
                var xmlhttp = new XMLHttpRequest();
                xmlhttp.onreadystatechange = function() {

                    // If the request is complete and OK, pull the data for the selected image
                    if (this.readyState == 4 && this.status == 200) {
                        var json = JSON.parse(this.responseText);
                        for ( var i = 0; i < json.img.length; i++) {
                            if(json.img[i].path == path) {

                                // Overwrite the value of credit
                                credit += json.img[i].credit;

                                // If a watermark is required, overwrite the value of watermark
                                if(json.img[i].watermark == 1) {
                                    watermark += ' checked';
                                }
                            }
                        }

                        // Dialog Box Content
                        function makeDialog( credit, path, watermark ) {
                            finder.request( 'dialog', {
                                name: 'CopyrightDialog',
                                title: 'Copyright Information',
                                template: '<p>Type the name of the copyright holder:</p>' +
                                '<input type="text" name="credit" value="{{= it.credit }}" placeholder="Image Credit...">' +
                                '<p>Protect the image with a watermark?</p>' +
                                '<label><input type="checkbox" name="watermark" value="watermark"{{= it.watermark }}>Enable Watermark?</label>' +
                                '<input type="hidden" name="path" value="{{= it.path }}">',
                                buttons: [ 'ok', 'cancel' ],
                                templateModel: new Backbone.Model( {
                                    credit   : credit,
                                    path     : path,
                                    watermark: watermark
                                } )
                            } );
                        }
                        makeDialog( credit, path, watermark );
                    }
                };

                // JSON file in which copyright data is stored
                xmlhttp.open("GET", "path/to/data.json", true);
                xmlhttp.send();

                // Action to take upon 'ok' click
                finder.on( 'dialog:CopyrightDialog:ok', function( evt ) {

                    // Define the value of the copyright credit
                    var credit    = evt.data.view.$el.find( '[name="credit"]' ).val();

                    // Define the value of the image path
                    var path      = evt.data.view.$el.find( '[name="path"]' ).val();

                    // Define whether a watermark has been requested
                    var watermark = evt.data.view.$el.find( '[name="watermark"]' ).is( ':checked' );

                    // Destroy the dialog.
                    finder.request( 'dialog:destroy' );

                    // Determine whether a watermark was requested
                    if ( watermark === true ) {
                        watermark = 1;
                    } else {
                        watermark = 0;
                    }

                    // Send the copyright information to the database via an AJAX request
                    if (window.XMLHttpRequest) {
                        var xmlhttp = new XMLHttpRequest();
                    }
                    xmlhttp.open("POST", "path/to/database.php", true);
                    xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                    xmlhttp.send("path=" + path + "&watermark=" + watermark + "&credit=" + credit);
                    return false;
                } );
            }

            // Update the handler
            finder.setHandler( 'imageCopyright', imageCopyright, this );
        }
    };} );

Again, this isn't something I'll be maintaining as an official plugin but feel free to use for your own means.

Chris Mann
  • 77
  • 6