--- /dev/null
+<?php
+
+/**
+ * Gaslight Media Members Database
+ * Admin Data Import
+ *
+ * PHP version 5.5
+ *
+ * @category glmWordPressPlugin
+ * @package glmMembersDatabase
+ * @author Chuck Scott <cscott@gaslightmedia.com>
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ * @version 0.1
+ */
+require_once GLM_MEMBERS_CONTACTS_PLUGIN_CLASS_PATH.'/data/dataContacts.php';
+/*
+ * This class performs the work for the default action of the "Import" menu
+ * option.
+ *
+ */
+class GlmMembersAdmin_import_contacts extends GlmDataContacts
+{
+
+ 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;
+
+ /**
+ * totalContacts
+ *
+ * @var float
+ * @access public
+ */
+ public $totalContacts = 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 = 'contacts.html';
+ $failure = false;
+ $option = 'contacts';
+ $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(
+ 'Contacts' => array(
+ 'field' => 'contacts_file',
+ 'name' => 'contactsData.csv',
+ 'exists' => false,
+ 'validate' => array(
+ 'member_id', 'member_name', 'member_login', 'member_passwd', 'member_email', 'first_name', 'last_name'
+ ),
+ 'type' => 'contacts',
+ ),
+ );
+ // 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 'contactsValidate';
+ $validFiles = 0;
+ // Set the view file
+ $view = 'contactsValidate.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 'contactsProcess':
+ foreach ( $fileData as $fileHeader => $file ) {
+ if ( is_file( $uploadPath . '/' . $file['name'] ) ) {
+ $contacts = $this->readCsvContacts( $uploadPath . '/'. $file['name'] );
+ $this->totalContacts = count( $contacts );
+ $importRet = $this->processCsvContacts( $contacts );
+ }
+ }
+ 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->totalContacts ) {
+ $this->processingComplete = true;
+ }
+ // Set the view file:<
+ $view = 'contactsProcess.html';
+ break;
+
+ case 'contacts':
+ default:
+ // Set the view file
+ $view = 'contacts.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,
+ 'totalContacts' => $this->totalContacts,
+ 'completed' => $this->processingComplete,
+ 'data' => false,
+ 'fileData' => $fileData,
+ 'clearData' => $clearData,
+ 'importRet' => $importRet,
+ 'csvData' => '<pre>$fileData: ' . print_r( $fileData, true ) . '</pre>',
+ '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 contacts 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 readCsvContacts($csv)
+ {
+
+ $contacts = 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) >= 5) {
+
+ // Add this line of data to Contacts
+ $contacts[] = $c;
+
+ }
+
+ // If we find the header, assume all data is below that
+ if ($c[0] == 'member_id' && $c[1] == 'member_name') {
+ $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($contacts) == 0) {
+ return "Header found but no data followed";
+ }
+
+ return $contacts;
+
+ }
+
+ /**
+ * processCsvContacts
+ *
+ * Process csv contact data and import or alter contact as appropriate
+ *
+ * @param array Array of contacts as produced by readCsvContacts()
+ *
+ * @return array Results or false if no contacts to import
+ *
+ */
+ public function processCsvContacts($contacts)
+ {
+
+ // Initialize our return status/data array
+ $ret = array(
+ 'status' => false,
+ 'errorMsg' => false,
+ 'numbContacts' => false,
+ 'numbContactsUpdated' => 0,
+ 'numbWpUserUpdated' => 0,
+ 'numbImported' => 0,
+ 'numbWpUsersCreated' => 0,
+ 'exceptionTable' => array()
+ );
+
+ // Make sure we have contacts
+ if (!is_array($contacts) || count($contacts) == 0) {
+ $ret['errorMsg'] = 'No contacts were supplied.';
+ return $ret;
+ }
+
+ // Instantiate member data class
+ require_once GLM_MEMBERS_PLUGIN_CLASS_PATH.'/data/dataMembers.php';
+ $Members = new GlmDataMembers($this->wpdb, $this->config);
+
+ $ret['numbContacts'] = count($contacts);
+
+ // For each contact
+ foreach ($contacts as $c) {
+
+ $okToProcess = false;
+
+ // Get the contact data into standard parameters
+ $oldMemberID = ($c[0]-0);
+ $memberName = trim($c[1]);
+ $memberLogin = preg_replace('/[^a-zA-Z0-9_-]+/', '', trim($c[2]));
+ $memberPasswd = trim($c[3]);
+ $memberContactEmail = trim($c[4]);
+ $contactFname = trim($c[5]);
+ $contactLname = trim($c[6]);
+
+ // Check if we had to fix up the username (memberLogin)
+ if ($memberLogin != $c[2]) {
+
+ // Report that we're stripping illegal characters from the username
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Removed invalid characters from this contact\'s username:',
+ 'data' => $memberName
+ .", Old member id = ".$oldMemberID
+ .", Username = ".$memberLogin." - was ".$c[2]
+ );
+
+ }
+
+ // Make sure we have a member matching the member_id
+ $membData = $Members->getEntry(false, 'id', 'T.old_member_id = '.$oldMemberID);
+ // echo '<pre>$membData: ' . print_r( $membData, true ) . '</pre>';
+ // exit;
+ if (!$membData) {
+
+ // Report that we didn't find the matching member
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'No member matching this old member ID:',
+ 'data' => $oldMemberID
+ .", Old member id = ".$oldMemberID
+ .", Username = ".$memberLogin." - was ".$memberLogin
+ );
+
+ $okToProcess = false;
+
+ } else if ( !$memberContactEmail ) {
+ // Report that the email field is empty
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Email field is empty:',
+ 'data' => $oldMemberID
+ .", Old member id = ".$oldMemberID
+ .", Username = ".$memberLogin." - was ".$memberLogin
+ );
+
+ $okToProcess = false;
+ } else {
+
+ // If we have and existing contact matching this user name (only 1) - Consider doing updates
+ $contactData = $this->getSimplified("T.username = '".addslashes($memberLogin)."'");
+ if ($contactData && count($contactData) ==1 ) {
+
+ $contactData = current($contactData);
+
+ // Check for changes to this contact
+ if ($memberContactEmail != $contactData['email'] || $memberPasswd != '') {
+
+ // Check if there's an existing WordPress user with this username
+ $contactCheck = $this->checkContact($memberContactEmail, $memberLogin);
+ if ($contactCheck['wordpressLogin'] && $contactCheck['wpUserLogin']->ID) {
+
+ $wpUserID = $contactCheck['wpUserLogin']->ID;
+
+ // get the contact ID for the WordPress user and make sure it matches the member with the correct old ID
+ $contactID = get_user_meta($wpUserID, 'glmMembersContactID', true);
+
+ // Check that the E-mail address isn't in use by another WordPress user
+ if ($contactCheck['wordpressEmail'] &&
+ $contactCheck['wpUserEmail']->ID != $contactCheck['wpUserLogin']->ID) {
+
+ // The E-Mail address is in use by another user so we can't set that for this contact
+ $ret['exceptionTable'][] = array(
+ 'exception' => "Another WordPress user exists with the requested E-Mail address (no dupes allowed): ",
+ 'data' => $memberName
+ .", Username = ".$memberLogin
+ .", Old member id = ".$oldMemberID
+ .", E-mail address = ".$memberContactEmail
+ );
+
+ // Also make sure the WordPress user matches the contact record
+ } elseif ($contactID != $contactData['id']) {
+
+ // The WordPress user meta data says this user is for a different contact
+ $ret['exceptionTable'][] = array(
+ 'exception' => "The WordPress user data says it is associated with a different contact - Not using this data: ",
+ 'data' => $memberName
+ .", Username = ".$memberLogin
+ .", Old member id = ".$oldMemberID
+ .", E-mail address = ".$memberContactEmail
+ );
+
+ // So now we should be OK to update the E-Mail address for this contact and WordPress user
+ } else {
+
+ // Update the contact Email data
+ $rows = $this->wpdb->update(
+ GLM_MEMBERS_CONTACTS_PLUGIN_DB_PREFIX . 'contacts',
+ array('email' => $memberContactEmail),
+ array( 'id' => $contactData['id']),
+ array('%s'),
+ array( '%d' )
+ );
+
+ // Check for failure
+ if ($rows != 1) {
+
+ // Report that we couldn't update the E-Mail address
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Problem updating E-mail address for this contact:',
+ 'data' => $memberName
+ .", Old member id = ".$oldMemberID
+ .", Username = ".$memberLogin
+ .", E-Mail address = ".$memberContactEmail
+ ."<br>Error Message: ".$rows->get_error_message()
+ );
+
+ // If we're still OK, try to update the WordPress user E-Mail address
+ } else {
+
+ $ret['numbContactsUpdated']++;
+
+ // Build data for updating WordPress user
+ $updateData = array(
+ 'ID' => $wpUserID,
+ 'user_email' => $memberContactEmail
+ );
+
+ // if a password is supplied, update that also
+ $updatePass = '';
+ if ($memberPasswd != '') {
+ $updateData['user_pass'] = $memberPasswd;
+ $updatePass = ' and password';
+ }
+
+ // Try to update the WordPress user - If all is fine, this should return the WordPress user ID, otherwise an error object
+ $updateStatus = wp_insert_user($updateData);
+ if ($updateStatus != $wpUserID) {
+
+ // Report that we had a problem updating the wordpress user
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Problem updating the E-mail address'.$updatePass.' of the matching WordPress user:',
+ 'data' => $memberName
+ .", Old member id = ".$oldMemberID
+ .", Username = ".$memberLogin
+ .", E-Mail address = ".$memberContactEmail
+ ."<br>Error Message: ".$updateStatus->get_error_message()
+ );
+
+ } else {
+ $ret['numbWpUserUpdated']++;
+ }
+
+ } // OK to update WordPress user
+
+ } // OK to update contact
+
+ } // Existing WordPress user
+
+ } // Changing existing contact
+
+ // Otherwise we don't have an existing contact, so try to create one
+ } else {
+
+ // Check if there's an existing WordPress user with this username
+ $contactCheck = $this->checkContact($memberContactEmail, $memberLogin);
+ if ($contactCheck['wordpressLogin'] && $contactCheck['wpUserLogin']->ID) {
+
+ $ret['exceptionTable'][] = array(
+ 'exception' => "A WordPress user already exists with this username - Can't create this contact: ",
+ 'data' => $memberName
+ .", Username = ".$memberLogin
+ .", Old member id = ".$oldMemberID
+ .", E-mail address = ".$memberContactEmail
+ );
+
+ // Otherwise check if another user has this E-Mail address
+ } elseif ( !empty($contactCheck['wordpressEmail'])
+ && !empty( $contactCheck['wpUserLogin'] )
+ && $contactCheck['wpUserEmail']->ID != $contactCheck['wpUserLogin']->ID) {
+
+ // The E-Mail address is in use by another user so we can't set that for this contact
+ $ret['exceptionTable'][] = array(
+ 'exception' => "Another WordPress user exists with the requested E-Mail address, not creating this contact: ",
+ 'data' => $memberName
+ .", Username = ".$memberLogin
+ .", Old member id = ".$oldMemberID
+ .", E-mail address = ".$memberContactEmail
+ );
+
+ // Otherwise try to create the contact and WordPress user
+ } else {
+
+ // We don't have a proper contact name, so do this
+ //$contactFname = $memberName;
+ //$contactLname = '(contact name not provided)';
+
+ // Determine contact type for import
+ $contactType = $this->config['contact_type_numb']['Personal'];
+
+ // Determine the Worpress Role to be used for contact import - Using Entity Manager right now
+ $contactRoleNumb = $this->config['contact_role_numb']['EntityManager'];
+ $wpRole = $this->config['contact_role_wordpress'][$contactRoleNumb];
+
+ // Try to create new contact
+ $this->wpdb->insert(
+ GLM_MEMBERS_CONTACTS_PLUGIN_DB_PREFIX . 'contacts',
+ array(
+ 'active' => true,
+ 'primary_contact' => true,
+ 'access' => $this->config['access_numb']['NotDisplayedModerated'],
+ 'fname' => $contactFname,
+ 'lname' => $contactLname,
+ 'contact_type' => $contactType,
+ 'contact_role' => $contactRoleNumb,
+ 'email' => $memberContactEmail,
+ 'username' => $memberLogin,
+ 'notes' => 'Imported from CSV file.',
+ 'create_time' => date('Y-m-d H:i:s', time()),
+ 'ref_type' => $this->config['ref_type_numb']['Member'],
+ 'ref_dest' => $membData['id']
+ ),
+ array(
+ '%d',
+ '%d',
+ '%d',
+ '%s',
+ '%s',
+ '%d',
+ '%d',
+ '%s',
+ '%s',
+ '%s',
+ '%s',
+ '%d',
+ '%d'
+ )
+ );
+ $newContactID = $this->wpdb->insert_id;
+
+ // If this contact didn't import correctly
+ if (!$newContactID) {
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Unable to create this contact: ',
+ 'data' => $c['member_name']
+ .", Old member id = ".$oldMemberID
+ .", E-mail address = ".$memberLogin
+ .", Username = ".$memberLogin
+ .", Name = $contactFname $contactLname"
+ );
+
+ // Contact imported correctly, proceed with WordPress user
+ } else {
+
+ if (trim($memberPasswd) == '') {
+ $memberPasswd = wp_generate_password( $length=12, $include_standard_special_chars=false );
+ }
+
+ // Try to create the WordPress user
+ $wpUserID = wp_insert_user(
+ array(
+ 'user_email' => $memberContactEmail,
+ 'user_login' => $memberLogin,
+ 'user_pass' => $memberPasswd,
+ 'first_name' => $contactFname,
+ 'last_name' => $contactLname,
+ 'role' => $wpRole
+ )
+ );
+
+ // If the result is an integer that's positive, the user was created
+ if (is_int($wpUserID) && $wpUserID > 0) {
+
+ // Store the contact ID and active status into user meta data
+ update_user_meta($wpUserID, 'glmMembersContactID', $newContactID);
+ update_user_meta($wpUserID, 'glmMembersContactActive', true);
+
+ $ret['numbWpUsersCreated']++;
+
+ // Else, add a an entry to the exception table for why
+ } else {
+
+ $ret['exceptionTable'][] = array(
+ 'exception' => 'Unable to add a WordPress user for this contact: ',
+ 'data' => $memberName
+ .", Old member id = ".$oldMemberID
+ .", Member Name = ".$memberName
+ .", Username = ".$memberLogin
+ ."<br>Error Message: ".$wpUserID->get_error_message()
+ );
+
+ }
+
+ }
+
+ $ret['numbImported']++;
+
+ } // else try to create contact and wp user
+
+ } // else add contact
+
+ } // else we have a good member ID
+
+ } // for each contact
+
+ $ret['status'] = true;
+
+ return $ret;
+
+ }
+
+ /**
+ * 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;
+ }
+}