Added Image Upload and Gallery features
authorChuck Scott <cscott@gaslightmedia.com>
Tue, 7 Jul 2015 17:13:18 +0000 (13:13 -0400)
committerChuck Scott <cscott@gaslightmedia.com>
Tue, 7 Jul 2015 17:13:18 +0000 (13:13 -0400)
12 files changed:
classes/data/dataImages.php
config/plugin.ini
controllers/admin.php
css/admin.css
js/imageUpload/imageUpload.css
js/imageUpload/imageUpload.js
lib/GlmDataAbstract/DataAbstract.php
lib/smartyTemplateSupport.php
models/admin/ajax/imageUpload.php [new file with mode: 0644]
models/admin/member/memberImage.php [deleted file]
models/admin/member/memberInfo.php
views/admin/member/memberInfo.html

index a5e66e3..1b33476 100644 (file)
@@ -235,6 +235,146 @@ class GlmDataImages extends GlmDataAbstract
         return $imageGallery;
 
     }
+
+    /*
+     * Set gallery image positions
+     *
+     * Reads a submitted comma-delimited list of ID numbers creted by jQuery sortable()
+     * and updates the "position" field in the images table for those entries to be
+     * in the requested sequential order.
+     *
+     * @param int $refType Reference Type
+     * @param int $refID Reference entry ID
+     * @param string $orderField Name of SUBMIT field containing sort order.
+     *
+     * @return boolean True if successful
+     *
+     */
+    public function galleryPositionOrder($refType, $refID, $orderField)
+    {
+
+        // Check for and retrieve the order field name
+        if (isset($_REQUEST[$orderField]) && trim($_REQUEST[$orderField]) == '') {
+
+            if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) {
+                glmMembersAdmin::addNotice('<b>dataImages: </b>galleryPositionOrder() unable to find '.$orderField.'data.', 'Process');
+            }
+
+            return false;
+        }
+
+        // Break order out into an array
+        $order = explode(',', $_REQUEST[$orderField]);
+        if (count($order) == 0) {
+            return false;
+        }
+
+        if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) {
+            glmMembersAdmin::addNotice($order, 'DataBlock', 'Image Gallery Sort Order');
+        }
+
+        // Update image entries with new sort order;
+        $pos = 0;
+        foreach($order as $i) {
+            $sql = "UPDATE ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."images SET position = $pos WHERE id = $i;";
+            $this->wpdb->query($sql);
+            $pos++;
+
+            $queryError = $this->wpdb->last_error;
+            if ($queryError) {
+
+                if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) {
+                    glmMembersAdmin::addNotice("<b>&nbsp;&nbsp;Error setting position order for images.</b><br>$queryError", 'Alert');
+                }
+
+                return false;
+            }
+        }
+
+        return true;
+
+    }
+
+    /*
+     * Process image gallery titles, decriptions, deletions, and image position ordering.
+     *
+     * @param int $refType Reference Type
+     * @param int $refID Reference entry ID
+     * @param string $orderField Name of SUBMIT field containing sort order.
+     *
+     * @return boolean True if successful
+     *
+     */
+    public function galleryImageDataUpdate($refType, $refID, $orderField)
+    {
+
+        // Re-order image gallery images
+        $this->galleryPositionOrder($refType, $refID, $orderField);
+
+        // Update text for title and descriptions
+        reset($_REQUEST['galleryImage_caption']);
+        while (list($k, $v) = each($_REQUEST['galleryImage_caption'])) {
+            $id = ($k -0);
+
+            // Sanitize input
+            $caption = sanitize_text_field( $_REQUEST['galleryImage_caption'][$k] );
+            $descr = sanitize_text_field( $_REQUEST['galleryImage_descr'][$k] );
+
+            // Update data for this image
+            $sql = "UPDATE ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."images SET caption = '$caption', descr = '$descr' WHERE id = $id;";
+            $this->wpdb->query($sql);
+
+        }
+
+        // Check for an image deletion
+        if (isset($_REQUEST['galleryImage_delete']) && count($_REQUEST['galleryImage_delete']) > 0) {
+
+            // For each delete selected
+            reset($_REQUEST['galleryImage_delete']);
+            while (list($k, $v) = each($_REQUEST['galleryImage_delete'])) {
+                $id = ($k -0);
+
+                // If a valid key and delete request is in fact set
+                if ($id > 0 && $v == 'on') {
+
+                    // Get the data for this image
+                    $sql = "SELECT * FROM ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."images WHERE id = $id;";
+                    $imgData = $this->wpdb->get_row($sql, ARRAY_A);
+
+                    // Do we have data?
+                    if ($imgData != null && trim($imgData['file_name']) != '') {
+
+                        // Delete the image from the gallery
+                        $sql = "DELETE FROM ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."images WHERE id = $id;";
+                        $this->wpdb->query($sql);
+
+                        // Also delete all copies of the image
+                        reset($this->config['imageSizes']);
+                        while (list($k, $v) = each($this->config['imageSizes'])) {
+
+                            if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) {
+                                glmMembersAdmin::addNotice("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Deleting size $k.", 'Process');
+                            }
+
+                            // Delete each image size
+                            unlink(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$k.'/'.$imgData['file_name']);
+
+                        }
+
+                        // Also delete the original
+                        unlink(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/original/'.$imgData['file_name']);
+
+                    }
+                }
+            }
+        }
+
+        // Get updated image gallery
+        $imageGallery = $this->getGallery($refType, $refID);
+        return $imageGallery;
+
+    }
+
 }
 
 ?>
\ No newline at end of file
index fad26a1..e3290a1 100644 (file)
@@ -166,21 +166,33 @@ memb_access_numb['Archived'] = 90
 ;
 ref_type[0] = 'None'
 ref_type[10] = 'Member'
-ref_type[20] = 'Location'
-ref_type[30] = 'Facility'
-ref_type[40] = 'Activity'
-ref_type[50] = 'Golf'
-ref_type[60] = 'Restaurant'
-ref_type[70] = 'Contact'
+ref_type[20] = 'MemberInfo'
+ref_type[30] = 'Location'
+ref_type[40] = 'Facility'
+ref_type[50] = 'Activity'
+ref_type[60] = 'Golf'
+ref_type[70] = 'Restaurant'
+ref_type[80] = 'Contact'
+
+ref_type_table[0] = ''
+ref_type_table[10] = 'members'
+ref_type_table[20] = 'member_info'
+ref_type_table[30] = 'locations'
+ref_type_table[40] = 'facilities'
+ref_type_table[50] = 'activities'
+ref_type_table[60] = 'golf'
+ref_type_table[70] = 'restaurants'
+ref_type_table[80] = 'contacts'
 
 ref_type_numb['None'] = 0
 ref_type_numb['Member'] = 10
-ref_type_numb['Location'] = 20
-ref_type_numb['Facility'] = 30
-ref_type_numb['Activity'] = 40
-ref_type_numb['Golf'] = 50
-ref_type_numb['Restaurant'] = 60
-ref_type_numb['Contact']  = 70
+ref_type_numb['MemberInfo'] = 20
+ref_type_numb['Location'] = 30
+ref_type_numb['Facility'] = 40
+ref_type_numb['Activity'] = 50
+ref_type_numb['Golf'] = 60
+ref_type_numb['Restaurant'] = 70
+ref_type_numb['Contact']  = 80
 
 ;
 ; Facility Type
index 064bc73..304fcb4 100644 (file)
@@ -26,6 +26,9 @@
  */
 $GLOBALS['glmMembersAdminValidActions'] = array(
 
+        'ajax' => array(
+                'imageUpload'
+        ),
         'dashboardWidget' => array(
                 'index'
         ),
@@ -41,8 +44,7 @@ $GLOBALS['glmMembersAdminValidActions'] = array(
                 'locations',
                 'facilities',
                 'activities',
-                'accommodations',
-                'memberImage',      // Member Image Processing via AJAX
+                'accommodations'
         )
         ,
         'configure' => array(
@@ -222,10 +224,10 @@ class glmMembersAdmin extends GlmPluginSupport
                 ));
 
         // Add AJAX image upload action
-        add_action( 'wp_ajax_glm_members_image_upload',
+        add_action( 'wp_ajax_glm_members_admin_ajax',
             array(
                 $this,
-                'glmMembersImageUpload'
+                'glmMembersAdminAjax'
             )
         );
 
@@ -233,35 +235,26 @@ class glmMembersAdmin extends GlmPluginSupport
     }
 
     /**
-     * Image Upload AJAX Target
+     * Admin Ajax Target
+     *
+     * Acccepts all AJAX requests for member db admin.
+     *
+     * Requests are submitted to the WordPress AJAX processor with "action" specified
+     * as "glm_members_admin_ajax". The AJAX call must also specify the target table
+     * the image entry will point to and the target table record ID of the record with
+     * which image will be associated. Only valid tables are accepted according to the
      *
-     * Acccepts image uploads and routes them to the appropriate
-     * model file.
      *
      * @return void
      * @access public
      */
-    public function  glmMembersImageUpload()
+    public function  glmMembersAdminAjax()
     {
-        // Get the model menu
-        if (isset($_REQUEST['glm_menu']) && $_REQUEST['glm_menu'] != '') {
-            $a = sanitize_text_field($_REQUEST['glm_menu']);
-            if ($a != '') {
-                $menuItem = $a;
-            } else {
-                // No menu provided - terminating
-                trigger_error ( 'ERROR: No "glm_menu" string provided in POST array.', E_USER_ERROR);
-            }
-        } else {
-            // Missing glm_menu Item in post array.
-            trigger_error ( 'ERROR: No "glm_menu" item in POST array.', E_USER_ERROR);
-        }
-
         // Get the model action
         if (isset($_REQUEST['glm_action']) && $_REQUEST['glm_action'] != '') {
             $a = sanitize_text_field($_REQUEST['glm_action']);
             if ($a != '') {
-                $Action = $a;
+                $glmAction = $a;
             } else {
                 // No menu provided - terminating
                 trigger_error ( 'ERROR: No "glm_action" string provided in POST array.', E_USER_ERROR);
@@ -271,18 +264,16 @@ class glmMembersAdmin extends GlmPluginSupport
             trigger_error ( 'ERROR: No "glm_action" itme in POST array.', E_USER_ERROR);
         }
 
-        // If menu item and action aren't valid, quit now.
-        if (! isset($GLOBALS['glmMembersAdminValidActions'][$menuItem]) ||
-            ! in_array($Action, $GLOBALS['glmMembersAdminValidActions'][$menuItem])) {
+        // Check for a valid action
+        if (!in_array($glmAction, $GLOBALS['glmMembersAdminValidActions']['ajax'], true)) {
 
             // Menu item/Action not in valid actions array
-            trigger_error ( 'ERROR: The specified menu item and action are not valid.', E_USER_ERROR);
+            trigger_error ( 'ERROR: The specified action is not valid.', E_USER_ERROR);
         }
 
         // Build model and path and class names
-        $modelName = GLM_MEMBERS_PLUGIN_PATH . '/models/admin/' . $menuItem .
-                 '/' . $Action . '.php';
-        $className = 'GlmMembersAdmin_' . $menuItem . '_' . $Action;
+        $modelName = GLM_MEMBERS_PLUGIN_PATH . '/models/admin/ajax/'.$glmAction.'.php';
+        $className = 'GlmMembersAdmin_ajax_'.$glmAction;
 
         // If model file doesn't exist - we have an error
         if (!file_exists($modelName)) {
@@ -668,8 +659,7 @@ class glmMembersAdmin extends GlmPluginSupport
 
             // Verify Menu item and action using array at top of this file
             if (! isset($GLOBALS['glmMembersAdminValidActions'][$menuItem]) ||
-                     ! in_array($action,
-                            $GLOBALS['glmMembersAdminValidActions'][$menuItem])) {
+                     ! in_array($action, $GLOBALS['glmMembersAdminValidActions'][$menuItem])) {
 
                 if (GLM_MEMBERS_PLUGIN_ADMIN_DEBUG) {
                     $this->addNotice('<b>Error in Admin Controller:</b> Requested Menu Item is invalid!', 'Alert');
@@ -808,8 +798,11 @@ class glmMembersAdmin extends GlmPluginSupport
         $smarty->templateAssign ( 'glmPluginName', GLM_MEMBERS_PLUGIN_NAME );
         $smarty->templateAssign ( 'glmPluginMediaURL', GLM_MEMBERS_PLUGIN_MEDIA_URL );
         $smarty->templateAssign ( 'thisYear', date ( 'Y' ) );
+        $smarty->templateAssign ( 'ref_type_numb', $this->config['ref_type_numb']);
         $smarty->templateAssign ( $this->config['term']);
+        $smarty->templateAssign ( 'term', $this->config['term']);
         $smarty->templateAssign ( $this->config['phrase']);
+        $smarty->templateAssign ( 'term', $this->config['phrase']);
         $smarty->templateAssign ( 'googleMapsBrowserApiKey', $this->config['googleMapsApiKey']);
 
         // Add data from model to Smarty template
index d8b629a..a42055e 100644 (file)
@@ -50,7 +50,7 @@
 .glm-item-container {
     border: 1px #ccc solid;
     padding: .4em;
-    background: #f8f8f8;
+    background: #fff;
 }
 
 /* Admin Tabs */
     background-color: #eee;
     font-size: .8em;
 }
+
+.glm-galleryContainer
+{
+    clear: both;
+    border: 1px #ccc solid;
+    height: 8em;
+    padding: .2em;
+    margin: .4em .1em .4em .1em;
+    background-color: #eee;
+}
+.glm-galleryImages
+{
+}
+
+.glm-imageGalleryContainer
+{
+    border: 1px #ddd solid !important;
+    dragable
+}
+.glm-galleryImage
+{
+    float: left;
+    height: 100%;
+    width: 20%;
+    overflow: hidden;
+}
+.glm-galleryImage img
+{
+  position: relative;
+  top: 0px;
+  right: 0px;
+  bottom: 0px;
+  left: 0px;
+  max-height: 100%;
+  max-width: 100%;
+}
+.glm-imageDialog
+{
+    display: none;
+    height: auto;
+    width: auto;
+}
+.glm-galleryImageData
+{
+    float: right;
+    height: 90%;
+    width: 78%;
+    text-align: left;
+    white-space: nowrap;
+}
+.glm-galleryImageData textarea
+{
+    height: 4em;
+    width: 70%;
+    resize: none;
+}
index 660dd1e..68f19ee 100644 (file)
@@ -17,8 +17,9 @@
     cursor: default;
     margin-left: auto;
     margin-right: auto;
-    width: 75%;
-    height: 5em;
+    margin-bottom: 1em;
+    width: 90%;
+    height: 2em;
 }
 .glm-imageDrop
 {
     box-shadow: 10px 10px 5px grey;
 }
 
-.glm-imageGalleryImages
-{
-}
-
 .glm-statusTable 
 {
     width: 100%;
 /*    table-layout: fixed; */
     line-height: 80%;
 }
-.glm-statusImageTD
-{
-/*    width: 25%; */
-    max-width: 100%;
-    overflow: hidden;
-    padding: 3px;
-    display: table-cell;
-    vertical-align: middle; 
-}
 .glm-statusPrompt 
 {
     width: 10%;
     padding: 0px;    
     text-align: left;
 }
+.glm-statusImageTD
+{
+    max-width: 100%; 
+    overflow: hidden;
+    padding: 13px;
+    display: table-cell;
+    vertical-align: middle; 
+}
 .glm-statusImageContainer
 {
-    display: block;
+    display: block; 
     width: 100px;
+    height: 100%;
 }
-.glm-statusImage
+.glm-statusImageContainer img
 {
-    height: 5.1em;
-    width: auto;
-    display: block;
-    margin-left: auto;
-    margin-right: auto;
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    bottom: 0px;
+    left: 0px;
+    /* Maintain aspect ratio */
+    max-height:100%;
+    max-width: 100%;
 }
 
 .glm-progressBarContainer 
     background-color: blue;
     width: 0%;
 }
-
index 5a64419..7a88797 100644 (file)
@@ -1,24 +1,34 @@
 /*
- * HTML5 Image Upload Support
+ * Gaslight Media HTML5 Image Upload Support for glm-member-db plugin.
+ * 
+ * Author: Chuck Scott <cscott@gaslightmedia.com>
  * 
  * Developed from information in 
  *     http://www.sitepoint.com/html5-file-drag-and-drop/
  *  http://hayageek.com/drag-and-drop-file-upload-jquery/
+ *  
+ * See also the following methods in classes/data/dataImages.php in this plugin.
+ *     galleryPositionOrder()
+ *  galleryImageDataUpdate()
+ *  
  */
 
-
-/****** NEED TO ADD FILE SIZE TEST AND USER NOTICE ALERT *******/
-
 jQuery(document).ready(function($) {
 
        
        var drop;
        var recordID;
-       var menuItem;
-       var action;
+       var refType;
+       var recordID;
+       var maxFileSize;
+       var allowedTypes;
        var files;
-       
-       // Setup Drag and Drop when Add and 
+       var uploadStatusTemplate;
+       var imageDataTemplate;
+       var galleryImages;
+       var newImageAdded = false;
+
+       // Setup Drag and Drop when Add and
        if (window.File && window.FileList && window.FileReader) {
        
                // is XHR2 available?
@@ -43,29 +53,31 @@ jQuery(document).ready(function($) {
                            e.preventDefault();
                        });
                        
-                       // For each image drop area
+                       // For each image drop area on the page
                        $('.glm-imageDrop').each(function() {
                                drop = $(this);
-                               initDrop();
-                               
-                               // **** NEED TO DEAL WITH DELAY TILL THE PRIOR DROP AREA HAS BEEN PROCESSED ***
-                               
+                               initDrop();                             
                        });
 
                } else {
-                       $('.glm-noImageDropText').removeClass('glm-imageItemHidden');
+                       alert('Your Web browser does not support "Drag & Drop" image uploads using "XHR2".\nThat capability is required to upload images for the image gallery on this page.\nConsider upgrading your browser.');
                }
 
        }
        
-       // Setup a single drop area
+       // Setup an image drop area
        function initDrop() {
 
-               // Get ID for the record number to which these images belong.
-               recordID = drop.attr("data-id");
-               menuItem = drop.attr("data-menuItem");
-               action = drop.attr("data-action");
+               // Get information from the field on where this image file is going
+               refType = drop.attr("data-refType");
+               recordID = drop.attr("data-recordID");
+               maxFileSize = drop.attr("data-maxFileSizeKB") * 1000;
+               allowedTypes = drop.attr("data-allowedTypes");
                
+               uploadStatusTemplate = drop.children('.glm-imageUploadStatusTemplate').html();
+               imageDataTemplate = drop.children('.glm-galleryImageDataTemplate').html();
+               galleryImages = drop.parent().parent().children('.glm-galleryImages');
+
                // Change drop destination appearance only when dragging over a file.
                drop.on('dragenter', function(e){
                        e.stopPropagation();
@@ -92,12 +104,11 @@ jQuery(document).ready(function($) {
 
        }
        
-       /* 
-        * This function sets up AJAX processing of the list of files.
-        * It then fires off the processFile() function to do the first
-        * file. When the AJAX call in sendFileToServer() completes,
-        * the complete: function will call processFile() again to
-        * do the next file, if one exists.  
+       /*
+        * This function sets up AJAX processing of the list of files. It then fires
+        * off the processFile() function to do the first file. When the AJAX call
+        * in sendFileToServer() completes, the complete: function will call
+        * processFile() again to do the next file, if one exists.
         */
        var thisFile = 0;
        var numbFiles = 0;
@@ -111,21 +122,11 @@ jQuery(document).ready(function($) {
                // Start with the first file
                processFile();
                
-/*                     var fd = new FormData();
-               fd.append('file', file);
-
-                       var status = new createStatusbar(file, drop);
-               statusArea.fadeIn( function() {
-                       sendFileToServer(fd,status);
-               });
-*/             
-//             statusArea.addClass('glm-imageItemHidden');
-                       
        }
        
        /*
-        * Process the current file - AJAX complete: will call back to this 
-        * function for the next file
+        * Process the current file - AJAX complete: will call back to this function
+        * for the next file
         */ 
        function processFile() {
 
@@ -136,22 +137,63 @@ jQuery(document).ready(function($) {
                        
                        // Setup field pairs for sending in request
                        var fd = new FormData();
-               fd.append('file', file);
+                       
+                       // Add file upload information
+                   fd.append('file', file);
+                   
+                       /*
+                        * Add "action" post parameter specifying where WordPress should
+                        * route the request. In this case we are routing this AJAX request
+                        * to the admin controller glmMembersAdminAjax() method which will
+                        * route the request to the proper file in the models/admin/ajax
+                        * directory of this plugin.
+                        * 
+                        * see "add_action( 'wp_ajax_glm_members_admin_ajax',..." in admin
+                        * controller.
+                        */
+                       fd.append( 'action', 'glm_members_admin_ajax' );
+
+                       // Tell admin controller where to route AJAX call
+                       // (models/admin/ajax/imageUpload.php)
+                       fd.append('glm_action', 'imageUpload');
+
+                       // Tell image upload AJAX processor who the image is for
+                       fd.append('glm_refType', refType);
+                       fd.append('glm_refDest', recordID);
 
                // Setup status display area
                        var status = new createStatusbar(file, thisFile, numbFiles);
+
                statusArea.fadeIn( function() {
-                       
-                       // When status has faded in, Send the files
-                       sendFileToServer(fd, status, recordID);
                
+                       // Check image size and alert the user if it's too big
+                       if (file.size > maxFileSize) {
+                               
+                               alert("This image file is too large to process.\nTo use this image, consider resizing it before uploading.\n\nMaximum image size is " + (maxFileSize/1000) + "KB.");
+                               statusArea.fadeOut();
+                               processFile();
+
+                       // Check the image mime type and alert the user if it's not
+                               // permitted
+                       } else if (allowedTypes.indexOf(file.type) < 0) {
+
+                               alert("The file is not an accepted image type.\nTo use this image, consider resaving it as a differnt image type.\n");
+                               statusArea.fadeOut();
+                               processFile();
+                               
+                       } else {
+                               // When status has faded in, Send the files
+                               sendFileToServer(fd, status);
+                       }
+                       
                });
 
                thisFile++;
                        
                } else {
-                       
-                       // done
+
+                       // Reset expanded image and sortable events
+                       setupExpandedImageEvents();
                        
                }
 
@@ -162,8 +204,8 @@ jQuery(document).ready(function($) {
                var statusDone = false;
                
                /*
-                * We need to redefine these values inside this function
-                * so the reader.onload function can see them.
+                * We need to redefine these values inside this function so the
+                * reader.onload function can see them.
                 */
                var curFile = thisFile + 1;
                var lastFile = numbFiles;
@@ -175,30 +217,22 @@ jQuery(document).ready(function($) {
                statusArea.html('');
 
                // If file is an image
-               var regex = /^([a-zA-Z0-9\s_\\.\-:])+(.jpg|.jpeg|.gif|.png|.bmp)$/;
+// var regex = /^([a-zA-Z0-9\s_\\.\-:])+(.jpg|.jpeg|.gif|.png|.bmp)$/;
 
-        if (regex.test(file.name.toLowerCase())) {
+// if (regex.test(file.name.toLowerCase())) {
 
                // Create HTML5 file reader and load image
                var reader = new FileReader();
                reader.onload = function (e) {
                
-                       // Add file information to status area
-                       statusbar =
-                               '<b><u>Uploading Image {thisFile} of {numbFiles}</u></b>' +
-                               '<table class="glm-statusTable">' +
-                               '       <tr><td class="glm-statusImageTD" rowspan="5"><div class="glm-statusImageContainer"><img class="glm-statusImage" src="{fileImage}"></div></td></tr>' +
-                               '       <tr><td class="glm-statusPrompt">Name: </td><td class="glm-statusValue">{fileName}</td></tr>' +
-                               '       <tr><td class="glm-statusPrompt">Type: </td><td class="glm-statusValue">{fileType}</td></tr>' +
-                               '       <tr><td class="glm-statusPrompt">Size: </td><td class="glm-statusValue">{fileSize}</td></tr>' +
-                               '       <tr><td class="glm-statusPrompt">Progress: </td><td class="glm-statusValue"><div class="glm-progressBarContainer"><div class="glm-progressBar"></div></div></td></tr>' +
-                               '</table>'
-                       ;
-                       statusbar = statusbar.replace('{thisFile}', curFile);
-                       statusbar = statusbar.replace('{numbFiles}', lastFile);
-                       statusbar = statusbar.replace('{fileImage}', e.target.result);
-                       statusbar = statusbar.replace('{fileName}', file.name);
-                       statusbar = statusbar.replace('{fileType}', file.type);
+                       // Using a copy of the supplied template, add file information
+                               // to statusbar
+                       statusbar = uploadStatusTemplate;
+                       statusbar = statusbar.replace('{ thisFile }', curFile);
+                       statusbar = statusbar.replace('{ numbFiles }', lastFile);
+                       statusbar = statusbar.replace('{ fileImage }', e.target.result);
+                       statusbar = statusbar.replace('{ fileName }', file.name);
+                       statusbar = statusbar.replace('{ fileType }', file.type);
 
                        // Fix up file size string and replace that
                        var sizeStr="";
@@ -212,37 +246,29 @@ jQuery(document).ready(function($) {
                 {
                     sizeStr = sizeKB.toFixed(2)+" KB";
                 }
-                       statusbar = statusbar.replace('{fileSize}', sizeStr);
+                       
+                       // If a large file, notify user it will take time.
+                       if (file.size > 100000 && file.type == 'image/png') {
+                               sizeStr += ' -- NOTE: Processing for this image may be slow!';
+                       }
+
+                       statusbar = statusbar.replace('{ fileSize }', sizeStr);
                        
                        // Assign the HTML to the status area
                        statusArea.html(statusbar);
 
                }
             reader.readAsDataURL(file);
-        } else {
-            alert(file.name + " is not a valid image file.");
-            statusArea.addClass('glm-imageItemHidden');
-            return false;
-        }
                
            this.setProgress = function(progress)
            {
                statusArea.find('.glm-progressBar').css('width', progress + '%');
-               
-/*             
-               var progressBarWidth =progress*this.progressBar.width()/ 100;  
-               this.progressBar.find('div').animate({ width: progressBarWidth }, 10).html(progress + "% ");
-               if(parseInt(progress) >= 100)
-               {
-                   this.abort.hide();
-               }
-*/             
            }
 
            this.setAbort = function(jqxhr)
            {
                var sb = this.statusbar;
-               this.abort.click(function()
+               $('#imageUploadCancel').click(function()
                {
                    jqxhr.abort();
                    sb.hide();
@@ -251,19 +277,12 @@ jQuery(document).ready(function($) {
        }
        
        /*
-        * Sends file to image plugin via AJAX submission targeting
-        * WordPress AJAX handling. An image upload processor has been
-        * declared with an add_action() in the admin controller 
-        * specifying the function containing that processor. We tell 
-        * WordPress how to route the submission using the "action" 
-        * post parameter. This matches everything after "wp_ajax_"
-        * in the action name. 
+        * Sends file to image plugin via AJAX submission targeting WordPress AJAX
+        * handler.
         */
-       function sendFileToServer(fd, status, recordID)
+       function sendFileToServer(fd, status)
        {
 
-               fd.append( 'action', 'glm_members_image_upload' );
-
            var jqXHR=$.ajax({
                    xhr: function() {
                    var xhrobj = $.ajaxSettings.xhr();
@@ -281,18 +300,41 @@ jQuery(document).ready(function($) {
                    }
                    return xhrobj;
                },      
-               url: ajaxurl + '?glm_menu=' + menuItem + '&glm_action=' + action + '&memberInfoID=' + recordID,         // WordPress admin AJAX url
+               url: ajaxurl,
                type: "POST",
                contentType:false,
                processData: false,
                cache: false,
                data: fd,
                success: function(data){
-alert(data);                   
-//                     status.clearStatus();
-//                 status.setProgress(100);
-        
-//                 $("#status1").append("File upload Done<br>");           
+
+                               // Parse returned data
+                               fileData = JSON.parse(data);
+                               
+                               // Check for success
+                               if (fileData.status) {                  
+
+                               // Add image(s) to gallery display
+                               for (f of fileData.files) {
+               
+                                       // Using a copy of the supplied template, add image information
+                                       imageData = imageDataTemplate;
+                                       imageData = imageData.replace(/{ id }/g, f.id);
+                                       imageData = imageData.replace(/\{ filename \}/g, f.newFileName);
+                                       galleryImages.prepend(imageData);
+
+                                       // Enable the fields that were just added
+                                       $("#" + f.id + " input, #" + f.id + " textarea").removeAttr('disabled');
+
+                                       // Prepend image ID to position order input field
+                                       $("#galleryPositionOrder").val(f.id + ',' + $("#galleryPositionOrder").val());
+                                       
+                               }
+
+                               } else {
+                                       alert('Upload failed');                                 
+                               }
+                       
                },
                complete: function() {
                        
@@ -308,7 +350,42 @@ alert(data);
            }); 
            
            return jqXHR;
-//         status.setAbort(jqXHR);
+// status.setAbort(jqXHR);
        }
+       
+       function setupExpandedImageEvents() {
+
+               // Handle expanded images on hover for image gallery
+           $('.glm-galleryImage').on("mouseenter", function(){
+                   id = $(this).attr("data-id");
+                   var imgTitle = $("#galleryImage_caption_" + id).val();                  
+                   $('#glm-galleryImageLarger_' + id).dialog({
+                       title: imgTitle,
+                       dialogClass: "no-close",
+                       autoOpen: true,
+                       resizable: false,
+                       width: 'auto',
+                       minWidth: 100,
+                       minHeight: 100,
+                       create: function() {
 
+                               $(this).dialog('option', 'maxHeight', $(window).height() * .9);        
+                               $(this).dialog('option', 'maxWidth', $(window).width() * .9);        
+                           },
+                       position: { my: "left+10 top+10", at: "right top", of: $(this), collision: "fit"  }
+                   });           
+           });
+           $('.glm-galleryImage').on("mouseleave", function(){
+               id = $(this).attr("data-id");
+               $('#glm-galleryImageLarger_' + id).dialog("close");
+           });
+           $('.glm-galleryImages').sortable({
+               update: function(event, ui) {
+                       var sortedIDs = $(this).sortable('toArray');
+                       $('#galleryPositionOrder').val(sortedIDs);
+               }
+           });
+
+       }
+       setupExpandedImageEvents();
 });
index c99a4c1..a0e26ca 100755 (executable)
@@ -2317,7 +2317,7 @@ abstract class GlmDataAbstract
 
                 // Get the desired file name and add a timestamp to it to ensure that it's unique
                 $fInfo = pathinfo($_FILES[$as.'_new']['name']);
-                $newFilename = $prefix.$fInfo['filename'].'_'.time().'.'.$fInfo['extension'];
+                $newFilename = strtolower($fInfo['filename'].'_'.time().'.'.$fInfo['extension']);
 
                 // Get image temp file name - Not currently using, but should be using to check for resizing sanity
                 $size = $newImage->get_size();
index 07ee1ef..e4e926f 100644 (file)
@@ -86,7 +86,7 @@ class smartyTemplateSupport {
         *
         * @return void
         */
-       public function templateAssign($param, $value = null) {
+       public function templateAssign($param, $value = null, $prefix = '') {
 
                // If this is a single assignment
                if ($value !== null) {
@@ -96,7 +96,7 @@ class smartyTemplateSupport {
                } elseif (is_array ( $param )) {
 
                        while ( list ( $key, $value ) = each ( $param ) ) {
-                               $this->template->assign ( $key, $value );
+                           $this->template->assign ( $prefix.$key, $value );
                        }
                }
        }
diff --git a/models/admin/ajax/imageUpload.php b/models/admin/ajax/imageUpload.php
new file mode 100644 (file)
index 0000000..bcba5dd
--- /dev/null
@@ -0,0 +1,252 @@
+<?php
+
+/**
+ * Gaslight Media Members Database
+ * Image Upload by AJAX
+ *
+ * PHP version 5.5
+ *
+ * @category glmWordPressPlugin
+ * @package  glmMembersDatabase
+ * @author   Chuck Scott <cscott@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @version  0.1
+ */
+
+// Load Members data abstract
+require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php');
+
+/*
+ * This class performs the work of handling images passed to it via
+ * an AJAX call that goes through the WorPress AJAX Handler.
+ *
+ */
+class GlmMembersAdmin_ajax_imageUpload extends GlmDataImages
+{
+
+    /**
+     * WordPress Database Object
+     *
+     * @var $wpdb
+     * @access public
+     */
+    public $wpdb;
+    /**
+     * Plugin Configuration Data
+     *
+     * @var $config
+     * @access public
+     */
+    public $config;
+
+    /*
+     * Constructor
+     *
+     * This contructor sets up this model. At this time that only includes
+     * storing away the WordPress data object.
+     *
+     * @return object Class object
+     *
+     */
+    public function __construct ($wpdb, $config)
+    {
+
+        // Save WordPress Database object
+        $this->wpdb = $wpdb;
+
+        // Save plugin configuration object
+        $this->config = $config;
+
+        // Run constructor for members data class
+        parent::__construct(false, false);
+
+    }
+
+    /*
+     * Perform Model Action
+     *
+     * This method does the work for this model and returns any resulting data
+     *
+     * @return array Status and data array
+     *
+     * 'status'
+     *
+     * True if successfull and false if there was a fatal failure.
+     *
+     * 'menuItemRedirect'
+     *
+     * If not false, provides a menu item the controller should
+     * execute after this one. Normally if this is used, there would also be a
+     * modelRedirect value supplied as well.
+     *
+     * 'modelRedirect'
+     *
+     * If not false, provides an action the controller should execute after
+     * this one.
+     *
+     * 'view'
+     *
+     * A suggested view name that the contoller should use instead of the
+     * default view for this model or false to indicate that the default view
+     * should be used.
+     *
+     * 'data'
+     *
+     * Data that the model is returning for use in merging with the view to
+     * produce output.
+     *
+     */
+    public function modelAction ($actionData = false)
+    {
+
+        $return = array(
+            'status' => false,       // Assume we didn't get files or nothing works
+            'files' => false,       // Provide submitted data along with stored image data
+            'message' => ''
+        );
+
+        // Check for valid refType - See glmMembersAdminValidActions in Admin Controller
+        if (!isset($_REQUEST['glm_refType']) || !isset($this->config['ref_type_table'][$_REQUEST['glm_refType']])) {
+            $return['message'] = 'Invalid target table specified in AJAX call to imageUpload!';
+            echo json_encode($return);
+            die();
+        }
+        $refType = $_REQUEST['glm_refType'];
+        $refTable = $this->config['ref_type_table'][$_REQUEST['glm_refType']];
+
+        // Check for uploaded files
+        if (!isset($_FILES) || count($_FILES) == 0) {
+            $return['message'] = 'No image file provided!';
+            echo json_encode($return);
+            die();
+        }
+
+        // Check for valid target record ID
+        if (!isset($_REQUEST['glm_refDest']) || ($_REQUEST['glm_refDest']-0) <= 0) {
+            $return['message'] = 'Invalid target ID specified in AJAX call to imageUpload!';
+            echo json_encode($return);
+            die();
+        }
+        $refDest = ($_REQUEST['glm_refDest']-0);
+
+        // Make sure the record actually exists
+        $sql = "SELECT id FROM ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."$refTable WHERE id = $refDest;";
+        $record = $this->wpdb->get_row($sql, ARRAY_A);
+        if ($record['id'] != 1) {
+            $return['message'] = 'Specified target ID does not exist in AJAX call to imageUpload!';
+            echo json_encode($return);
+            die();
+        }
+
+
+        foreach( $_FILES as $file ) {
+
+            // Is there a file of the right type with a temp file that exists
+            if (isset($file['name']) && is_file($file['tmp_name'])) {
+
+                // Get new image using temporary file name
+                $newImage = wp_get_image_editor($file['tmp_name']);
+
+                // If we have a good image
+                if ( ! is_wp_error( $newImage ) ) {
+
+                    // Get the desired file name and add a timestamp to it to ensure that it's unique
+                    $fInfo = pathinfo($file['name']);
+                    $newFilename = strtolower($fInfo['filename'].'_'.time().'.'.$fInfo['extension']);
+
+                    // Get image temp file name - Not currently using, but should be using to check for resizing sanity
+                    $size = $newImage->get_size();
+
+                    // Try to store the image using that file name in the 'original' directory
+                    $storedImage = $newImage->save( GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$newFilename );
+
+                    // Now resize the images using $sizeArray
+                    $sizes = $newImage->multi_resize($this->config['imageSizes']);
+
+                    // Finally, move the files to the various size directories and rename them back to the correct name
+                    while (list($k, $v) = each($this->config['imageSizes'])) {
+
+                        // Check if size directory needs to be made
+                        if (!file_exists(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$k)) {
+                            mkdir(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$k);
+                        }
+
+                        // If there's an entry in the $sizes array, it means that the image resized to this size
+                        if (isset($sizes[$k])) {
+                            // Move resized file to desired direectory
+                            rename(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$sizes[$k]['file'], GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$k.'/'.$newFilename);
+                        } else {
+                            // Image didn't resize to this size, probably because it was smaller than the destination size (silly WP Image Editor class) - so just use the original
+                            copy(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$newFilename, GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$k.'/'.$newFilename);
+                        }
+
+                    }
+
+                    // Move the original to original directory
+                    if (!file_exists(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/original')) {
+                        mkdir(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/original');
+                    }
+                    rename(GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/'.$newFilename, GLM_MEMBERS_PLUGIN_IMAGES_PATH.'/original/'.$newFilename);
+
+                    $file['newFileName'] = $newFilename;
+
+                    // Store image name in images table
+                    $sql = "
+                        INSERT INTO ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."images
+                            (
+                                name,
+                                file_name,
+                                descr,
+                                caption,
+                                status,
+                                position,
+                                ref_type,
+                                ref_dest
+                            )
+                        VALUES
+                            (
+                                '".$file['name']."',
+                                '".$file['newFileName']."',
+                                '',
+                                '',
+                                ".$this->config['status_numb']['Inactive'].",
+                                99,
+                                $refType,
+                                $refDest
+                            );
+                    ";
+                    $this->wpdb->query($sql);
+                    $queryError = $this->wpdb->last_error;
+
+                    if ($queryError) {
+                        $file['error'] = true;
+                        $file['message'] = $queryError."\n".$sql;
+                    }
+
+                    // Get new ID and data from the new record
+                    $file['id'] = $this->wpdb->insert_id;
+                    $return['files'][] = $file;
+
+                    // Indicate that we actually stored an image
+                    $return['status'] = true;
+
+                } else {
+
+                    // File was not uploaded or of the wrong type
+                    $file['message'] = 'Image file "'.$file['name'].'" was not uploaded or is not a valid image file!';
+                    $file['status'] = false;
+
+                }
+
+            }
+
+        }
+
+        // Return stored image data
+        echo json_encode($return);
+        die();
+    }
+
+
+}
+?>
diff --git a/models/admin/member/memberImage.php b/models/admin/member/memberImage.php
deleted file mode 100644 (file)
index 668cc51..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-
-/**
- * Gaslight Media Members Database
- * Admin Member Info Image Upload by AJAX
- *
- * PHP version 5.5
- *
- * @category glmWordPressPlugin
- * @package  glmMembersDatabase
- * @author   Chuck Scott <cscott@gaslightmedia.com>
- * @license  http://www.gaslightmedia.com Gaslightmedia
- * @version  0.1
- */
-
-// Load Members data abstract
-require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php');
-
-/*
- * This class performs the work of handling images passed to it via
- * an AJAX call that goes through the WorPress AJAX Handler.
- *
- */
-class GlmMembersAdmin_member_memberImage extends GlmDataImages
-{
-
-    /**
-     * WordPress Database Object
-     *
-     * @var $wpdb
-     * @access public
-     */
-    public $wpdb;
-    /**
-     * Plugin Configuration Data
-     *
-     * @var $config
-     * @access public
-     */
-    public $config;
-
-    /*
-     * Constructor
-     *
-     * This contructor sets up this model. At this time that only includes
-     * storing away the WordPress data object.
-     *
-     * @return object Class object
-     *
-     */
-    public function __construct ($wpdb, $config)
-    {
-
-        // Save WordPress Database object
-        $this->wpdb = $wpdb;
-
-        // Save plugin configuration object
-        $this->config = $config;
-
-        // Run constructor for members data class
-        parent::__construct(false, false);
-
-    }
-
-    /*
-     * Perform Model Action
-     *
-     * This method does the work for this model and returns any resulting data
-     *
-     * @return array Status and data array
-     *
-     * 'status'
-     *
-     * True if successfull and false if there was a fatal failure.
-     *
-     * 'menuItemRedirect'
-     *
-     * If not false, provides a menu item the controller should
-     * execute after this one. Normally if this is used, there would also be a
-     * modelRedirect value supplied as well.
-     *
-     * 'modelRedirect'
-     *
-     * If not false, provides an action the controller should execute after
-     * this one.
-     *
-     * 'view'
-     *
-     * A suggested view name that the contoller should use instead of the
-     * default view for this model or false to indicate that the default view
-     * should be used.
-     *
-     * 'data'
-     *
-     * Data that the model is returning for use in merging with the view to
-     * produce output.
-     *
-     */
-    public function modelAction ($actionData = false)
-    {
-
-        $return = array(
-            'status' => false,       // Assume we didn't get files or nothing works
-            'files' => false,       // Provide submitted data along with stored image data
-            'message' => false
-        );
-
-
-        // Check for uploaded files
-        if (!isset($_FILES) || count($_FILES) == 0) {
-            $return['message'] = 'No image file provided!';
-            echo json_encode($return);
-            die();
-        }
-
-        // Include the incoming files data
-        $files = $_FILES;
-
-        // Check for Member Info record ID
-        if (!isset($_REQUEST['memberInfoID']) || trim($_REQUEST['memberInfoID']) == '') {
-            $return['message'] = 'No Member Info Record ID provided!';
-            echo json_encode($return);
-            die();
-        }
-echo "memberInfoID = ".$_REQUEST['memberInfoID']."\n\n";
-        // Store each file away
-
-            // Add stored image to return data
-
-        // Return stored image data
-        echo json_encode($return);
-        die();
-    }
-
-
-}
-?>
index 19cdeb8..286afd7 100644 (file)
@@ -207,6 +207,13 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo
             // Process submission of a member information record update
             case 'submit':
 
+                require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php');
+                $Images = new GlmDataImages($this->wpdb, $this->config);
+
+                // Update image gallery titles, descriptions, and image positions then return current image gallery
+                $imageGallery = $Images->galleryImageDataUpdate($this->config['ref_type_numb']['MemberInfo'], $memberInfoID, 'galleryPositionOrder');
+                $haveImageGallery = ($imageGallery != false);
+
                 if ($haveMemberInfo) {
 
                     // Update the member Info data
@@ -273,8 +280,7 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo
                     // Also get any image gallery images
                     require_once(GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php');
                     $Images = new GlmDataImages($this->wpdb, $this->config);
-                    $imageGallery = $Images->getGallery($this->config['ref_type_numb']['Member'], $memberInfoID);
-
+                    $imageGallery = $Images->getGallery($this->config['ref_type_numb']['MemberInfo'], $memberInfoID);
                     $haveImageGallery = ($imageGallery != false);
 
                 // Otherwise
@@ -489,6 +495,7 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo
             'haveCategories' => $haveCategories,
             'categories' => $categories,
             'categoryMemberInfo' => $categoryMemberInfo,
+            'imageGallery' => $imageGallery,
             'noActive' => $noActive,
             'time' => time()
         );
index eaa60c0..b2186c3 100644 (file)
@@ -99,8 +99,7 @@
                             // 'media_buttons' => true,
                             // 'quicktags' => false,
                             'wpautop' => false,
-                            'textarea_name' => 'descr',                    </div>
-                            
+                            'textarea_name' => 'descr',
                             'editor_height' => 200,     // Height in px, overrides editor_rows
                                 // 'textarea_rows' => 8
                         ));
             <tr>
                 <th>Image Gallery</th>
                 <td class="glm-item-container glm-imageGalleryContainer">
-                    <input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />
+                    <input type="hidden" id="galleryPositionOrder" name="galleryPositionOrder" 
+                        value="{foreach $imageGallery as $i name=ig}{if $i.file_name}{$i.id}{if not $smarty.foreach.ig.last},{/if}{/if}{/foreach}" />
                     <div class="glm-imageDropContainer">
-                        <div class="glm-imageDrop glm-imageItemHidden" data-refType="" data-recordID="{$memberInfo.fieldData.id}" data-menuItem="member" data-action="memberImage"></div> <!-- Overlay of parent for drag detection -->
+                            <!-- All fields with class "glm-imageDrop" are automatically processed by imageUpload.js -->
+                        <div class="glm-imageDrop glm-imageItemHidden" 
+                                data-refType="{$ref_type_numb.MemberInfo}" 
+                                data-recordID="{$memberInfo.fieldData.id}" 
+                                data-maxFileSizeKB="2500"
+                                data-allowedTypes="image/jpeg,image/png,image/gif,image/x-ms-bmp"
+                                >
+                            <!-- The contents of this div are read by imageUpload.js to use for display of upload progress. -->
+                            <div class="glm-imageUploadStatusTemplate glm-imageItemHidden">
+                                
+                                <!-- Start of upload status pop-up template - Parameters are of the form "[name]" -->
+                                <b><u>Uploading Image [thisFile] of [numbFiles]</u></b>
+                                <table class="glm-statusTable">
+                                    <tr><td class="glm-statusImageTD" rowspan="5"><div class="glm-statusImageContainer"><img src="{ fileImage }"></div></td></tr>
+                                    <tr>
+                                        <div style="float: right; margin: .2em .2em 0 0;" id="imageUploadCancel" class="button-primary glm-right">Cancel Upload</div>
+                                        <td class="glm-statusPrompt">Name: </td><td class="glm-statusValue">{ fileName }</td>
+                                    </tr>
+                                    <tr><td class="glm-statusPrompt">Type: </td><td class="glm-statusValue">{ fileType }</td></tr>
+                                    <tr><td class="glm-statusPrompt">Size: </td><td class="glm-statusValue">{ fileSize }</td></tr>
+                                    <tr><td class="glm-statusPrompt">Progress: </td><td class="glm-statusValue"><div class="glm-progressBarContainer"><div class="glm-progressBar"></div></div></td></tr>
+                                </table>
+                                <!-- End of template -->
+                                
+                            </div>
+                            <!-- The contents of this div are read by imageUpload.js to use as a template for inserting a new image into the gallery area -->
+                            <div class="glm-galleryImageDataTemplate glm-imageItemHidden">
+                            
+                                <!-- 
+                                    Start of new image template.
+                                    Parmeters are of the form "{ name }" where the spaces are required to avoid Smarty interpreting the parameters.
+                                    Note that these input fields are dissabled to avoid the template fields being submitted. The imageUpload.js
+                                    code enables the fields when it replicates the template for a new image. 
+                                -->
+                                <li id="{ id }" class="glm-galleryContainer">
+                                    <div class="glm-galleryImageData">
+                                        <table class="glm-statusTable">
+                                            <tr>
+                                                <th>Caption:</th>
+                                                <td><input id="galleryImage_caption_{ id }" type="text" name="galleryImage_caption[{ id }]" value="" class="glm-form-text-input-medium" disabled></td>
+                                                <th>Delete:</th>
+                                                <td><input type="checkbox" name="galleryImage_delete[{ id }]" disabled></td>
+                                            </tr>
+                                            <tr>
+                                                <th>Description:</th>
+                                                <td><textarea name="galleryImage_descr[{ id }]" disabled></textarea></td>
+                                                <th colspan="2">New Upload</th>
+                                            </tr>
+                                        </table>
+                                    </div>
+                                    <div class="glm-galleryImage" data-id="{ id }">
+                                        <img src="{$glmPluginMediaURL}/images/small/{ filename }">
+                                    </div>
+                                </li>
+                                <div id="glm-galleryImageLarger_{ id }" class="glm-imageDialog"><img src="{$glmPluginMediaURL}/images/large/{ filename }"></div>
+                                <!-- End of template -->
+                                
+                            </div>
+                        </div> <!-- Overlay of parent for drag/drop detection -->
                         <div class="glm-imageUploadStatus glm-imageItemHidden"></div> <!-- Overlay for Upload Status Bars -->
-                        <input id="newImage" type="file" name="newImage[]" multiple="multiple" class="glm-form-text-input glm-imageBrowseButton">
                         <div class="glm-imageDropText glm-imageItemHidden">Drag and drop new images here</div>
                         <div class="glm-noImageDropText glm-imageItemHidden">HTML5 file drag-and-drop not supported by your browser.<br>Use "Browse" button above to upload an image.</div>
                     </div>
+                    <ul class="glm-galleryImages">
+                        <!-- Note that id in li is needed for sorting -->
+        {foreach $imageGallery as $i}
+            {if $i.file_name}
+                        <li id="{$i.id}" class="glm-galleryContainer">
+                            <div class="glm-galleryImageData">
+                                <table class="glm-statusTable">
+                                    <tr>
+                                        <th>Caption:</th>
+                                        <td><input id="galleryImage_caption_{$i.id}" type="text" name="galleryImage_caption[{$i.id}]" value="{$i.caption}" class="glm-form-text-input-medium"></td>
+                                        <th>Delete:</th>
+                                        <td><input type="checkbox" name="galleryImage_delete[{$i.id}]"></td>
+                                    </tr>
+                                    <tr>
+                                        <th>Description:</th>
+                                        <td><textarea name="galleryImage_descr[{$i.id}]">{$i.descr}</textarea></td>
+                                        <th colspan="2">
+                                            <input type="hidden" name="galleryImage_position[{$i.id}]" value="{$i.position}">
+                                        </th>
+                                    </tr>
+                                </table>
+                            </div>
+                            <div class="glm-galleryImage" data-id="{$i.id}">
+                                <img src="{$glmPluginMediaURL}/images/small/{$i.file_name}">
+                            </div>
+                            <div id="glm-galleryImageLarger_{$i.id}" class="glm-imageDialog"><img src="{$glmPluginMediaURL}/images/large/{$i.file_name}"></div>
+            {/if}       </li>
+                                         
+        {/foreach}
+                    </div>
                 </td>
             </tr>
         </table>
         
     </form>
 
-<!-- 
-                    <input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />
-                    <div id="newImageButton" class="button-primary glm-right">Add an Image</div>
-                    <div id="newImageDialog" class="glm-dialog-box" title="Select a new image">
-                        <table class="glm-admin-table">
-                            <tr>
-                                <th class="glm-required">Image:</th>
-                                <td id="newImageTD">
-                                    <input id="newImage" type="file" name="newImage[]" multiple="multiple" class="glm-form-text-input">
-                                    <div id="imageDrop"><span id="imageDropText">or drop files here</span></div>
-                                    <div id="noImageDrop">HTML5 file drag-and-drop not supported by your browser.<br>Use "Browse" button above to upload an image.</div>
-                                    <div id="newImageRequired"></div>
-                                </td>
-                            </tr>
-                            <tr>
-                                <th class="glm-required">Image Caption:</th>
-                                <td id="newImageNameTD">
-                                    <input id="newImageName" type="text" name="newImageName" class="glm-form-text-input">
-                                    <div id="newImageNameRequired"></div>
-                                </td>
-                            </tr>
-                            <tr>
-                                <th>Description:</th>
-                                <td>
-                                    <textarea id="editImageDescr" name="descr" class="glm-form-textarea"></textarea>
-                                </td>
-                            </tr>
-                        </table>
-                        <div id="imageDragMessages">
-                            <p>Status Messages</p>
-                        </div>
-                        <p><span class="glm-required">*</span> Required</p>
-                        <a id="newImageCancel" class="button-primary glm-right">Cancel</a>
-                        <input id="newImageSubmit" type="submit" value="Add new image">
-                    </div>
- -->
-
-
-
     <!-- Include scripts for html5-File-Upload -->
     
     <script type="text/javascript">
             $('.glm-geocodeAction').change( function() {
                 glmGeocode();  
             });
-            
-
 
         });
     </script>