From 94f3e9898eae78a814298ac62ec2218f4daf30bc Mon Sep 17 00:00:00 2001 From: Steve Sutton Date: Thu, 30 Mar 2017 13:09:36 -0400 Subject: [PATCH] Working on adding Files and Counties Adding new table for Counties. Adding the File edit page. Using the Image edit page as example. Adding new functions to the js and ajax model for files to be processed differently. --- classes/data/dataFiles.php | 509 ++++++++++++++++++ classes/data/dataMemberInfo.php | 2 +- css/admin.css | 3 +- index.php | 2 +- js/imageUpload/imageUpload.css | 165 +++++- js/imageUpload/imageUpload.js | 146 ++++- models/admin/ajax/fileUpload.php | 253 +++++++++ models/admin/management/import/members.php | 4 +- models/admin/member/memberInfo.php | 37 +- ...1.1.26.sql => create_database_V1.1.27.sql} | 18 +- setup/databaseScripts/dbVersions.php | 1 + ..._V1.1.24.sql => drop_database_V1.1.27.sql} | 0 .../update_database_V1.1.27.sql | 25 + setup/validActions.php | 1 + views/admin/member/memberInfo.html | 12 +- views/admin/member/memberInfo/editFiles.html | 119 ++++ views/admin/member/memberInfo/editStatus.html | 61 +++ views/front/members/detail.html | 2 +- 18 files changed, 1327 insertions(+), 33 deletions(-) create mode 100644 classes/data/dataFiles.php create mode 100644 models/admin/ajax/fileUpload.php rename setup/databaseScripts/{create_database_V1.1.26.sql => create_database_V1.1.27.sql} (97%) rename setup/databaseScripts/{drop_database_V1.1.24.sql => drop_database_V1.1.27.sql} (100%) create mode 100644 setup/databaseScripts/update_database_V1.1.27.sql create mode 100644 views/admin/member/memberInfo/editFiles.html create mode 100644 views/admin/member/memberInfo/editStatus.html diff --git a/classes/data/dataFiles.php b/classes/data/dataFiles.php new file mode 100644 index 00000000..31d19de7 --- /dev/null +++ b/classes/data/dataFiles.php @@ -0,0 +1,509 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @release SVN: $Id: dataFiles.php,v 1.0 2011/01/25 19:31:47 cscott Exp $ + */ + +/** + * EventManagementDataFiles class + * + * PHP version 5 + * + * @category Data + * @package EventManagement + * @author Chuck Scott + * @license http://www.gaslightmedia.com Gaslightmedia + * @release SVN: $Id: dataFiles.php,v 1.0 2011/01/25 19:31:47 cscott + * Exp $ + */ +class GlmDataFiles extends GlmDataAbstract +{ + + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + /** + * Field definitions + * + * @var $ini + * @access public + */ + public $table; + + /** + * Field definitions + * + * 'type' is type of field as defined by the application + * text Regular text field + * pointer Pointer to an entry in another table + * 'filters' is the filter name for a particular filter ID in PHP filter + * functions + * See PHP filter_id() + * + * 'use' is when to use the field + * l = List + * g = Get + * n = New + * i = Insert + * e = Edit + * u = Update + * d = Delete + * a = All + * + * @var $ini + * @access public + */ + public $fields = false; + + /** + * Constructor + * + * @param object $d + * database connection + * + * @return void + * @access public + */ + function __construct ($wpdb, $config) + { + + // If this class is not being extended along with existing $wpdb and $config + if (!$this->wpdb) { + + // Save WordPress Database object + $this->wpdb = $wpdb; + + // Save plugin configuration object + $this->config = $config; + + } + + /* + * Table Name + */ + $this->table = GLM_MEMBERS_PLUGIN_DB_PREFIX . 'files'; + + /* + * Table Data Fields + */ + $this->fields = array( + + 'id' => array( + 'field' => 'id', + 'type' => 'integer', + 'view_only' => true, + 'use' => 'a' + ), + + // Original file name + 'name' => array( + 'field' => 'name', + 'type' => 'text', + 'required' => true, + 'use' => 'a' + ), + + // Status + 'status' => array ( + 'field' => 'status', + 'type' => 'list', + 'list' => $this->config['status'], + 'required' => true, + 'default' => $this->config['status_numb']['Pending'], + 'force_list' => true, + 'use' => 'a' + ), +/* Not using for now + // Special Image flag - only one of these + 'selected' => array ( + 'field' => 'selected', + 'type' => 'checkbox', + 'use' => 'gled' + ), +*/ + // File Name + 'file_name' => array( + 'field' => 'file_name', + 'type' => 'text', + 'required' => true, + 'use' => 'a' + ), + + // Description + 'descr' => array( + 'field' => 'descr', + 'type' => 'text', + 'use' => 'a' + ), + + // Caption + 'caption' => array( + 'field' => 'caption', + 'type' => 'text', + 'use' => 'a' + ), + + // Position + 'position' => array( + 'field' => 'position', + 'type' => 'integer', + 'use' => 'a' + ), + + // Reference Type - Which table this entry is associated with + 'ref_type' => array ( + 'field' => 'ref_type', + 'type' => 'list', + 'list' => $this->config['ref_type'], + 'required' => true, + 'default' => $this->config['ref_type_numb']['None'], + 'force_list' => true, + 'use' => 'a' + ), + + // Reference Entry ID + 'ref_dest' => array( + 'field' => 'ref_dest', + 'type' => 'integer', + 'use' => 'gled' + ), + + // Reference Destination - Which entry in the table is it associated with + 'position' => array( + 'field' => 'position', + 'type' => 'integer', + 'use' => 'a' + ) + + ); + + if (is_admin() && GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { + glmMembersAdmin::addNotice($this->fields, 'DataBlock', 'Table Fields: '.$this->table); + } + + } + + + /* + * Get Files + * + * Retrieves all files for a specific reference type and ID + * + * Reference types point to a particular table of data, such as + * the member_info table. The ID points to a particular entry, + * such as a specific member information record (Reference + * Destination). + * + * @param int $refType Reference Type + * @param int $refID Reference entry ID + * + * @return object Class object + * + */ + public function getFiles($refType, $refID ) + { + + // Check for a valid reference type + if (!isset($this->config['ref_type'][$refType])) { + + if (is_admin() && GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { + glmMembersAdmin::addNotice("dataFiles.php - getFiles()
  Specified reference type ($refType) is invalid.", 'Alert'); + } + + return false; + } + + // Make sure $refID is a positive integer - If it's not a valid reference, nothing will be returned anyway. + $refID = $refID -0; + if ($refID <= 0) { + + if (is_admin() && GLM_MEMBERS_PLUGIN_ADMIN_DEBUG_VERBOSE) { + glmMembersAdmin::addNotice("dataFiles.php - getFiles()
  Specified reference destination ($refID) is invalid.", 'Alert'); + } + + return false; + } + + + // Get all files matching $refType and $refID ordered by "position" number + $where = "T.ref_type = $refType AND T.ref_dest = $refID"; + $files = $this->getList($where, 'T.position'); + + // Check if we found anything + if (!is_array($files) || count($files) == 0) { + return false; + } + + return $files; + + } + + /* + * Set gallery image positions + * + * Reads a submitted comma-delimited list of ID numbers creted by jQuery sortable() + * and updates the "position" field in the files 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 positionOrder($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('dataFiles: positionOrder() unable to find '.$orderField.' data. Perhaps there\'s no files at this time.', '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', 'File Sort Order'); + } + + // Update image entries with new sort order; + $pos = 0; + foreach($order as $i) { + + // Do sanity check on image ID + if (($i-0) > 0) { + + // Set the position for this image + $sql = "UPDATE ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."files 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 files.
$queryError
$sql", 'Alert'); + } + + return false; + } + } + } + + return true; + + } + + /* + * Process image gallery titles, descriptions, 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 filesDataUpdate($refType, $refID, $orderField) + { + + // Re-order image gallery files + $this->positionOrder($refType, $refID, $orderField); + + // If any image gallery data is submitted + if (isset($_REQUEST['file_caption']) + && is_array($_REQUEST['file_caption']) + && count($_REQUEST['file_caption']) > 0) { + + // Update other data for this image + reset($_REQUEST['file_caption']); + while (list($k, $v) = each($_REQUEST['file_caption'])) { + $id = ($k -0); + + // Sanitize input + $caption = sanitize_text_field( $_REQUEST['file_caption'][$k] ); + $descr = sanitize_text_field( $_REQUEST['file_descr'][$k] ); + + // Update data for this image + $sql = "UPDATE ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."files SET caption = '$caption', descr = '$descr' WHERE id = $id;"; + $this->wpdb->query($sql); + } + + // Check for an image deletion + if (isset($_REQUEST['file_delete']) && count($_REQUEST['file_delete']) > 0) { + + // For each delete requested + reset($_REQUEST['file_delete']); + while (list($k, $v) = each($_REQUEST['file_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 ."files 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 ."files 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'); + } + + // Check if the image actually exists - May not if we're doing development + if (is_file(GLM_MEMBERS_PLUGIN_FILES_PATH.'/'.$k.'/'.$imgData['file_name'])) { + + // Delete each image size + unlink(GLM_MEMBERS_PLUGIN_FILES_PATH.'/'.$k.'/'.$imgData['file_name']); + + } + + } + + // Check if the image actually exists - May not if we're doing development + if (is_file(GLM_MEMBERS_PLUGIN_FILES_PATH.'/original/'.$imgData['file_name'])) { + + // Also delete the original + unlink(GLM_MEMBERS_PLUGIN_FILES_PATH.'/original/'.$imgData['file_name']); + + } + } + } + } + } + } + + // Get updated image gallery + $files = $this->getFiles($refType, $refID); + return $files; + + } + + + /* + * Clone an image gallery + * + * Makes a copy of all files in a gallery and assigns them to another refType and refID + * + * @param int $refTypeSrc Source Reference Type + * @param int $refIDSrc Source Reference entry ID + * @param int $refTypeDst Destination Reference Type + * @param int $refIDDst Destination Reference entry ID + * @param string $orderField Name of SUBMIT field containing sort order. + * + * @return boolean True if successful + * + */ + public function galleryImageDataClone($refTypeSrc, $refIDSrc, $refTypeDst, $refIDDst) + { + + // Get Source Image Gallery + $imageGallerySrc = $this->getFiles($refTypeSrc, $refIDSrc); + + // Get the destination target table name + $dstTable = $this->config['ref_type_table'][$refTypeDst]; + + // Get Image sizes and add original + $sizes = $this->config['imageSizes']; + $sizes['original'] = array(); + + // If we have an image gallery with any files + if ($imageGallerySrc !== false + && is_array($imageGallerySrc) + && count($imageGallerySrc) > 0) { + + // for each gallery image + foreach ($imageGallerySrc as $i) { + + $srcName = $i['file_name']; + + // Strip the source table name and ID from the file name (part after first '-') + $dstName = $dstTable.'_'.$refIDDst.'-'.substr(strchr($i['file_name'], '-'), 1); + + // For each image size + reset($sizes); + while (list($k, $v) = each($sizes)) { + + // Check if the source image exists + if (is_file(GLM_MEMBERS_PLUGIN_FILES_PATH.'/'.$k.'/'.$srcName)) { + + // Copy src file to dst file for this size + copy(GLM_MEMBERS_PLUGIN_FILES_PATH.'/'.$k.'/'.$srcName, GLM_MEMBERS_PLUGIN_FILES_PATH.'/'.$k.'/'.$dstName); + + } + } + + // Add this image to the files table + // Store image name in files table + $sql = " + INSERT INTO ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."files + ( + name, + file_name, + descr, + caption, + status, + position, + ref_type, + ref_dest + ) + VALUES + ( + '".$i['name']."', + '$dstName', + '".addslashes($i['descr'])."', + '".addslashes($i['caption'])."', + ".$i['status']['value'].", + ".$i['position'].", + $refTypeDst, + $refIDDst + ); + "; + $this->wpdb->query($sql); + // $queryError = $this->wpdb->last_error; + + } + + } + + } + +} diff --git a/classes/data/dataMemberInfo.php b/classes/data/dataMemberInfo.php index 5ac7306e..339e0a41 100644 --- a/classes/data/dataMemberInfo.php +++ b/classes/data/dataMemberInfo.php @@ -361,7 +361,7 @@ class GlmDataMemberInfo extends GlmDataAbstract 'short_descr' => array ( 'field' => 'short_descr', 'type' => 'text', - 'maxLength' => '120', + 'maxLength' => '124', 'use' => 'a' ), diff --git a/css/admin.css b/css/admin.css index cddc0f77..7f86277a 100644 --- a/css/admin.css +++ b/css/admin.css @@ -448,7 +448,8 @@ td.glm-nowrap { margin-top: 10px; margin-right: 5px; } -.glm-galleryImage img +.glm-galleryImage img, +.glm-galleryFiles a { position: relative; top: 0px; diff --git a/index.php b/index.php index 0e81b132..ff988cfe 100644 --- a/index.php +++ b/index.php @@ -39,7 +39,7 @@ */ define('GLM_MEMBERS_PLUGIN_VERSION', '2.9.12'); -define('GLM_MEMBERS_PLUGIN_DB_VERSION', '1.1.26'); +define('GLM_MEMBERS_PLUGIN_DB_VERSION', '1.1.27'); // Check if plugin version is not current in WordPress option and if needed updated it if (GLM_MEMBERS_PLUGIN_VERSION != get_option('glmMembersDatabasePluginVersion')) { diff --git a/js/imageUpload/imageUpload.css b/js/imageUpload/imageUpload.css index 68f19ee5..d82fe33d 100644 --- a/js/imageUpload/imageUpload.css +++ b/js/imageUpload/imageUpload.css @@ -34,12 +34,12 @@ background-color: #000; opacity: .0; filter: alpha(opacity=0); /* For IE8 and earlier */ - + } .glm-imageDropText { color: #666; - z-index: 9; + z-index: 9; } .glm-noImageDropText { @@ -48,7 +48,7 @@ .glm-imageItemHidden { display: none; - z-index: 9; + z-index: 9; } .glm-imageDropDragOver { @@ -70,13 +70,13 @@ box-shadow: 10px 10px 5px grey; } -.glm-statusTable +.glm-statusTable { width: 100%; /* table-layout: fixed; */ line-height: 80%; } -.glm-statusPrompt +.glm-statusPrompt { width: 10%; padding: 0px; @@ -86,20 +86,20 @@ .glm-statusValue { width: 65%; - padding: 0px; + padding: 0px; text-align: left; } .glm-statusImageTD { - max-width: 100%; + max-width: 100%; overflow: hidden; padding: 13px; display: table-cell; - vertical-align: middle; + vertical-align: middle; } .glm-statusImageContainer { - display: block; + display: block; width: 100px; height: 100%; } @@ -115,7 +115,152 @@ max-width: 100%; } -.glm-progressBarContainer +.glm-progressBarContainer +{ + height: 100%; + width: 95%; + background-color: #ccc; + border: 1px solid black; + line-height: 1em; +} +.glm-progressBar +{ + height: 1em; + background-color: blue; + width: 0%; +} + +/* Files */ +.glm-filesContainer +{ + margin: .5em 1em 1em 1em; + padding: .5em; + border: 2px solid #ccc; + text-align: center; + width: 90%; +} + +.glm-fileDropContainer +{ + position: relative; + text-align: center; + padding: 0 1em 1em 1em; + background-color: #eee; + border: 2px solid #ccc; + border-radius: 7px; + cursor: default; + margin-left: auto; + margin-right: auto; + margin-bottom: 1em; + width: 90%; + height: 2em; +} +.glm-fileDrop +{ + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 10; + border-radius: 7px; + height: 100%; + background-color: #000; + opacity: .0; + filter: alpha(opacity=0); /* For IE8 and earlier */ + +} +.glm-fileDropText +{ + color: #666; + z-index: 9; +} +.glm-noFileDropText +{ + color: #666; +} +.glm-fileItemHidden +{ + display: none; + z-index: 9; +} +#glm-table-files { + width: 90%; +} +.glm-fileDropDragOver +{ + border: 2px solid #000 important; + background-color: #fff; +} +.glm-fileData { + width: 70%; + float: right; +} +.glm-file { + width: 30%; + float: left; +} +.glm-imageUploadStatus +{ + position: absolute; + width: 100%; + height: 100%; + top: 20px; + left: 10px; + z-index: 11; + border: 2px solid black; + border-radius: 7px; + height: 8em; + background-color: #fff; + box-shadow: 10px 10px 5px grey; +} + +.glm-statusTable +{ + width: 100%; +/* table-layout: fixed; */ + line-height: 80%; +} +.glm-statusPrompt +{ + width: 10%; + padding: 0px; + font-weight: bold; + text-align: right; +} +.glm-statusValue +{ + width: 65%; + padding: 0px; + text-align: left; +} +.glm-statusFileTD +{ + max-width: 100%; + overflow: hidden; + padding: 13px; + display: table-cell; + vertical-align: middle; +} +.glm-statusFileContainer +{ + display: block; + width: 100px; + height: 100%; +} +.glm-statusFileContainer img +{ + position: absolute; + top: 0px; + right: 0px; + bottom: 0px; + left: 0px; + /* Maintain aspect ratio */ + max-height:100%; + max-width: 100%; +} + +.glm-progressBarContainer { height: 100%; width: 95%; diff --git a/js/imageUpload/imageUpload.js b/js/imageUpload/imageUpload.js index 8a3308b6..b06131c9 100644 --- a/js/imageUpload/imageUpload.js +++ b/js/imageUpload/imageUpload.js @@ -16,6 +16,7 @@ jQuery(document).ready(function($) { var drop; + var fileDrop; var recordID; var refType; var recordID; @@ -28,7 +29,7 @@ jQuery(document).ready(function($) { var newImageAdded = false; var enableDraggable = true; - // Setup Drag and Drop when Add + // Setup Drag and Drop when Add if (window.File && window.FileList && window.FileReader) { // is XHR2 available? @@ -40,6 +41,11 @@ jQuery(document).ready(function($) { $('.glm-imageDropText').removeClass('glm-imageItemHidden'); $('.glm-imageDrop').removeClass('glm-imageItemHidden'); + // Change from Drag/Drop not supported to drop here text + $('.glm-fileBrowseButton').addClass('glm-fileItemHidden'); + $('.glm-fileDropText').removeClass('glm-fileItemHidden'); + $('.glm-fileDrop').removeClass('glm-fileItemHidden'); + // Prevent dropping on the document $(document).on('dragenter', function (e) { e.stopPropagation(); @@ -59,6 +65,12 @@ jQuery(document).ready(function($) { initDrop(); }); + // For each file drop area on the page + $('.glm-fileDrop').each(function() { + fileDrop = $(this); + initFileDrop(); + }); + } else { 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.'); } @@ -104,6 +116,43 @@ jQuery(document).ready(function($) { } + // Setup a File fileDrop area + function initFileDrop() { + // Get information from the field on where this image file is going + refType = fileDrop.attr("data-refType"); + recordID = fileDrop.attr("data-recordID"); + maxFileSize = fileDrop.attr("data-maxFileSizeKB") * 1000; + allowedTypes = fileDrop.attr("data-allowedTypes"); + + uploadStatusTemplate = fileDrop.children('.glm-fileUploadStatusTemplate').html(); + fileDataTemplate = fileDrop.children('.glm-fileDataTemplate').html(); + galleryFiles = fileDrop.parent().parent().children('.glm-galleryFiles'); + + // Change fileDrop destination appearance only when dragging over a file. + fileDrop.on('dragenter', function(e){ + e.stopPropagation(); + e.preventDefault(); + fileDrop.parent().addClass('glm-fileDropDragOver'); + }); + fileDrop.on('dragover', function(e){ + e.stopPropagation(); + e.preventDefault(); + }); + fileDrop.on('dragleave', function(e){ + e.stopPropagation(); + e.preventDefault(); + fileDrop.parent().removeClass('glm-fileDropDragOver'); + }); + + // File fileDrop action + $('.glm-fileDrop').on('drop', function (e) { + e.preventDefault(); + files = e.originalEvent.dataTransfer.files; + handleFileOnlyDrop(); + fileDrop.parent().removeClass('glm-fileDropDragOver'); + }); + } + /* * 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 @@ -123,6 +172,18 @@ jQuery(document).ready(function($) { processFile(); } + /* + * This function sets up AJAX processing for just files and not images + */ + function handleFileOnlyDrop() { + // Reset file pointer and set number of last file + thisFile = 0; + numbFiles = files.length; + + // Process only Files + processOnlyFiles(); + console.log('Processing ' + numbFiles + ' file '); + } /* * Process the current file - AJAX complete: will call back to this function @@ -169,7 +230,7 @@ jQuery(document).ready(function($) { // 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."); + alert("This 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(); @@ -177,12 +238,83 @@ jQuery(document).ready(function($) { // 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"); + alert("The file is not an accepted type.\nTo use this file, consider resaving it as a different file type.\n"); statusArea.fadeOut(); processFile(); } else { - + + // When status has faded in, Send the files + sendFileToServer(fd, status); + + } + + }); + + thisFile++; + + } else { + + // Reset expanded image and sortable events + setupExpandedImageEvents(); + + } + + } + + function processOnlyFiles() { + // If we still have files to process + if (thisFile < numbFiles) { + file = files[thisFile]; + + // Setup field pairs for sending in request + var fd = new FormData(); + + // 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 the AJAX call. + // (models/admin/ajax/fileUpload.php) + fd.append( 'glm_action', 'fileUpload' ); + + // 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() { + + // Check image size and alert the user if it's too big + if (file.size > maxFileSize) { + + alert("This 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 type.\nTo use this file, consider resaving it as a different file type.\n"); + statusArea.fadeOut(); + processFile(); + + } else { + // When status has faded in, Send the files sendFileToServer(fd, status); @@ -367,10 +499,10 @@ jQuery(document).ready(function($) { function setupExpandedImageEvents() { var largeImageDisplayed = false; - + // Handle expanded images on hover for image gallery $('.glm-galleryImage').on("click", function(){ - + // If an image is currently displayed, close it now and reset the flag if (largeImageDisplayed) { $('#glm-galleryImageLarger_' + largeImageDisplayed).dialog("close"); @@ -379,7 +511,7 @@ jQuery(document).ready(function($) { id = $(this).attr("data-id"); largeImageDisplayed = id; - + // Display the larger image var imgTitle = $("#galleryImage_caption_" + id).val(); $('#glm-galleryImageLarger_' + id).dialog({ diff --git a/models/admin/ajax/fileUpload.php b/models/admin/ajax/fileUpload.php new file mode 100644 index 00000000..4916ce84 --- /dev/null +++ b/models/admin/ajax/fileUpload.php @@ -0,0 +1,253 @@ + + * @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_fileUpload 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 modelAction takes an AJAX image upload and stores the image in the + * media/images directory of the plugin. + * + * This model action does not return, it simply does it's work then calls die(); + * + * @param $actionData + * + * Echos JSON string as response and does not return + */ + 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 validActions in Admin Controller + if (!isset($_REQUEST['glm_refType']) || !isset($this->config['ref_type_table'][$_REQUEST['glm_refType']])) { + $return['message'] = 'Invalid target table does not exists!'; + 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!'; + 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 (count($record['id']) != 1) { + $return['message'] = "Specified target record (".$record['id'].") does not exist in table (".$refTable.")!\n\n".$sql; + echo json_encode($return); + die(); + } + + // For each submitted file (usually only one) + 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'])) { + + // Store the image + $file = $this->storeImage($file, $refType, $refTable, $refDest); + + $return['files'][] = $file; + + // Indicate if we actually stored an image + $return['status'] = ($file != false); + + } 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(); + } + + + /* + * Store a single image file using a reference table to link to a table. + * + * Images are stored in various sizes in the media/images/{size} directories + * which are created if they don't already exist. + * + * Image names consist of the following... + * + * {target table name}_{target table id}-{original name}_{timestamp}.{ext} + * + * {target table name} The name of the table were the gallery target is located + * {target table id} The ID of the record in the target table associated with the gallery + * {original name} The base original name of the file + * {ext} The file extension related to the type of the image + * + * @param $file array Array containing uploaded file data + * @param $refType integer What this image is related to + * @param $refTable string Table name where the reference data for this image is stored + * @param $refDest integer Pointer to table entry this image is for + * + * @return array + * + */ + public function storeFile ($file, $refType = false, $refTable = false, $refDest = false, $caption = '') + { + + // If $file is just a URL to an image, then simulate the file array from a form submission + if (!is_array($file)) { + $file = array( + 'tmp_name' => $file, + 'name' => $file + ); + } + + // Get the desired file name and add a timestamps to it to ensure that it's unique + $fInfo = pathinfo($file['name']); + + // Strip all characters from the file name other than the permitted characters. + $fInfo['filename'] = preg_replace('/([^a-zA-Z0-9-_\.]+)/','_', $fInfo['filename']); + + // Build new file name + if ($refType != false) { + $newFilename = $refTable.'_'.$refDest.'-'.strtolower($fInfo['filename'].'_'.time().'.'.$fInfo['extension']); + } else { + $newFilename = strtolower($fInfo['filename'].'_'.time().'.'.$fInfo['extension']); + } + + $fileUploaded = move_uploaded_file( $_FILES['file']['tm_name'], GLM_MEMBERS_PLUGIN_IMAGES_PATH . '/'. $newFilename ); + + // If we have a good image + if ( $fileUploaded ) { + + $file['newFileName'] = $newFilename; + + // If using a reference tab,e, Store image name in images table + if ($refType != false) { + $sql = " + INSERT INTO ".GLM_MEMBERS_PLUGIN_DB_PREFIX ."files + ( + name, + file_name, + descr, + caption, + status, + position, + ref_type, + ref_dest + ) + VALUES + ( + '".$file['name']."', + '".$file['newFileName']."', + '', + '".addslashes($caption)."', + ".$this->config['status_numb']['Active'].", + 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 $file; + + // Otherwise we don't have a good image, so fail + } else { + return false; + } + + + } + +} diff --git a/models/admin/management/import/members.php b/models/admin/management/import/members.php index f573ec21..ba519e97 100644 --- a/models/admin/management/import/members.php +++ b/models/admin/management/import/members.php @@ -232,8 +232,8 @@ if (!$failure) { $res = pg_query($db, $sql); $rows = pg_num_rows($res); if ($rows == 0) { - $templateData['genError'] = 'There does not appear to be any member/credit card cross-reference entries listed in this database!'; - $failure = true; + //$templateData['genError'] = 'There does not appear to be any member/credit card cross-reference entries listed in this database!'; + //$failure = true; } else { $tmp = pg_fetch_all($res); if (count($tmp) != $rows) { diff --git a/models/admin/member/memberInfo.php b/models/admin/member/memberInfo.php index 253871f8..e64806f6 100644 --- a/models/admin/member/memberInfo.php +++ b/models/admin/member/memberInfo.php @@ -101,6 +101,20 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo * @access public */ public $imageGallery = false; + /** + * Have files + * + * @var bool + * @access public + */ + public $haveFiles = false; + /** + * Files + * + * @var bool + * @access public + */ + public $files = false; /** * Amenities * @@ -169,7 +183,7 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo * * 'status' * - * True if successfull and false if there was a fatal failure. + * True if successful and false if there was a fatal failure. * * 'menuItemRedirect' * @@ -184,7 +198,7 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo * * 'view' * - * A suggested view name that the contoller should use instead of the + * A suggested view name that the controller should use instead of the * default view for this model or false to indicate that the default view * should be used. * @@ -310,10 +324,19 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php'; $Images = new GlmDataImages($this->wpdb, $this->config); + // Load File data class + require_once GLM_MEMBERS_PLUGIN_CLASS_PATH . '/data/dataFiles.php'; + $Files = new GlmDataFiles( $this->wpdb, $this->config ); + // Update image gallery titles, descriptions, and image positions then return current image gallery $this->imageGallery = $Images->galleryImageDataUpdate($this->config['ref_type_numb']['MemberInfo'], $this->memberInfoID, 'galleryPositionOrder'); $this->haveImageGallery = ($this->imageGallery != false); + // Update file captions, descriptions, and file positions then + // return current files data. + $this->files = $Files->filesDataUpdate( $this->config['ref_type_numb']['MemberInfo'], $this->memberInfoID, 'filePositionOrder' ); + $this->haveFiles = ($this->files != false); + if ($this->haveMemberInfo) { // Update the member Info data @@ -445,12 +468,18 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo // Say we do $this->haveMemberInfo = true; - // Also, if this is not a create, get any image gallery images if ($this->memberInfoID) { + // Also, if this is not a create, get any image gallery images require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataImages.php'; $Images = new GlmDataImages($this->wpdb, $this->config); $this->imageGallery = $Images->getGallery($this->config['ref_type_numb']['MemberInfo'], $this->memberInfoID); $this->haveImageGallery = ($this->imageGallery != false); + + // Also, if this is not a create, get any files + require_once GLM_MEMBERS_PLUGIN_CLASS_PATH . '/data/dataFiles.php'; + $Files = new GlmDataFiles( $this->wpdb, $this->config ); + $this->files = $Files->getFiles( $this->config['ref_type_numb']['MemberInfo'], $this->memberInfoID ); + $this->haveFiles = ($this->files != false); } // Otherwise @@ -484,6 +513,8 @@ class GlmMembersAdmin_member_memberInfo extends GlmDataMemberInfo 'availableAmenities' => $this->amenities, 'haveImageGallery' => $this->haveImageGallery, 'imageGallery' => $this->imageGallery, + 'haveFiles' => $this->haveFiles, + 'files' => $this->files, 'noActive' => $this->noActiveInfoRecord(), 'time' => time() ); diff --git a/setup/databaseScripts/create_database_V1.1.26.sql b/setup/databaseScripts/create_database_V1.1.27.sql similarity index 97% rename from setup/databaseScripts/create_database_V1.1.26.sql rename to setup/databaseScripts/create_database_V1.1.27.sql index a369b7c2..8ecbb4c9 100644 --- a/setup/databaseScripts/create_database_V1.1.26.sql +++ b/setup/databaseScripts/create_database_V1.1.27.sql @@ -1,6 +1,6 @@ -- Gaslight Media Members Database -- File Created: 12/29/16 12:06:00 --- Database Version: 1.1.26 +-- Database Version: 1.1.27 -- Database Creation Script -- -- To permit each query below to be executed separately, @@ -117,6 +117,7 @@ CREATE TABLE {prefix}files ( status TINYINT(1) NULL, -- Display/Use status - See plugin.ini status table file_name TINYTEXT NULL, -- Stored file name for the file descr TEXT NULL, -- Description + caption TINYTEXT NULL, -- Caption for the image position INT NULL, -- Numeric position for sequence of display ref_type INT NULL, -- Type of entity this image is associated with ref_dest INT NULL, -- Pointer to the specific entity of ref_type this image is associated with @@ -202,6 +203,7 @@ CREATE TABLE {prefix}member_info ( lat FLOAT NULL, -- Latitude of member's location lon FLOAT NULL, -- Longitude of member's location region INT NULL, -- Pointer to entry in regions table + county INT NULL, -- Pointer to entry in regions table phone TINYTEXT NULL, -- Primary phone number toll_free TINYTEXT NULL, -- Toll Free phone number url TINYTEXT NULL, -- URL with information about this member @@ -232,7 +234,8 @@ CREATE TABLE {prefix}member_info ( INDEX(zip(10)), INDEX(lat), INDEX(lon), - INDEX(region) + INDEX(region), + INDEX(county) ); ---- @@ -259,6 +262,17 @@ CREATE TABLE {prefix}regions ( ---- +-- Counties +CREATE TABLE {prefix}counties ( + id INT NOT NULL AUTO_INCREMENT, + name TINYTEXT NULL, -- Name of county + descr TEXT NULL, -- Description of county + short_descr TINYTEXT NULL, -- Short description of county + PRIMARY KEY (id) +); + +---- + -- General settings available on Management page in admin - Only 1 entry in this table -- Items in this table should be all self-explanatory CREATE TABLE {prefix}settings_general ( diff --git a/setup/databaseScripts/dbVersions.php b/setup/databaseScripts/dbVersions.php index 34389698..b5e81436 100644 --- a/setup/databaseScripts/dbVersions.php +++ b/setup/databaseScripts/dbVersions.php @@ -58,6 +58,7 @@ $glmMembersDbVersions = array( '1.1.24' => array('version' => '1.1.24', 'tables' => 18, 'date' => '01/03/17'), '1.1.25' => array('version' => '1.1.25', 'tables' => 18, 'date' => '02/02/17'), '1.1.26' => array('version' => '1.1.26', 'tables' => 18, 'date' => '03/08/17'), + '1.1.27' => array('version' => '1.1.27', 'tables' => 19, 'date' => '03/29/17'), ); diff --git a/setup/databaseScripts/drop_database_V1.1.24.sql b/setup/databaseScripts/drop_database_V1.1.27.sql similarity index 100% rename from setup/databaseScripts/drop_database_V1.1.24.sql rename to setup/databaseScripts/drop_database_V1.1.27.sql diff --git a/setup/databaseScripts/update_database_V1.1.27.sql b/setup/databaseScripts/update_database_V1.1.27.sql new file mode 100644 index 00000000..5ff0d0a5 --- /dev/null +++ b/setup/databaseScripts/update_database_V1.1.27.sql @@ -0,0 +1,25 @@ +-- Gaslight Media Members Database +-- File Created: 03/29/17 +-- Database Version: 1.1.27 +-- Database Update From Previous Version Script +-- +-- To permit each query below to be executed separately, +-- all queries must be separated by a line with four dashes +-- Sets which character to use to separate phone numbers + +ALTER TABLE {prefix}files ADD COLUMN caption TINYTEXT DEFAULT NULL; + +---- + +ALTER TABLE {prefix}member_info ADD COLUMN county INT DEFAULT NULL; + +---- + +-- Counties +CREATE TABLE {prefix}counties ( + id INT NOT NULL AUTO_INCREMENT, + name TINYTEXT NULL, -- Name of county + descr TEXT NULL, -- Description of county + short_descr TINYTEXT NULL, -- Short description of county + PRIMARY KEY (id) +); diff --git a/setup/validActions.php b/setup/validActions.php index c0ab1a35..4ba560d0 100644 --- a/setup/validActions.php +++ b/setup/validActions.php @@ -34,6 +34,7 @@ $glmMembersValidActions = array( 'adminActions' => array( 'ajax' => array( 'imageUpload' => 'glm-member-db', + 'fileUpload' => 'glm-member-db', 'newOldMemberIdsCsv' => 'glm-member-db', 'membersListExport' => 'glm-member-db', 'memberClickThrough' => 'glm-member-db', diff --git a/views/admin/member/memberInfo.html b/views/admin/member/memberInfo.html index b843ff4c..6030e524 100644 --- a/views/admin/member/memberInfo.html +++ b/views/admin/member/memberInfo.html @@ -102,6 +102,7 @@ Categories & Amenities Images/Media + Files @@ -119,19 +120,20 @@ {include file='admin/member/memberInfo/editCategory.html'} - + {include file='admin/member/memberInfo/editMedia.html'} + + + + {include file='admin/member/memberInfo/editFiles.html'} {if $memberInfoID && $memberInfo} {apply_filters('glm-member-db-member-info-custom-tab', '', $memberInfoID)} {else} {apply_filters('glm-member-db-member-info-custom-tab', '', '')} {/if} - - - {include file='admin/member/memberInfo/editCreditCards.html'}

* Required

@@ -229,7 +231,7 @@ // Setup edit area selected function glmSetupAreaTab(t) { - // Clear tabl highlights and hide all tables + // Clear table highlights and hide all tables $('.glm-member-info-tab').removeClass('nav-tab-active'); $('.glm-member-info-table').addClass('glm-hidden'); diff --git a/views/admin/member/memberInfo/editFiles.html b/views/admin/member/memberInfo/editFiles.html new file mode 100644 index 00000000..adc84d3f --- /dev/null +++ b/views/admin/member/memberInfo/editFiles.html @@ -0,0 +1,119 @@ + + + + + +
Files + {if $memberInfoID} + +
+ +
+ +
+ + + Uploading File { thisFile } of { numbFiles } + + + + + + + + + + + +
+
+
+
Cancel Upload
Name: { fileName }
Type: { fileType }
Size: { fileSize }
Progress:
+ + +
+ +
+ + +
  • +
    + + + + + + + + + + +
    Caption: + + + + +
    Delete:
    featured File:
    New Upload
    +
    Description:
    +
    +
  • + + +
    +
    +
    +
    Drag and drop new files here
    +
    HTML5 file drag-and-drop not supported by your browser.
    Use "Browse" button above to upload an file.
    +
    +
      + + {if $haveFiles} + {foreach $files as $i} + {if $i.file_name} +
    • +
      + + + + + + + + + + + +
      Caption: + + +
      Delete:
      + +
      Description: +
      +
      +
      + {$i.name} +
      + {/if} +
    • + + {/foreach} + {/if} +
    + {else} + Note: Files may only be added to {$terms.term_member} profile records that have been created. + You are editing information to create a new record. +
    After you click "Add new {$terms.term_member}" below, you may come back here to add files. + {/if} +
    diff --git a/views/admin/member/memberInfo/editStatus.html b/views/admin/member/memberInfo/editStatus.html new file mode 100644 index 00000000..264a7339 --- /dev/null +++ b/views/admin/member/memberInfo/editStatus.html @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + +
    Profile Name: + + {if $memberInfo.fieldFail.reference_name}

    {$memberInfo.fieldFail.reference_name}

    {/if}
    + The "Profile Name" field is used to help identify this particular {$terms.term_member} profile. {$terms.term_member_plur_cap} may have multiple profiles, + only one of which can be active at a time. Multiple profiles make it easy to manage separate sets of information that may be + used at different times, for example seasons of the year or for special events (i.e. "Winter Profile"). +
    Profile Status: + + {if $memberInfo.fieldFail.status}

    {$memberInfo.fieldFail.status}

    {/if} +
    Description: + {php} + wp_editor('{$memberInfo.fieldData.descr|escape:quotes}', 'glm_descr', array( + 'media_buttons' => false, + // 'quicktags' => false, + // 'wpautop' => false, NOTE: Dont's use. Problem when numerous spaces before text. + 'textarea_name' => 'descr', + 'editor_height' => 200, // Height in px, overrides editor_rows + // 'textarea_rows' => 8 + )); + {/php} + {if $memberInfo.fieldFail.descr}

    {$memberInfo.fieldFail.descr}

    {/if} +
    Short Description: + +
    + {if $memberInfo.fieldFail.short_descr}

    {$memberInfo.fieldFail.short_descr}

    {/if} +
    Notes: + + {if $memberInfo.fieldFail.notes} +

    {$memberInfo.fieldFail.notes}

    {/if}
    This + "Notes" field is not displayed anywhere else. It is + strictly for making and keeping notes related to this + {$terms.term_member} profile. +
    + diff --git a/views/front/members/detail.html b/views/front/members/detail.html index 8bf9c412..8863305c 100644 --- a/views/front/members/detail.html +++ b/views/front/members/detail.html @@ -143,7 +143,7 @@
    - + -- 2.17.1