Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 328
0.00% covered (danger)
0.00%
0 / 7
CRAP
n/a
0 / 0
zeroBSCRM_CSVImporterLiteadmin_menu
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
zeroBSCRM_CSVImporter_lite_admin_styles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
zeroBSCRM_CSVImporterLitepages_header
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
zeroBSCRM_CSVImporterLitepages_footer
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
zeroBSCRM_CSVImporterLitepages_app
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
jpcrm_csvimporter_lite_preflight_checks
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 1
552
zeroBSCRM_CSVImporterLitehtml_app
0.00% covered (danger)
0.00%
0 / 235
0.00% covered (danger)
0.00%
0 / 1
4830
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 * V1.0
6 *
7 * Copyright 2020 Automattic
8 *
9 * Date: 07/03/2017
10 */
11
12/*
13======================================================
14    Breaking Checks ( stops direct access )
15    ====================================================== */
16if ( ! defined( 'ZEROBSCRM_PATH' ) ) {
17    exit( 0 );
18}
19/*
20======================================================
21    / Breaking Checks
22    ====================================================== */
23
24/*
25// } #coreintegration - MOVED to core.extensions
26
27// } Log with main core (Has to be here, outside of all funcs)
28// } Note, this function permacode (e.g. woo) needs to have a matching function for settings page named "zeroBSCRM_extensionhtml_settings_woo" (e.g.)
29global $zeroBSCRM_extensionsInstalledList;
30if (!is_array($zeroBSCRM_extensionsInstalledList)) $zeroBSCRM_extensionsInstalledList = array();
31$zeroBSCRM_extensionsInstalledList[] = 'csvimporterlite'; #woo #pay #env
32// } Super simpz function to return this extensions name to core (for use on settings tabs etc.)
33function zeroBSCRM_extension_name_csvimporterlite(){ return 'CSV Importer LITE'; }
34*/
35
36    // } IMPORTANT FOR SETTINGS EXTENSIONS MODEL!
37    // } Unique str for each plugin extension, e.g. "mail" or "wooimporter" (lower case no numbers or spaces/special chars)
38    $zeroBSCRM_CSVImporterconfigkey = 'csvimporterlite';
39    $zeroBSCRM_extensions[]         = $zeroBSCRM_CSVImporterconfigkey;
40
41    global $zeroBSCRM_CSVImporterLiteslugs;
42$zeroBSCRM_CSVImporterLiteslugs            = array();
43    $zeroBSCRM_CSVImporterLiteslugs['app'] = 'zerobscrm-csvimporterlite-app'; // NOTE: this should now be ignored, use $zbs->slugs['csvlite'] as is WL friendly
44
45    global $zeroBSCRM_CSVImporterLiteversion;
46    $zeroBSCRM_CSVImporterLiteversion = '2.0';
47
48/*
49No settings included in CSV Importer LITE - pro only :)
50// } If legit... #CORELOADORDER
51if (!defined('ZBSCRMCORELOADFAILURE')){
52
53    #} Should be safe as called from core
54
55    #} Settings Model. req. > v1.1
56
57        #} Init settings model using your defaults set in the file above
58        #} Note "zeroBSCRM_extension_extensionName_defaults" var below must match your var name in the config.
59        global $zeroBSCRM_CSVImporterSettings, $zeroBSCRM_extension_extensionName_defaults;
60        $zeroBSCRM_CSVImporterSettings = new WHWPConfigExtensionsLib($zeroBSCRM_CSVImporterconfigkey,$zeroBSCRM_extension_extensionName_defaults);
61
62} */
63
64// CA: Block commented because the issue #1116 about a Woocommerce - JPCRM import conflict
65/*
66function zeroBSCRM_CSVImporterLite_extended_upload ( $mime_types =array() ) {
67
68    //$mime_types['csv']  = "text/csv";
69    //wonder it actually this..
70    $mime_types['csv']  = "text/plain";
71
72    return $mime_types;
73} */
74// add_filter('upload_mimes', 'zeroBSCRM_CSVImporterLite_extended_upload');
75
76// } Add le admin menu
77function zeroBSCRM_CSVImporterLiteadmin_menu() {
78
79    global $zbs, $zeroBSCRM_CSVImporterLiteslugs; // req
80
81    wp_register_style( 'zerobscrm-csvimporter-admcss', ZEROBSCRM_URL . 'css/ZeroBSCRM.admin.csvimporter' . wp_scripts_get_suffix() . '.css', array(), $zbs::VERSION );
82    $csv_admin_page = add_submenu_page( 'jpcrm-hidden', 'CSV Importer', 'CSV Importer', 'admin_zerobs_customers', $zbs->slugs['csvlite'], 'zeroBSCRM_CSVImporterLitepages_app', 1 ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
83    add_action( "admin_print_styles-{$csv_admin_page}", 'zeroBSCRM_CSVImporter_lite_admin_styles' );
84    add_action( "admin_print_styles-{$csv_admin_page}", 'zeroBSCRM_global_admin_styles' ); // } and this.
85}
86add_action( 'zerobs_admin_menu', 'zeroBSCRM_CSVImporterLiteadmin_menu' );
87
88function zeroBSCRM_CSVImporter_lite_admin_styles() {
89    wp_enqueue_style( 'zerobscrm-csvimporter-admcss' );
90}
91
92// ================== Admin Pages
93
94// } Admin Page header
95function zeroBSCRM_CSVImporterLitepages_header( $subpage = '' ) {
96
97    global $wpdb, $zbs, $zeroBSCRM_CSVImporterLiteversion;  // } Req
98
99    if ( ! current_user_can( 'admin_zerobs_customers' ) ) {
100        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'zero-bs-crm' ) ); }
101
102    ?>
103
104<div id="sgpBody">
105
106    <div id="ZeroBSCRMAdminPage" class="ui segment">
107
108<h1><?php echo ( empty( $subpage ) ? '' : esc_html( $subpage ) ); ?></h1>
109    <?php
110
111    // } Check for required upgrade
112    // zeroBSCRM_CSVImportercheckForUpgrade();
113}
114
115// } Admin Page footer
116function zeroBSCRM_CSVImporterLitepages_footer() {
117
118    ?>
119    </div>
120    <?php
121}
122
123// } Main Uploader Page
124function zeroBSCRM_CSVImporterLitepages_app() {
125
126    global $wpdb, $zbs, $zeroBSCRM_CSVImporterLiteversion;  // } Req
127
128    if ( ! current_user_can( 'admin_zerobs_customers' ) ) {
129        wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'zero-bs-crm' ) ); }
130
131    // } Homepage
132    zeroBSCRM_CSVImporterLitehtml_app();
133
134    // } Footer
135    zeroBSCRM_CSVImporterLitepages_footer();
136
137    ?>
138</div>
139    <?php
140}
141
142// catch errors with nonce or other oddities
143function jpcrm_csvimporter_lite_preflight_checks( $stage ) {
144
145    if ( ! isset( $_POST['zbscrmcsvimportnonce'] ) || ! wp_verify_nonce( $_POST['zbscrmcsvimportnonce'], 'zbscrm_csv_import' ) ) {
146        // hard no
147        zeroBSCRM_html_msg( -1, __( 'There was an error processing your CSV file. Please try again.', 'zero-bs-crm' ) );
148        exit( 0 );
149    }
150
151    // eventually update this to use the zbscrm-store/_wip replacement
152    // apparently sys_get_temp_dir() isn't consistent on whether it has a trailing slash
153    $tmp_dir = untrailingslashit( sys_get_temp_dir() );
154    $tmp_dir = realpath( $tmp_dir ) . DIRECTORY_SEPARATOR;
155
156    $field_map = array();
157
158    if ( $stage == 1 ) {
159
160        if ( empty( $_FILES['zbscrmcsvfile'] ) || empty( $_FILES['zbscrmcsvfile']['name'] ) ) {
161            throw new Exception( __( 'No CSV file was provided. Please choose the CSV file you want to upload.', 'zero-bs-crm' ) );
162        }
163
164        $csv_file_data = $_FILES['zbscrmcsvfile'];
165
166        // error uploading
167        if ( $csv_file_data['error'] !== UPLOAD_ERR_OK ) {
168            throw new Exception( __( 'There was an error processing your CSV file. Please try again.', 'zero-bs-crm' ) );
169        }
170
171        // verify file extension and MIME
172        if ( ! jpcrm_file_check_mime_extension( $csv_file_data, '.csv', array( 'text/csv', 'text/plain', 'application/csv' ) ) ) {
173            throw new Exception( __( 'Your file is not a correctly-formatted CSV file. Please check your file format. If you continue to have issues please contact support.', 'zero-bs-crm' ) );
174        }
175
176        /*
177            The main goal below is to have a file that can be read in future steps, but also that is unreadable to the public.
178
179            Things to be aware of:
180            - If we don't move/rename the file, PHP automatically deletes it at the end of the process.
181            - The hash/encryption is overkill at the moment but exists in case the destination folder is publicly available (see 2435-gh).
182            - For now, we just rename the file and leave it in the system tmp folder, but eventually we can move it to the zbscrm-store replacement.
183        */
184
185        $public_name = basename( $csv_file_data['tmp_name'] );
186
187        $hashed_filename = jpcrm_get_hashed_filename( $public_name, '.csv' );
188        $file_path       = $tmp_dir . $hashed_filename;
189
190        // try to move file to destination for future processing
191        if ( ! move_uploaded_file( $csv_file_data['tmp_name'], $file_path ) ) {
192            throw new Exception( __( 'Unable to upload CSV file.', 'zero-bs-crm' ) );
193        }
194    }
195
196    // Check stage 2 and 3
197    if ( $stage === 2 || $stage === 3 ) {
198
199        // (carefully) check for file presence
200        $public_name = ( isset( $_POST['zbscrmcsvimpf'] ) ? sanitize_file_name( $_POST['zbscrmcsvimpf'] ) : '' );
201
202        if ( empty( $public_name ) ) {
203            throw new Exception( __( 'There was an error processing your CSV file. Please try again.', 'zero-bs-crm' ) );
204        }
205
206        $hashed_filename = jpcrm_get_hashed_filename( $public_name, '.csv' );
207        $file_path       = $tmp_dir . $hashed_filename;
208
209        // Retrieve fields
210        $field_map          = array();
211        $mapped_field_count = 0;
212        for ( $fieldI = 0; $fieldI <= 30; $fieldI++ ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
213
214            // Default to ignore
215            $map_to = 'ignorezbs';
216
217            // Map :)
218            if ( ! empty( $_POST[ 'zbscrm-csv-fieldmap-' . $fieldI ] ) && $_POST[ 'zbscrm-csv-fieldmap-' . $fieldI ] !== -1 ) {
219
220                $map_to = sanitize_text_field( $_POST[ 'zbscrm-csv-fieldmap-' . $fieldI ] );
221
222                // Count actual mapped fields
223                if ( $map_to != 'ignorezbs' ) {
224                    ++$mapped_field_count;
225                }
226
227                // Pass it.
228                $field_map[ $fieldI ] = $map_to;
229
230            }
231        }
232
233        // no fields were mapped
234        if ( $mapped_field_count === 0 ) {
235            // delete the file
236            unlink( $file_path );
237            throw new Exception( __( 'No fields were mapped. You cannot import contacts without at least one field mapped to a contact attribute.', 'zero-bs-crm' ) );
238        }
239    }
240
241    // Now that we only pass the filename via POST, and we encrypt+hash it, the following few lines are probably
242    // no longer needed, but leaving for now
243    $file_path = realpath( $file_path );
244    // This ensures that the provided file exists and is inside the upload folder or one of its subdirs (ie `/wp-content/uploads/*`)
245    // and not somewhere else, also prevent traversal attacks, and usage of wrappers like phar:// etc
246    if ( $file_path === false || ! str_starts_with( $file_path, $tmp_dir ) ) {
247        // Traversal attempt, file does not exist, invalid wrapper
248        throw new Exception( __( 'There was an error processing your CSV file. Please try again.', 'zero-bs-crm' ) );
249    }
250
251    $csv_data = array();
252
253    $file = fopen( $file_path, 'r' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
254    while ( ! feof( $file ) ) {
255        // @todo Consider passing empty-string for `$escape` for better spec compatibility.
256        $csv_data[] = fgetcsv( $file, 0, ',', '"', '\\' );
257    }
258
259    fclose( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
260
261    // no lines or empty first line
262    if ( empty( $csv_data ) ) {
263        // delete the file
264        unlink( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
265        throw new Exception( __( 'We did not find any usable lines in the provided file. If you are having continued problems please contact support.', 'zero-bs-crm' ) );
266    }
267
268    // Count lines
269    $num_lines         = count( $csv_data );
270    $ignore_first_line = isset( $_POST['zbscrmcsvimpignorefirst'] );
271    if ( $ignore_first_line ) {
272        --$num_lines;
273    }
274
275    $file_details = array(
276        'public_name'       => $public_name,
277        'filename'          => $hashed_filename,
278        'file_path'         => $file_path,
279        'csv_data'          => $csv_data,
280        'num_lines'         => $num_lines,
281        'ignore_first_line' => $ignore_first_line,
282        'field_map'         => $field_map,
283    );
284
285    return $file_details;
286}
287
288// } HTML for main app
289function zeroBSCRM_CSVImporterLitehtml_app() {
290
291    global $zbsCustomerFields, $zeroBSCRM_CSVImporterLiteslugs, $zbs;// ,$zeroBSCRM_CSVImporterSettings;
292
293    // $settings = $zeroBSCRM_CSVImporterSettings->getAll();
294    $default_status    = $zbs->settings->get( 'defaultstatus' );
295    $settings          = array(
296        'savecopy'              => false,
297        'defaultcustomerstatus' => $default_status ? $default_status : __( 'Customer', 'zero-bs-crm' ),
298    );
299    $saveCopyOfCSVFile = false; // Not in LITE : ) if (isset($settings['savecopy'])) $saveCopyOfCSVFile = $settings['savecopy'];
300
301    // } 3 stages:
302    // } - Upload
303    // } - Map
304    // } - Complete (button)
305    // } - Process
306    $stage = 0;
307    if ( ! empty( $_POST['zbscrmcsvimpstage'] ) ) {
308        $stage = (int) $_POST['zbscrmcsvimpstage'];
309    }
310
311    if ( in_array( $stage, array( 1, 2, 3 ) ) ) {
312        try {
313            // check nonce and other things
314            $file_details = jpcrm_csvimporter_lite_preflight_checks( $stage );
315        } catch ( Exception $e ) {
316            // send back to beginning and show error
317            $stage      = 0;
318            $stageError = $e->getMessage();
319        }
320    }
321
322    switch ( $stage ) {
323
324        case 1:
325            // } Title
326            zeroBSCRM_CSVImporterLitepages_header( __( 'Step 2: Map Fields', 'zero-bs-crm' ) );
327
328            ?>
329            <div class="zbscrm-csvimport-wrap">
330                <h2><?php esc_html_e( 'Map columns from your CSV to contact fields', 'zero-bs-crm' ); ?></h2>
331                <?php
332                if ( isset( $stageError ) && ! empty( $stageError ) ) {
333                    zeroBSCRM_html_msg( -1, $stageError ); }
334                ?>
335                <div class="zbscrm-csv-map">
336                    <p class="zbscrm-csv-map-help"><?php esc_html_e( 'Your CSV file has been successfully uploaded. Please map your CSV columns to their corresponding CRM fields with the drop down options below.', 'zero-bs-crm' ); ?></p>
337                    <form method="post" class="zbscrm-csv-map-form">
338                        <input type="hidden" id="zbscrmcsvimpstage" name="zbscrmcsvimpstage" value="2" />
339                        <input type="hidden" id="zbscrmcsvimpf" name="zbscrmcsvimpf" value="<?php echo esc_attr( $file_details['public_name'] ); ?>" />
340                        <?php wp_nonce_field( 'zbscrm_csv_import', 'zbscrmcsvimportnonce' ); ?>
341
342                        <hr />
343                        <div class="zbscrm-csv-map-ignorefirst">
344                            <input type="checkbox" id="zbscrmcsvimpignorefirst" name="zbscrmcsvimpignorefirst" value="1" />
345                            <label for="zbscrmcsvimpignorefirst" ><?php echo esc_html__( 'Ignore first line of CSV file when running import.', 'zero-bs-crm' ) . '<br />' . esc_html__( 'Use this if you have a "header line" in your CSV file.', 'zero-bs-crm' ); ?></label>
346                        </div>
347                        <hr />
348
349                        <?php
350                        // print_r($fileDetails);
351
352                            // } Cycle through each field and display a mapping option
353                            // } Using first line of import
354                            $first_line_parts = $file_details['csv_data'][0];
355
356                            // } Retrieve possible map fields from fields model
357                            $possibleFields = array();
358                        foreach ( $zbsCustomerFields as $fieldKey => $fieldDeets ) {
359
360                            // not custom-fields
361                            if ( ! isset( $fieldDeets['custom-field'] ) ) {
362                                $possibleFields[ $fieldKey ] = __( $fieldDeets[1], 'zero-bs-crm' );
363                            }
364
365                            if ( in_array( $fieldKey, array( 'secaddr1', 'secaddr2', 'seccity', 'seccounty', 'seccountry', 'secpostcode' ) ) ) {
366                                $possibleFields[ $fieldKey ] .= ' (' . __( '2nd Address', 'zero-bs-crm' ) . ')';
367                            }
368                        }
369
370                            // } Loop
371                            $indx = 1;
372                        foreach ( $first_line_parts as $userField ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
373
374                            // } Clean user field - ""
375                            if ( str_starts_with( $userField, '"' ) && str_ends_with( $userField, '"' ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
376                                $userField = substr( $userField, 1, strlen( $userField ) - 2 );
377                            }
378                            // } Clean user field - ''
379                            if ( str_starts_with( $userField, "'" ) && str_ends_with( $userField, "'" ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
380                                $userField = substr( $userField, 1, strlen( $userField ) - 2 );
381                            }
382
383                            ?>
384                                    <div class="zbscrm-csv-map-field">
385                                        <span><?php echo esc_html_x( 'Map', 'As in map CSV column to field', 'zero-bs-crm' ); ?>:</span> <div class="zbscrm-csv-map-user-field">"<?php echo esc_html( $userField ); ?>"</div><br />
386                                        <div class="zbscrm-csv-map-zbs-field">
387                                            <span class="to"><?php esc_html_e( 'To:', 'zero-bs-crm' ); ?></span> <select name="zbscrm-csv-fieldmap-<?php echo esc_attr( $indx ); ?>" id="zbscrm-csv-fieldmap-<?php echo esc_attr( $indx ); ?>">
388                                                <option value="-1" disabled="disabled"><?php esc_html_e( 'Select a field', 'zero-bs-crm' ); ?></option>
389                                                <option value="-1" disabled="disabled">==============</option>
390                                                <option value="ignorezbs" selected="selected"><?php esc_html_e( 'Ignore this field', 'zero-bs-crm' ); ?></option>
391                                                <option value="-1" disabled="disabled">==============</option>
392                                        <?php foreach ( $possibleFields as $fieldID => $fieldTitle ) { ?>
393                                                <option value="<?php echo esc_attr( $fieldID ); ?>"><?php esc_html_e( $fieldTitle, 'zero-bs-crm' ); ?></option>
394                                            <?php } ?>
395                                            </select>
396                                        </div>
397                                    </div>
398                                <?php
399
400                                ++$indx;
401
402                        }
403
404                        ?>
405                            <hr />
406                        <div style="text-align:center">
407                            <button type="submit" name="csv-map-submit" id="csv-map-submit" class="ui button button-primary button-large green" type="submit"><?php esc_html_e( 'Continue', 'zero-bs-crm' ); ?></button>    
408                        </div>
409                    </form>
410                </div>
411            </div>
412            <?php
413
414            break;
415        case 2:
416            // Title
417            zeroBSCRM_CSVImporterLitepages_header( __( 'Step 3: Run Import', 'zero-bs-crm' ) );
418
419            // Stolen from plugin-install.php?tab=upload
420            ?>
421            <div class="zbscrm-csvimport-wrap">
422                <h2>Verify field mapping</h2>
423                <?php
424                if ( isset( $stageError ) && ! empty( $stageError ) ) {
425                    zeroBSCRM_html_msg( -1, $stageError ); }
426                ?>
427                <div class="zbscrm-confirmimport-csv">
428                    <div>
429                        <?php
430                        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
431                        zeroBSCRM_html_msg( 1, esc_html__( 'Note: There is no automatic way to undo a CSV import. To remove any contacts that have been added you will need to manually remove them.', 'zero-bs-crm' ) );
432                        ?>
433                    <form method="post" enctype="multipart/form-data" class="zbscrm-csv-import-form">
434                        <input type="hidden" id="zbscrmcsvimpstage" name="zbscrmcsvimpstage" value="3" />
435                        <input type="hidden" id="zbscrmcsvimpf" name="zbscrmcsvimpf" value="<?php echo esc_attr( $file_details['public_name'] ); ?>" />
436                        <?php wp_nonce_field( 'zbscrm_csv_import', 'zbscrmcsvimportnonce' ); ?>
437                        <h3>Import <?php echo esc_html( zeroBSCRM_prettifyLongInts( $file_details['num_lines'] ) ); ?> Contacts</h3>
438                        <hr />
439                        <?php if ( $file_details['ignore_first_line'] ) { ?>
440                            <p style="font-size:16px;text-align:center;">Ignore first line of CSV <i class="fa fa-check"></i></p>
441                            <hr />
442                            <input type="hidden" id="zbscrmcsvimpignorefirst" name="zbscrmcsvimpignorefirst" value="1" />
443                        <?php } ?>
444                        <p style="font-size:16px;text-align:center;">Map the following fields:</p>
445                        <?php
446
447                        // Cycle through each field
448                        // Using first line of import
449                        $first_line_parts = $file_details['csv_data'][0];
450
451                        foreach ( $file_details['field_map'] as $fieldID => $fieldTarget ) {
452
453                            $fieldTargetName = $fieldTarget;
454                            if ( isset( $zbsCustomerFields[ $fieldTarget ] ) && isset( $zbsCustomerFields[ $fieldTarget ][1] ) && ! empty( $zbsCustomerFields[ $fieldTarget ][1] ) ) {
455                                $fieldTargetName = __( $zbsCustomerFields[ $fieldTarget ][1], 'zero-bs-crm' );
456                            }
457
458                            if ( in_array( $fieldTarget, array( 'secaddr1', 'secaddr2', 'seccity', 'seccounty', 'seccountry', 'secpostcode' ) ) ) {
459                                $fieldTargetName .= ' (' . __( '2nd Address', 'zero-bs-crm' ) . ')';
460                            }
461
462                            $fromStr = '';
463                            if ( isset( $first_line_parts[ $fieldID - 1 ] ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
464                                $fromStr = $first_line_parts[ $fieldID - 1 ]; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
465                            }
466
467                            // Clean user field - ""
468                            if ( str_starts_with( $fromStr, '"' ) && str_ends_with( $fromStr, '"' ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
469                                $fromStr = substr( $fromStr, 1, strlen( $fromStr ) - 2 );
470                            }
471                            // Clean user field - ''
472                            if ( str_starts_with( $fromStr, "'" ) && str_ends_with( $fromStr, "'" ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
473                                $fromStr = substr( $fromStr, 1, strlen( $fromStr ) - 2 );
474                            }
475
476                            ?>
477                                <input type="hidden" id="zbscrm-csv-fieldmap-<?php echo esc_attr( $fieldID - 1 ); ?>" name="zbscrm-csv-fieldmap-<?php echo esc_attr( $fieldID - 1 ); ?>" value="<?php echo esc_attr( $fieldTarget ); ?>" />
478                                <div class="zbscrm-impcsv-map">
479                                    <div class="zbscrm-impcsv-from">
480                                    <?php
481                                    if ( ! empty( $fromStr ) ) {
482                                        echo '"' . esc_html( $fromStr ) . '"';
483                                    } else {
484                                        echo esc_html( sprintf( __( 'Field #%s', 'zero-bs-crm' ), $fieldID ) );
485                                    }
486                                    ?>
487                                    </div>
488                                    <div class="zbscrm-impcsv-arrow">
489                                    <?php
490                                    if ( $fieldTarget != 'ignorezbs' ) {
491                                        echo '<i class="fa fa-long-arrow-right"></i>';
492                                    } else {
493                                        echo '-';
494                                    }
495                                    ?>
496                                    </div>
497                                    <div class="zbscrm-impcsv-to">
498                                    <?php
499                                    if ( $fieldTarget != 'ignorezbs' ) {
500                                        echo '"' . esc_html( $fieldTargetName ) . '"';
501                                    } else {
502                                        esc_html_e( 'Ignore', 'zero-bs-crm' );
503                                    }
504                                    ?>
505                                    </div>
506                                </div>
507                            <?php
508
509                        }
510
511                        ?>
512                        <hr />
513                        <div style="text-align:center">
514                            <button type="submit" name="csv-map-submit" id="csv-map-submit" class="ui button button-primary button-large green" type="submit"><?php esc_html_e( 'Run import', 'zero-bs-crm' ); ?></button>    
515                        </div>
516                    </form>
517                </div>
518            </div>
519            <?php
520
521            break;
522
523        case 3:
524            // } Title
525            zeroBSCRM_CSVImporterLitepages_header( __( 'Step 4: Import', 'zero-bs-crm' ) );
526
527            ?>
528            <div class="zbscrm-csvimport-wrap">
529                <h2 id="jpcrm_final_step_heading"><?php esc_html_e( 'Running import...', 'zero-bs-crm' ); ?></h2>
530                <?php
531                if ( isset( $stageError ) && ! empty( $stageError ) ) {
532                    zeroBSCRM_html_msg( -1, $stageError ); }
533                ?>
534                <div class="zbscrm-final-stage" style="text-align: center;">
535                    <p>New contacts added: <span id="jpcrm_new_contact_count">0</span></p>
536                    <p>Existing contacts updated: <span id="jpcrm_update_contact_count">0</span></p>
537                    <button id="jpcrm_toggle_log_button" class="ui button grey"><?php esc_html_e( 'Toggle log', 'zero-bs-crm' ); ?></button>
538                    <a id="jpcrm_import_finish_button" href="<?php echo jpcrm_esc_link( $zbs->slugs['managecontacts'] ); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */ ?>" class="ui button green hidden"><?php esc_html_e( 'Finish', 'zero-bs-crm' ); ?></a>
539                </div>
540                    <div id="jpcrm_import_log_div" class="zbscrm-import-log hidden">
541                        <div class="zbscrm-import-log-line"><?php esc_html_e( 'Loading CSV File...', 'zero-bs-crm' ); ?> <i class="fa fa-check"></i></div>
542                        <div class="zbscrm-import-log-line"><?php esc_html_e( 'Parsing rows...', 'zero-bs-crm' ); ?> <i class="fa fa-check"></i></div>
543                        <div class="zbscrm-import-log-line"><?php echo esc_html( sprintf( __( 'Beginning Import of %s rows...', 'zero-bs-crm' ), zeroBSCRM_prettifyLongInts( $file_details['num_lines'] ) ) ); ?></div>
544                        <?php
545
546                            // } Cycle through
547                            $lineIndx       = 0;
548                        $linesAdded         = 0;
549                        $existingOverwrites = array();
550                        $brStrs             = array( '<br>', '<BR>', '<br />', '<BR />', '<br/>', '<BR/>' );
551                        foreach ( $file_details['csv_data'] as $lineParts ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
552
553                            // } Check line
554                            if ( $lineIndx === 0 && $file_details['ignore_first_line'] ) {
555
556                                echo '<div class="zbscrm-import-log-line">' . esc_html__( 'Skipping header row...', 'zero-bs-crm' ) . '<i class="fa fa-check"></i></div>';
557
558                            } else {
559
560                                // } build arr
561                                $customerFields = array();
562                                // } Catch first if there
563
564                                foreach ( $file_details['field_map'] as $fieldID => $fieldTarget ) {
565
566                                    // } id
567                                    $fieldIndx = $fieldID;
568
569                                    // } Anything to set?
570                                    if (
571
572                                            // data in line
573                                            isset( $lineParts[ $fieldIndx ] ) && ! empty( $lineParts[ $fieldIndx ] ) &&
574
575                                            // isn't ignore
576                                            $fieldTarget != 'ignorezbs'
577
578                                        ) {
579
580                                        // for <br> passes, we convert them to nl
581                                        $cleanUserField = str_replace( $brStrs, "\r\n", $lineParts[ $fieldIndx ] );
582
583                                        $cleanUserField = trim( $cleanUserField );
584
585                                        if ( $cleanUserField == 'NULL' ) {
586                                            $cleanUserField = '';
587                                        }
588
589                                        $cleanUserField = sanitize_text_field( $cleanUserField ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
590
591                                        // } set customer fields
592                                        $customerFields[ 'zbsc_' . $fieldTarget ] = $cleanUserField;
593
594                                    }
595                                }
596
597                                // } Any legit fields?
598                                if ( count( $customerFields ) > 0 ) {
599
600                                    // } Try and find a unique id for this user
601                                    // adjusted for backward-compatibility, but this should be rewritten
602                                    $userUniqueID = md5( implode( ',', $lineParts ) . '#' . $file_details['public_name'] ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
603
604                                        // } 1st use email if there
605                                    if ( isset( $customerFields['zbsc_email'] ) && ! empty( $customerFields['zbsc_email'] ) ) {
606                                        $userUniqueID = $customerFields['zbsc_email'];
607                                    }
608
609                                        // } else use md5 of the line + Filename
610
611                                    // } If no STATUS have to add one!
612                                    $status_override_value = null;
613                                    if ( ! isset( $customerFields['zbsc_status'] ) ) {
614
615                                        // } Get from setting, if present
616                                        if ( isset( $settings['defaultcustomerstatus'] ) && ! empty( $settings['defaultcustomerstatus'] ) ) {
617                                            $status_override_value = $settings['defaultcustomerstatus'];
618                                        } else {
619                                            $status_override_value = 'Contact';
620                                        }
621                                    }
622
623                                    // } Already exists? (This is only used to find dupes
624                                    $potentialCustomerID = zeroBS_getCustomerIDWithExternalSource( 'csv', $userUniqueID );
625                                    if ( ! empty( $potentialCustomerID ) && $potentialCustomerID > 0 ) {
626
627                                        $thisDupeRef = '#' . $potentialCustomerID;
628                                        if ( isset( $customerFields['zbsc_email'] ) && ! empty( $customerFields['zbsc_email'] ) ) {
629                                            $thisDupeRef .= ' (' . $customerFields['zbsc_email'] . ')';
630                                        }
631
632                                        $existingOverwrites[] = $thisDupeRef;
633                                    }
634
635                                    if ( ! empty( $potentialCustomerID ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
636                                        // We could modify `zeroBS_integrations_addOrUpdateCustomer`
637                                        // to touch only on the fields we are passing to the function,
638                                        // but that function is used in other places and this could
639                                        // result in unwanted side effects.
640                                        // Instead we are passing all original fields
641                                        // to the function, and overriding only the ones
642                                        // we want.
643                                        $original_contact = $zbs->DAL->contacts->getContact( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
644                                            $potentialCustomerID, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
645                                            array(
646                                                'withCustomFields' => true,
647                                                'ignoreowner' => true,
648                                            )
649                                        );
650                                        foreach ( $original_contact as $original_key => $original_value ) {
651                                            // We need to prefix all fields coming from the above function, because
652                                            // `zeroBS_integrations_addOrUpdateCustomer` expects the fields to be prefixed
653                                            // (this is an older function).
654                                            $original_contact[ 'zbsc_' . $original_key ] = $original_value;
655                                            unset( $original_contact[ $original_key ] );
656                                        }
657                                        $customerFields = array_merge( $original_contact, $customerFields ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
658                                    } else {
659                                        // We should override the status only when adding a new contact.
660                                        $customerFields['zbsc_status'] = ! empty( $status_override_value ) ? $status_override_value : $customerFields['zbsc_status']; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
661                                    }
662
663                                    // } Add customer
664                                    $newCustID = zeroBS_integrations_addOrUpdateCustomer( 'csv', $userUniqueID, $customerFields );
665
666                                    if ( ! empty( $newCustID ) && empty( $potentialCustomerID ) ) {
667
668                                        ++$linesAdded;
669
670                                        // } Line
671                                        echo '<div class="zbscrm-import-log-line">' .
672                                                                                                    sprintf(
673                                                                                                        __( 'Successfully added contact #<a href="%1$s" target="_blank">%2$d</a>... <i class="fa fa-user"></i><span>+1</span>', 'zero-bs-crm' ),
674                                                                                                        jpcrm_esc_link( 'edit', $newCustID, 'contact', false, false ),
675                                                                                                        esc_html( $newCustID )
676                                                                                                    )
677                                                                                                . '</div>';
678
679                                    } else {
680
681                                        // dupe overriten?
682                                        if ( ! empty( $potentialCustomerID ) ) {
683
684                                            // } Line
685                                            echo '<div class="zbscrm-import-log-line">' . esc_html__( 'Contact Already Exists!:', 'zero-bs-crm' ) . ' #' . esc_html( $newCustID ) . '... <i class="fa fa-user"></i><span>[' . esc_html__( 'Updated', 'zero-bs-crm' ) . ']</span></div>';
686
687                                        }
688                                    }
689                                } else {
690
691                                    echo '<div class="zbscrm-import-log-line">' . esc_html__( 'Skipping row (no usable fields)', 'zero-bs-crm' ) . '... <i class="fa fa-check"></i></div>';
692
693                                }
694                            }
695
696                            ++$lineIndx;
697
698                        }
699
700                            // any of these?
701                        if ( count( $existingOverwrites ) > 0 ) {
702
703                            echo '<div class="zbscrm-import-log-line"><strong>' . esc_html__( 'The following contacts were already in your Jetpack CRM, and were updated:', 'zero-bs-crm' ) . '</strong></div>';
704
705                            foreach ( $existingOverwrites as $l ) {
706
707                                echo '<div class="zbscrm-import-log-line">' . $l . '</div>';
708                            }
709                        }
710
711                        if ( $file_details['file_path'] ) {
712                            unlink( $file_details['file_path'] );
713                        }
714                            echo '<div class="zbscrm-import-log-line">' . esc_html__( 'CSV Upload File Deleted...', 'zero-bs-crm' ) . '<i class="fa fa-check"></i></div>';
715
716                        ?>
717                        <hr />
718                    </div>
719            </div>
720            <script>
721                // these are some quick hacks for better usability until the importer rewrite
722
723                function jpcrm_toggle_csv_log() {
724                    document.getElementById('jpcrm_import_log_div').classList.toggle('hidden');
725                }
726
727                document.getElementById('jpcrm_toggle_log_button').addEventListener('click',jpcrm_toggle_csv_log);
728                document.getElementById('jpcrm_final_step_heading').innerHTML = '<?php esc_html_e( 'Import complete!', 'zero-bs-crm' ); ?>';
729                document.getElementById('jpcrm_new_contact_count').innerHTML = <?php echo esc_html( $linesAdded ); /* phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase */ ?>;
730                document.getElementById('jpcrm_update_contact_count').innerHTML = <?php echo esc_html( count( $existingOverwrites ) ); /* phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase */ ?>;
731                document.getElementById('jpcrm_import_finish_button').classList.remove('hidden');
732            </script>
733            <?php
734
735            break;
736        default: // } Also case 0
737            // } Title
738            zeroBSCRM_CSVImporterLitepages_header( __( 'Step 1: Upload', 'zero-bs-crm' ) );
739
740            // } Stolen from plugin-install.php?tab=upload
741            ?>
742            <div class="zbscrm-csvimport-wrap">
743                <h2><?php esc_html_e( 'Import contacts from a CSV file', 'zero-bs-crm' ); ?></h2>
744                <?php
745                if ( isset( $stageError ) && ! empty( $stageError ) ) {
746                    zeroBSCRM_html_msg( -1, $stageError ); }
747                ?>
748                <div class="zbscrm-upload-csv">
749                    <p class="zbscrm-csv-import-help"><?php esc_html_e( 'If you have a CSV file of contacts that you would like to import into Jetpack CRM, you can start the import wizard by uploading your .CSV file here.', 'zero-bs-crm' ); ?></p>
750                    <form method="post" enctype="multipart/form-data" class="zbscrm-csv-import-form">
751                        <input type="hidden" id="zbscrmcsvimpstage" name="zbscrmcsvimpstage" value="1" />
752                        <?php wp_nonce_field( 'zbscrm_csv_import', 'zbscrmcsvimportnonce' ); ?>
753                        <label class="screen-reader-text" for="zbscrmcsvfile"><?php esc_html_e( '.CSV file', 'zero-bs-crm' ); ?></label>
754                        <input type="file" id="zbscrmcsvfile" name="zbscrmcsvfile">
755                        <div class="csv-import__start-btn">
756                            <input type="submit" name="csv-file-submit" id="csv-file-submit" class="ui button black" value="<?php esc_attr_e( 'Upload CSV file', 'zero-bs-crm' ); ?>">
757                        </div>
758                    </form>
759                </div>
760            </div>
761            <?php
762
763            // } Lite upsell (remove from rebrander) but also make it translation OK.
764            ##WLREMOVE
765
766                // WH added: Is now polite to License-key based settings like 'entrepreneur' doesn't try and upsell
767                // this might be a bit easy to "hack out" hmmmm
768                $bundle = false;
769            if ( $zbs->hasEntrepreneurBundleMin() ) {
770                $bundle = true;
771            }
772
773            if ( ! $bundle ) {
774                ?>
775                    <hr style="margin-top:40px" />
776                    <div class="zbscrm-lite-notice">
777                        <h2><?php esc_html_e( 'CSV Importer: Lite Version', 'zero-bs-crm' ); ?></h2>
778                        <p><?php echo wp_kses( sprintf( __( 'If you would like to benefit from more features (such as logging your imports, automatically creating companies (B2B), and direct support) then please purchase a copy of our <a href="%s" target="_blank">CSV Importer PRO</a> extension.', 'zero-bs-crm' ), esc_url( $zbs->urls['extcsvimporterpro'] ) ), $zbs->acceptable_restricted_html ); ?><br /><br /><a href="<?php echo esc_url( $zbs->urls['extcsvimporterpro'] ); ?>" target="_blank" class="ui button blue large"><?php esc_html_e( 'Get CSV Importer PRO', 'zero-bs-crm' ); ?></a></p>
779
780                    </div>
781                    <?php
782
783            } else {
784
785                // has bundle should download + install
786                ?>
787                    <hr style="margin-top:40px" />
788                    <div class="zbscrm-lite-notice">
789                        <h2><?php esc_html_e( 'CSV Importer: Lite Version', 'zero-bs-crm' ); ?></h2>
790                        <p><?php echo wp_kses( sprintf( __( 'You have the PRO version of CSV importer available as part of your bundle. Please download and install from <a href="%s" target="_blank">your account</a>.', 'zero-bs-crm' ), esc_url( $zbs->urls['account'] ) ), $zbs->acceptable_restricted_html ); ?></p>
791                    </div>
792                    <?php
793            }
794            ##/WLREMOVE
795
796            break;
797
798    }
799}