From: Chuck Scott Date: Tue, 7 Jul 2015 17:13:18 +0000 (-0400) Subject: Added Image Upload and Gallery features X-Git-Tag: v1.0.0^2~2^2~1 X-Git-Url: http://cvs2.gaslightmedia.com/gitweb/?a=commitdiff_plain;h=84608c9a1a52bd3fbcfb466862172aace44d2e2f;p=WP-Plugins%2Fglm-member-db.git Added Image Upload and Gallery features --- diff --git a/classes/data/dataImages.php b/classes/data/dataImages.php index a5e66e33..1b334769 100644 --- a/classes/data/dataImages.php +++ b/classes/data/dataImages.php @@ -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('dataImages: 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("  Error setting position order for images.
$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("      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 diff --git a/config/plugin.ini b/config/plugin.ini index fad26a1f..e3290a18 100644 --- a/config/plugin.ini +++ b/config/plugin.ini @@ -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 diff --git a/controllers/admin.php b/controllers/admin.php index 064bc736..304fcb4b 100644 --- a/controllers/admin.php +++ b/controllers/admin.php @@ -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('Error in Admin Controller: 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 diff --git a/css/admin.css b/css/admin.css index d8b629ab..a42055ea 100644 --- a/css/admin.css +++ b/css/admin.css @@ -50,7 +50,7 @@ .glm-item-container { border: 1px #ccc solid; padding: .4em; - background: #f8f8f8; + background: #fff; } /* Admin Tabs */ @@ -201,3 +201,60 @@ 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; +} diff --git a/js/imageUpload/imageUpload.css b/js/imageUpload/imageUpload.css index 660dd1e6..68f19ee5 100644 --- a/js/imageUpload/imageUpload.css +++ b/js/imageUpload/imageUpload.css @@ -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 { @@ -69,25 +70,12 @@ 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%; @@ -101,18 +89,30 @@ 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 @@ -129,4 +129,3 @@ background-color: blue; width: 0%; } - diff --git a/js/imageUpload/imageUpload.js b/js/imageUpload/imageUpload.js index 5a644194..7a887972 100644 --- a/js/imageUpload/imageUpload.js +++ b/js/imageUpload/imageUpload.js @@ -1,24 +1,34 @@ /* - * HTML5 Image Upload Support + * Gaslight Media HTML5 Image Upload Support for glm-member-db plugin. + * + * Author: Chuck Scott * * 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 = - 'Uploading Image {thisFile} of {numbFiles}' + - '' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + - '
Name: {fileName}
Type: {fileType}
Size: {fileSize}
Progress:
' - ; - 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
"); + + // 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(); }); diff --git a/lib/GlmDataAbstract/DataAbstract.php b/lib/GlmDataAbstract/DataAbstract.php index c99a4c19..a0e26ca0 100755 --- a/lib/GlmDataAbstract/DataAbstract.php +++ b/lib/GlmDataAbstract/DataAbstract.php @@ -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(); diff --git a/lib/smartyTemplateSupport.php b/lib/smartyTemplateSupport.php index 07ee1efa..e4e926f6 100644 --- a/lib/smartyTemplateSupport.php +++ b/lib/smartyTemplateSupport.php @@ -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 index 00000000..bcba5dd1 --- /dev/null +++ b/models/admin/ajax/imageUpload.php @@ -0,0 +1,252 @@ + + * @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 index 668cc511..00000000 --- a/models/admin/member/memberImage.php +++ /dev/null @@ -1,137 +0,0 @@ - - * @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(); - } - - -} -?> diff --git a/models/admin/member/memberInfo.php b/models/admin/member/memberInfo.php index 19cdeb85..286afd7c 100644 --- a/models/admin/member/memberInfo.php +++ b/models/admin/member/memberInfo.php @@ -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() ); diff --git a/views/admin/member/memberInfo.html b/views/admin/member/memberInfo.html index eaa60c0e..b2186c3f 100644 --- a/views/admin/member/memberInfo.html +++ b/views/admin/member/memberInfo.html @@ -99,8 +99,7 @@ // 'media_buttons' => true, // 'quicktags' => false, 'wpautop' => false, - 'textarea_name' => 'descr', - + 'textarea_name' => 'descr', 'editor_height' => 200, // Height in px, overrides editor_rows // 'textarea_rows' => 8 )); @@ -367,14 +366,102 @@ Image Gallery - +
-
+ +
+ +
+ + + Uploading Image [thisFile] of [numbFiles] + + + +
Cancel Upload
+ + + + + +
Name: { fileName }
Type: { fileType }
Size: { fileSize }
Progress:
+ + +
+ +
+ + +
  • +
    + + + + + + + + + + + + +
    Caption:Delete:
    Description:New Upload
    +
    +
    + +
    +
  • +
    + + +
    +
    -
    Drag and drop new images here
    HTML5 file drag-and-drop not supported by your browser.
    Use "Browse" button above to upload an image.
    +