Update field plugin for csv import feature.
authorSteve Sutton <steve@gaslightmedia.com>
Wed, 26 Jul 2017 17:23:10 +0000 (13:23 -0400)
committerSteve Sutton <steve@gaslightmedia.com>
Wed, 26 Jul 2017 17:23:10 +0000 (13:23 -0400)
Adding csv import for the custom fields.

models/admin/import/fields.php [new file with mode: 0644]
setup/adminTabs.php
setup/validActions.php
views/admin/import/fields.html [new file with mode: 0644]
views/admin/import/fieldsProcess.html [new file with mode: 0644]
views/admin/import/fieldsValidate.html [new file with mode: 0644]

diff --git a/models/admin/import/fields.php b/models/admin/import/fields.php
new file mode 100644 (file)
index 0000000..e89a270
--- /dev/null
@@ -0,0 +1,411 @@
+<?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_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 '<pre>$fields: ' . print_r( $fields, true ) . '</pre>';
+                    $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'         => '<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 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;
+    }
+}
index 0860651..1a1447e 100644 (file)
@@ -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' => '<p>' . __( 'Follow the Sample File provided in creating the file for custom field import.
+                Each record must have the following fields. <h3>Required Fields</h3><ul><li>member_id</li><li>member_name</li><li>member_login</li>
+                <li>member_passwd</li><li>member_email</li></ul>' )  . '</p>'
+            );
+            return $helpTabs;
+        }
+    );
+}
index 33ebbae..086f431 100644 (file)
@@ -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 (file)
index 0000000..0b17672
--- /dev/null
@@ -0,0 +1,48 @@
+{include file='admin/import/header.html'}
+
+    <h2>Social Data Import Step 1: Upload file</h2>
+
+    <form action="{$thisUrl}?page={$thisPage}" method="post" enctype="multipart/form-data">
+        <input type="hidden" name="glm_action" value="fields" />
+        <input type="hidden" name="option" value="fieldsValidate" />
+
+        <table class="glm-admin-table" border="0" cellspacing="5" cellpadding="10">
+            <tr>
+                <th>File Type</th>
+                <th>New File</th>
+                <th>Sample File</th>
+                <th>Current File</th>
+                <th>Updated</th>
+            </tr>
+            {$count = 0}
+            {foreach $fileData as $fileHeader => $file}
+            <tr{if $count%2 == 0} class="alternate"{/if}>
+                <td class="glm-import-td">
+                    {$fileHeader}
+                </td>
+                <td class="glm-import-td">
+                    <input type="file" name="{$file.field}">
+                </td>
+                <td class="glm-import-td">
+                    <a href="{$sampleFileUrl}{$file.name}">Sample {$fileHeader} File</a>
+                </td>
+                <td class="glm-import-td">
+                    {if $file.exists}
+                        {$fileHeader} File
+                    {/if}
+                </td>
+                <td class="glm-import-td">
+                    {if $file.exists}
+                        {$file.mtime|date_format:"%D %I:%M %p"}
+                    {/if}
+                </td>
+            </tr>
+            {$count = $count + 1}
+            {/foreach}
+        </table>
+
+        <input type="submit" value="Continue" class="button button-primary submit-import">
+
+    </form>
+
+{include file='admin/footer.html'}
diff --git a/views/admin/import/fieldsProcess.html b/views/admin/import/fieldsProcess.html
new file mode 100644 (file)
index 0000000..dab58af
--- /dev/null
@@ -0,0 +1,16 @@
+{include file='admin/import/header.html'}
+
+    <h2>Data Import Step 3: Process Fields File</h2>
+    <table class="glm-admin-table">
+        <tr>
+            <th>Total Records</th>
+            <td>{$totalFields}</td>
+        </tr>
+        <tr>
+            <th>Processed Records</th>
+            <td>{$numberProcessed}</td>
+        </tr>
+
+    </table>
+
+{include file='admin/footer.html'}
diff --git a/views/admin/import/fieldsValidate.html b/views/admin/import/fieldsValidate.html
new file mode 100644 (file)
index 0000000..9ff514d
--- /dev/null
@@ -0,0 +1,40 @@
+{include file='admin/import/header.html'}
+
+    <h2>Secial Import Step 2: Validate Fields file</h2>
+
+    <table class="glm-admin-table">
+        {foreach $fileData as $fileHeader => $file}
+        <tr>
+            <td>
+                {if $file.exists}
+                    {$fileHeader} File
+                {/if}
+            </td>
+            <td>
+                {if $file.isValid}
+                    Is Valid
+                {else}
+                    Not Valid
+                {/if}
+            </td>
+        </tr>
+        {/foreach}
+
+        {if $readyToProcess}
+        <tr>
+            <td colspan="2">
+                <a href="{$thisUrl}?page={$thisPage}&glm_action=fields&option=fieldsProcess" class="button">Process Files</a>
+            </td>
+        </tr>
+        {else}
+        <tr>
+            <td colspan="2">
+                <p>One or more of your files are not the correct csv format. Please go back and try again.</p>
+                <a href="{$thisUrl}?page={$thisPage}&glm_action=fields" class="button">Go Back</a>
+            </td>
+        </tr>
+        {/if}
+
+    </table>
+
+{include file='admin/footer.html'}