Extjs: Simple Image Gallery Using DataView and PHP

July 21st, 2010 by aditia rahman / 18 Comments  

     

Last week I tried some of the extjs dataview example, it really great, just my thought, why not creating simple image gallery with dataview? All that we need is provided in the example (the panel, fileinputfield, the xtemplate) and this is the page wire frame that I created using Pencil Firefox Plugins.

Ext JS Dataview Gallery Wire Frame

1. Div Continer (HTML)

<div id="container">
  <div id="left"></div>
  <div id="right">
      <div id="right-top"></div>
      <div id="right-bottom"></div>
  </div>
  <div class="clear"></div>
</div>

I will not explain the CSS code in here you can see on the code I provided below, just playing with float properties, the div element inside the body tag have to be rendered by the Ext Panel that we will create on the next step, div with “left” id for displaying the Ext Panel that have a DataView Component, div with “right-top” id for displaying the Ext FormPanel (to upload the image) and div with “right-bottom” id is for displaying simple image detail when one of the image in DataView clicked.

2. Div Left (HTML)

Now for the left, we need to create Ext Panel that has the DataView inside it, before start you need to add the file from the Ext example files, it located on “examples\view\data-view.css” and “examples\ux\DataView-more.js”, they used to styling the XTemplate in DataView and to add multiple selection using mouse drag, here the code for the left div

var store = new Ext.data.JsonStore({
    url: 'get-images.php',
    root: 'images',
    fields: [
        'name', 'url',
        { name: 'size', type: 'float' },
        { name: 'lastmod', type: 'date', dateFormat: 'timestamp' },
        'thumb_url'
    ]
});
store.load();

var tpl = new Ext.XTemplate(
    '<tpl for=".">',
        '<div class="thumb-wrap" id="{name}">',
        '<div class="thumb"><img src="{thumb_url}" title="{name}"></div>',
        '<span class="x-editable">{shortName}</span></div>',
    '</tpl>',
    '<div class="x-clear"></div>'
);

var tbar = new Ext.Toolbar({
    style: 'border:1px solid #99BBE8;'
});

tbar.add('->', {
    text: 'Delete',
    icon: 'img/delete.png',
    handler: function() {
        var records = datav.getSelectedRecords();
        if (records.length != 0) {
            var imgName = '';
            for (var i = 0; i < records.length; i++) {
                imgName = imgName + records[i].data.name + ';';
            }
            Ext.Ajax.request({
               url: 'delete.php',
               method: 'post',
               params: { images: imgName},
               success: function() {
                   store.load();
               }
            });
        }
    }
});

var datav = new Ext.DataView({
    autoScroll: true,
    store: store,
    tpl: tpl,
    autoHeight: false,
    height: 400,
    multiSelect: true,
    overClass: 'x-view-over',
    itemSelector: 'div.thumb-wrap',
    emptyText: 'No images to display',
    style: 'border:1px solid #99BBE8; border-top-width: 0',

    plugins: [
        new Ext.DataView.DragSelector(),
    ],

    prepareData: function(data){
        data.shortName = Ext.util.Format.ellipsis(data.name, 15);
        data.sizeString = Ext.util.Format.fileSize(data.size);
        data.dateString = data.lastmod.format("m/d/Y g:i a");
        return data;
    },

    listeners: {
        selectionchange: {
            fn: function(dv,nodes){
                var l = nodes.length;
                var s = l != 1 ? 's' : '';
                panelLeft.setTitle('Simple DataView Gallery ('+l+' image'+s+' selected)');
            }
        },
        click: {
            fn: function() {
                var selNode = datav.getSelectedRecords();
                tplDetail.overwrite(panelRightBottom.body, selNode[0].data);
            }
        }
    }
})

var panelLeft = new Ext.Panel({
    id: 'images-view',
    frame: true,
    width: 520,
    height: 200,
    autoHeight: true,
    layout: 'auto',
    title: 'Simple DataView Gallery (0 images selected)',
    items: [tbar, datav]
});
panelLeft.render('left');

Mostly I imitate the DataView Example, so the code almost the same the official example. First the store component is to getting the image data from the server by calling the get-image.php script, I modified a bit cause I want to separate the original image and the thumbnail image, here the code.

<?php
$dir = "img/ori/";
$dir_thumbs = "img/thumbs/";

$images = array();
$d = dir($dir);
while($name = $d->read()){
    if(!preg_match('/\.(jpg|gif|png)$/', $name)) continue;
    $size = filesize($dir.$name);
    $lastmod = filemtime($dir.$name)*1000;
    $thumb = "thumb_".$name;
    $images[] = array('name' => $name, 'size' => $size,
			'lastmod' => $lastmod, 'url' => $dir.$name,
            'thumb_url' => $dir_thumbs.$thumb);
}
$d->close();
$o = array('images'=>$images);
echo json_encode($o);

There is a toolbar inside the panel just add one function that is deleting the selected images, and the handler function calling the delete.php script, the variable that sent from the client is the multiple images name with “;” (semicolon) separator, so in the PHP it has to be exploded as an array, it something like this code below.

<?php

$dir = 'img/ori/';
$dir_thumbs = 'img/thumbs/';

$arrayImg = explode(";", $_POST['images']);

foreach($arrayImg as $imgname) {
    if ($imgname != "") {
        unlink($dir.$imgname);
        unlink($dir_thumbs.'thumb_'.$imgname);
    }
}

echo '{success: true}';

3. Div Top Right (HTML)

The div top right is the place for Ext FormPanel to uploading images, (similar to my previous post about multiple file upload in Extjs) this code use the Ext ux that you can find on the source examples\ux\fileuploadfield\FileUploadField.jsand the css file in folder œexamples\ux\fileuploadfield\css\fileuploadfield.css, in showed code below the maximum number file to upload is two images but in the provided code I make it 5 images to upload at a time.

var panelRightTop = new Ext.FormPanel({
    title: 'Upload Images',
    width: 270,
    renderTo: 'right-top',
    buttonAlign: 'center',
    labelWidth: 50,
    fileUpload: true,
    frame: true,
    items: [{
        xtype: 'fileuploadfield',
        emptyText: '',
        fieldLabel: 'Image 1',
        buttonText: 'Select a File',
        width: 200,
        name: 'img[]'
    }, {
        xtype: 'fileuploadfield',
        emptyText: '',
        fieldLabel: 'Image 2',
        buttonText: 'Select a File',
        width: 200,
        name: 'img[]'
    }],
    buttons: [{
        text: 'Upload',
        handler: function() {
            panelRightTop.getForm().submit({
                url: 'upload.php',
                waitMsg: 'Uploading ....',
                success: function(form, o) {
                    obj = Ext.util.JSON.decode(o.response.responseText);
                    if (obj.failed == '0' && obj.uploaded != '0') {
                        Ext.Msg.alert('Success', 'All files uploaded');
                    } else if (obj.uploaded == '0') {
                        Ext.Msg.alert('Success', 'Nothing Uploaded');
                    } else {
                        Ext.Msg.alert('Success',
                            obj.uploaded + ' files uploaded <br/>' +
                            obj.failed + ' files failed to upload');
                    }
                    panelRightTop.getForm().reset();
                    store.load();
                }
            });
        }
    }, {
        text: 'Reset',
        handler: function() {
            panelRightTop.getForm().reset();
        }
    }]
});

The upload.php have the  code to uploading image and checking whether the image can be uploaded, I limit max file size is 500 KB and the thumbnail is created to the image that pass the validation, the code for creating thumbnail is almost like my previous post, just modified to be a function and last there are two counter to counting the failed upload and the success upload, here the code.

<?php

function createThumb($img_file, $ori_path, $thumb_path, $img_type) {

    // get the image source
    $path = $ori_path;
    $img = $path.$img_file;
    switch ($img_type) {
        case "image/jpeg":
            $img_src = @imagecreatefromjpeg($img);
            break;
        case "image/png":
            $img_src = @imagecreatefrompng($img);
            break;
        case "image/x-png":
            $img_src = @imagecreatefrompng($img);
            break;
        case "image/gif":
            $img_src = @imagecreatefromgif($img);
            break;
    }
    $img_width = imagesx($img_src);
    $img_height = imagesy($img_src);

    $square_size = 100;

    // check width, height, or square
    if ($img_width == $img_height) {
        // square
        $tmp_width = $square_size;
        $tmp_height = $square_size;
    } else if ($img_height < $img_width) {
        // wide
        $tmp_height = $square_size;
        $tmp_width = intval(($img_width / $img_height) * $square_size);
        if ($tmp_width % 2 != 0) {
            $tmp_width++;
        }
    } else if ($img_height > $img_width) {
        $tmp_width = $square_size;
        $tmp_height = intval(($img_height / $img_width) * $square_size);
        if ($tmp_height % 2 != 0) {
            $tmp_height++;
        }
    }

    $img_new = imagecreatetruecolor($tmp_width, $tmp_height);
    imagecopyresampled($img_new, $img_src, 0, 0, 0, 0,
            $tmp_width, $tmp_height, $img_width, $img_height);

    // create temporary thumbnail and locate on the server
    $thumb = $thumb_path."thumb_".$img_file;
    switch ($img_type) {
        case "image/jpeg":
            imagejpeg($img_new, $thumb);
            break;
        case "image/png":
            imagepng($img_new, $thumb);
            break;
        case "image/x-png":
            imagepng($img_new, $thumb);
            break;
        case "image/gif":
            imagegif($img_new, $thumb);
            break;
    }

    // get tmp_image
    switch ($img_type) {
        case "image/jpeg":
            $img_thumb_square = imagecreatefromjpeg($thumb);
            break;
        case "image/png":
            $img_thumb_square = imagecreatefrompng($thumb);
            break;
        case "image/x-png":
            $img_thumb_square = imagecreatefrompng($thumb);
            break;
        case "image/gif":
            $img_thumb_square = imagecreatefromgif($thumb);
            break;
    }

    $thumb_width = imagesx($img_thumb_square);
    $thumb_height = imagesy($img_thumb_square);

    if ($thumb_height < $thumb_width) {
        // wide
        $x_src = ($thumb_width - $square_size) / 2;
        $y_src = 0;
        $img_final = imagecreatetruecolor($square_size, $square_size);
        imagecopy($img_final, $img_thumb_square, 0, 0,
                $x_src, $y_src, $square_size, $square_size);
    } else if ($thumb_height > $thumb_width) {
        // landscape
        $x_src = 0;
        $y_src = ($thumb_height - $square_size) / 2;
        $img_final = imagecreatetruecolor($square_size, $square_size);
        imagecopy($img_final, $img_thumb_square, 0, 0,
                $x_src, $y_src, $square_size, $square_size);
    } else {
        $img_final = imagecreatetruecolor($square_size, $square_size);
        imagecopy($img_final, $img_thumb_square, 0, 0,
                0, 0, $square_size, $square_size);
    }

    switch ($img_type) {
        case "image/jpeg":
            @imagejpeg($img_final, $thumb);
            break;
        case "image/png":
            @imagepng($img_final, $thumb);
            break;
        case "image/x-png":
            @imagepng($img_final, $thumb);
            break;
        case "image/gif":
            @imagegif($img_final, $thumb);
            break;
    }
}

$ori_dir = 'img/ori/';
$thumb_dir = 'img/thumbs/';

$allowedType = array(
    'image/jpeg', 'image/png', 'image/gif', 'image/x-png'
);

$uploaded = 0;
$failed = 0;

foreach($_FILES['img']['name'] as $key => $img) {
    if (in_array($_FILES['img']['type'][$key], $allowedType)) {
        // max upload file is 500 KB
        if ($_FILES['img']['size'][$key] <= 500000) {
            // upload file
            move_uploaded_file($_FILES['img']['tmp_name'][$key],
                    $ori_dir.$_FILES['img']['name'][$key]);

            // create thumbnail
            createThumb($_FILES['img']['name'][$key],
                $ori_dir, $thumb_dir,
                $_FILES['img']['type'][$key]);

            // count how many files uploaded
            $uploaded++;
        } else {
            $failed++;
        }
    } else if ($_FILES['img']['type'][$key] != '') {
        $failed++;
    }
}

echo '{success: true, failed: '.$failed.', uploaded: '.$uploaded.'}';

?>

4. Div Top Bottom (HTML)

The last part is the Ext Panel to displaying the image detail, it use the Ext XTemplate and put inside the panel, this dynamic value of the panel is triggered by the DataView click event, that declared before.

var tplDetail = new Ext.XTemplate(
    '<div class="details">',
        '<tpl for=".">',
            '<img src="{thumb_url}"><div class="details-info">',
            '<b>Image Name:</b>',
            '<span>{name}</span>',
            '<b>Size:</b>',
            '<span>{sizeString}</span>',
            '<b>Last Modified:</b>',
            '<span>{dateString}</span>',
            '<span><a href="{url}" target="_blank">view original</a></span></div>',
        '</tpl>',
    '</div>'
);

var panelRightBottom = new Ext.Panel({
    title: 'Image Detail',
    frame: true,
    width: 270,
    height: 255,
    id: 'panelDetail',
    renderTo: 'right-bottom',
    tpl: tplDetail
});

Below you can see the sample screen shot of this post, and you can download the source or see online demo, but sorry I disabled  the upload and delete feature for online demo.

Extjs DataView Gallery

View Demo | Download Source

        submit to reddit Delicious

18 Comments Leave a Comment Subscribe RSS

Leave a Comment