From ea08f3603cbcdd4813a6da1cf34424af8a385888 Mon Sep 17 00:00:00 2001 From: Steve Sutton Date: Wed, 26 Jul 2017 13:23:10 -0400 Subject: [PATCH] Update field plugin for csv import feature. Adding csv import for the custom fields. --- models/admin/import/fields.php | 411 +++++++++++++++++++++++++ setup/adminTabs.php | 27 ++ setup/validActions.php | 3 + views/admin/import/fields.html | 48 +++ views/admin/import/fieldsProcess.html | 16 + views/admin/import/fieldsValidate.html | 40 +++ 6 files changed, 545 insertions(+) create mode 100644 models/admin/import/fields.php create mode 100644 views/admin/import/fields.html create mode 100644 views/admin/import/fieldsProcess.html create mode 100644 views/admin/import/fieldsValidate.html diff --git a/models/admin/import/fields.php b/models/admin/import/fields.php new file mode 100644 index 0000000..e89a270 --- /dev/null +++ b/models/admin/import/fields.php @@ -0,0 +1,411 @@ + + * @license http://www.gaslightmedia.com Gaslightmedia + * @version 0.1 + */ +require_once GLM_MEMBERS_FIELDS_PLUGIN_CLASS_PATH.'/data/dataCustomFieldsData.php'; +require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataMemberInfo.php'; +/* + * This class performs the work for the default action of the "Import" menu + * option. + * + */ +class GlmMembersAdmin_import_fields extends GlmDataFieldsCustomFieldsData +{ + + const CSV_CHARS_PER_LINE = 6000; + /** + * WordPress Database Object + * + * @var $wpdb + * @access public + */ + public $wpdb; + /** + * Plugin Configuration Data + * + * @var $config + * @access public + */ + public $config; + + /** + * errors + * + * @var $errors + * @access public + */ + public $errors = array(); + + /** + * numberProcessed + * + * @var float + * @access public + */ + public $numberProcessed = 0; + + /** + * totalFields + * + * @var float + * @access public + */ + public $totalFields = 0; + + /** + * processingComplete + * + * @var bool + * @access public + */ + public $processingComplete = false; + + /** + * Constructor + * + * This contractor 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 successful 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 controller 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) + { + // Set the view file + $view = 'fields.html'; + $failure = false; + $option = 'fields'; + $clearData = false; + $importRet = false; + $haveMembers = false; + $fileExists = false; + $isValid = false; + // Check to see if they have members + $haveMembers = $this->wpdb->get_var( + "SELECT count(id) + FROM " . GLM_MEMBERS_PLUGIN_DB_PREFIX . "members" + ); + // $fileData - The main files needed for the member import + // - field: input field name + // - name: file name + // - exists: Does file exists. Set to false at first. + // - validate: Validation array. Header line must match this. + // - type: Type of file. Used in the processing function. + $fileData = array( + 'Fields' => array( + 'field' => 'fields_file', + 'name' => 'fieldsData.csv', + 'exists' => false, + 'validate' => array( 'member_id', 'field_name', 'field_value' ), + 'type' => 'fields', + ), + ); + // Setting readyToProcess to false (initialize) + $readyToProcess = false; + + // Set the $option if found in $_REQUEST array + if (isset($_REQUEST['option']) && $_REQUEST['option'] != '') { + $option = $_REQUEST['option']; + } + + // Set the $option2 if found in $_REQUEST array + if (isset($_REQUEST['option2']) && $_REQUEST['option2'] != '') { + $option2 = $_REQUEST['option2']; + } + + // Set variable for the upload directory + $wpUploadDir = wp_get_upload_dir(); + + // Set the $uploadPath for import files + $uploadPath = $wpUploadDir['basedir'] . '/' . 'glm-member-import'; + + // If the folder for the upload import files doesn't exists create one. + if ( !is_dir( $uploadPath ) ) { + // Get old umask + $oldMask = umask(0); + // Set folder permission + mkdir( $uploadPath, 0770 ); + // reset old umask + umask( $oldMask ); + } + + switch( $option ) { + + case 'fieldsValidate'; + $validFiles = 0; + // Set the view file + $view = 'fieldsValidate.html'; + $fileCount = count( $fileData ); + // Move any files uploaded + if ( isset( $_FILES ) ) { + foreach ( $fileData as $fileHeader => $file ) { + if ( !$_FILES[$file['field']]['error'] ) { + move_uploaded_file( $_FILES[$file['field']]['tmp_name'], $uploadPath . '/'. $file['name'] ); + } + } + } + // Check that each file exists + foreach ( $fileData as $fileHeader => $file ) { + if ( is_file( $uploadPath . '/' . $file['name'] ) ) { + $fileData[$fileHeader]['exists'] = true; + $fData = $this->readCSVFileHeaders( $uploadPath . '/' . $file['name'] ); + $isValid = ( $file['validate'] == $fData ); + if ( $isValid ) { + $validFiles++; + } + $fileData[$fileHeader]['data'] = $fData; + $fileData[$fileHeader]['isValid'] = $isValid; + } + } + $readyToProcess = ( $validFiles == $fileCount ); + break; + + case 'fieldsProcess': + $memberInfoData = new GlmDataMemberInfo( $this->wpdb, $this->config ); + foreach ( $fileData as $fileHeader => $file ) { + if ( is_file( $uploadPath . '/' . $file['name'] ) ) { + $fields = $this->readCSVFields( $uploadPath . '/'. $file['name'] ); + //echo '
$fields: ' . print_r( $fields, true ) . '
'; + $this->totalFields = count( $fields ); + // Clear the custom field data table + $this->wpdb->query( "DELETE FROM " . GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . "custom_field_data" ); + foreach ( $fields as $customRow ) { + // Need to first get the member id from the database + // It will match from the old_member_id field + $memberId = $this->wpdb->get_var( + $this->wpdb->prepare( + "SELECT id + FROM " . GLM_MEMBERS_PLUGIN_DB_PREFIX . "members + WHERE old_member_id = %d", + $customRow[0] + ) + ); + // get the active member info id + $memberInfoId = + $memberInfoData->getActiveInfoIdForMember( $memberId ); + $customFieldId = $this->wpdb->get_var( + $this->wpdb->prepare( + "SELECT id + FROM " . GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . "custom_fields + WHERE field_name = %s", + $customRow[1] + ) + ); + if ( $customFieldId ) { + $newId = $this->wpdb->insert( + GLM_MEMBERS_FIELDS_PLUGIN_DB_PREFIX . 'custom_field_data', + array( + 'ref_dest' => $memberInfoId, + 'field_id' => $customFieldId, + 'field_data' => $customRow[2], + ), + array( + '%d', + '%d', + '%s' + ) + ); + $this->numberProcessed++; + } + } + $importRet = array(); + } + } + if ( count( $this->errors ) == 0 ) { + $readyToProcess = true; + } + // Here we need to check to see if we processed all members. + // Also the counter has to increment the total processed so far. + if ( $this->numberProcessed == $this->totalFields ) { + $this->processingComplete = true; + } + // Set the view file:< + $view = 'fieldsProcess.html'; + break; + + case 'fields': + default: + // Set the view file + $view = 'fields.html'; + // check upload dir to see if they have any files in yet + foreach ( $fileData as $fileHeader => $file ) { + if ( is_file( $uploadPath . '/' . $file['name'] ) ) { + $fileData[$fileHeader]['exists'] = true; + $fileData[$fileHeader]['mtime'] = filemtime( $uploadPath . '/' . $file['name'] ); + } + } + + break; + + } + + // Setup the template data array + $templateData = array( + 'fileExists' => $fileExists, + 'option' => $option, + 'errors' => $this->errors, + 'numberProcessed' => $this->numberProcessed, + 'totalFields' => $this->totalFields, + 'completed' => $this->processingComplete, + 'data' => false, + 'fileData' => $fileData, + 'clearData' => $clearData, + 'importRet' => $importRet, + 'csvData' => '
$fileData: ' . print_r( $fileData, true ) . '
', + 'readyToProcess' => $readyToProcess, + 'haveMembers' => $haveMembers, + 'isValid' => $isValid, + 'sampleFileUrl' => GLM_MEMBERS_PLUGIN_BASE_URL . '/sample-files/', + ); + + // Return status, suggested view, and data to controller + return array( + 'status' => true, + 'menuItemRedirect' => false, + 'modelRedirect' => false, + 'view' => 'admin/import/' . $view, + 'data' => $templateData, + ); + + } + + /** + * Read in fields from a csv file + * + * Header line as follows. Data follows immediately below this line. this + * line and all above it are ignored. + * + * 'member_id','member_name','member_login','member_passwd','contact_email' + * + * @param string $csv Temporary file name of csv file upload + * + * @return array Array of data from CSV file or an error message + * + */ + public function readCSVFields($csv) + { + + $fields = array(); + $startImport = false; + + // Try to open file + if (($handle = fopen($csv, "r")) !== false) { + + // For each line in the file + while (($c = fgetcsv($handle, 1000, ",")) !== false) { + + // If we're past the header, the first item is numeric, and we have at least 5 fields + if($startImport && ($c[0]-0) > 0 && count($c) >= 3) { + + // Add this line of data to Contacts + $fields[] = $c; + + } + + // If we find the header, assume all data is below that + if ($c[0] == 'member_id') { + $startImport = true; + } + + } + + fclose($handle); + + } else { + return "No file submitted."; + } + + // If we never found the header + if (!$startImport) { + return "Required header not found in file."; + } + + // If we found no data below the header + if (count($fields) == 0) { + return "Header found but no data followed"; + } + + return $fields; + + } + + /** + * readCSVFileHeaders + * + * Read the cvs file. Just the first line is read. + * + * @param mixed $fileName Name of the file (path) + + * @access public + * @return void + */ + public function readCSVFileHeaders( $fileName ) + { + $fileHeaders = array(); + if ( ( $fp = fopen( $fileName, 'r' ) ) !== false ) { + // get first line to use as headers + $fileHeaders = fgetcsv( $fp, SELF::CSV_CHARS_PER_LINE, ',' ); + fclose( $fp ); + } + + return $fileHeaders; + } +} diff --git a/setup/adminTabs.php b/setup/adminTabs.php index 0860651..1a1447e 100644 --- a/setup/adminTabs.php +++ b/setup/adminTabs.php @@ -50,3 +50,30 @@ if (current_user_can('glm_members_members')) { } ); } +if (current_user_can('glm_members_management')) { + add_filter('glm-member-db-add-tab-for-import', + function($addOnTabs) { + $newTabs = array( + array( + 'text' => 'Custom Fields', + 'menu' => 'import', + 'action' => 'fields', + ) + ); + $addOnTabs = array_merge($addOnTabs, $newTabs); + return $addOnTabs; + } + ); + add_filter( 'glm-member-db-add-help-for-import', + function( $helpTabs ){ + $helpTabs[] = array( + 'id' => 'glm-member-db-import-help-fields', + 'title' => __( 'Custom Fields' ), + 'content' => '

' . __( 'Follow the Sample File provided in creating the file for custom field import. + Each record must have the following fields.

Required Fields

' ) . '

' + ); + return $helpTabs; + } + ); +} diff --git a/setup/validActions.php b/setup/validActions.php index 33ebbae..086f431 100644 --- a/setup/validActions.php +++ b/setup/validActions.php @@ -68,6 +68,9 @@ $glmMembersFieldsAddOnValidActions = array( 'management' => array( 'fields' => GLM_MEMBERS_FIELDS_PLUGIN_SLUG, ), + 'import' => array( + 'fields' => GLM_MEMBERS_FIELDS_PLUGIN_SLUG, + ), ), 'frontActions' => array( 'fields' => array( diff --git a/views/admin/import/fields.html b/views/admin/import/fields.html new file mode 100644 index 0000000..0b17672 --- /dev/null +++ b/views/admin/import/fields.html @@ -0,0 +1,48 @@ +{include file='admin/import/header.html'} + +

Social Data Import Step 1: Upload file

+ +
+ + + + + + + + + + + + {$count = 0} + {foreach $fileData as $fileHeader => $file} + + + + + + + + {$count = $count + 1} + {/foreach} +
File TypeNew FileSample FileCurrent FileUpdated
+ {$fileHeader} + + + + Sample {$fileHeader} File + + {if $file.exists} + {$fileHeader} File + {/if} + + {if $file.exists} + {$file.mtime|date_format:"%D %I:%M %p"} + {/if} +
+ + + +
+ +{include file='admin/footer.html'} diff --git a/views/admin/import/fieldsProcess.html b/views/admin/import/fieldsProcess.html new file mode 100644 index 0000000..dab58af --- /dev/null +++ b/views/admin/import/fieldsProcess.html @@ -0,0 +1,16 @@ +{include file='admin/import/header.html'} + +

Data Import Step 3: Process Fields File

+ + + + + + + + + + +
Total Records{$totalFields}
Processed Records{$numberProcessed}
+ +{include file='admin/footer.html'} diff --git a/views/admin/import/fieldsValidate.html b/views/admin/import/fieldsValidate.html new file mode 100644 index 0000000..9ff514d --- /dev/null +++ b/views/admin/import/fieldsValidate.html @@ -0,0 +1,40 @@ +{include file='admin/import/header.html'} + +

Secial Import Step 2: Validate Fields file

+ + + {foreach $fileData as $fileHeader => $file} + + + + + {/foreach} + + {if $readyToProcess} + + + + {else} + + + + {/if} + +
+ {if $file.exists} + {$fileHeader} File + {/if} + + {if $file.isValid} + Is Valid + {else} + Not Valid + {/if} +
+ Process Files +
+

One or more of your files are not the correct csv format. Please go back and try again.

+ Go Back +
+ +{include file='admin/footer.html'} -- 2.17.1