Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 328 |
|
0.00% |
0 / 7 |
CRAP | n/a |
0 / 0 |
|
| zeroBSCRM_CSVImporterLiteadmin_menu | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBSCRM_CSVImporter_lite_admin_styles | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBSCRM_CSVImporterLitepages_header | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
| zeroBSCRM_CSVImporterLitepages_footer | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| zeroBSCRM_CSVImporterLitepages_app | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
| jpcrm_csvimporter_lite_preflight_checks | |
0.00% |
0 / 62 |
|
0.00% |
0 / 1 |
552 | |||
| zeroBSCRM_CSVImporterLitehtml_app | |
0.00% |
0 / 235 |
|
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 | ====================================================== */ |
| 16 | if ( ! 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.) |
| 29 | global $zeroBSCRM_extensionsInstalledList; |
| 30 | if (!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.) |
| 33 | function 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 | /* |
| 49 | No settings included in CSV Importer LITE - pro only :) |
| 50 | // } If legit... #CORELOADORDER |
| 51 | if (!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 | /* |
| 66 | function 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 |
| 77 | function 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 | } |
| 86 | add_action( 'zerobs_admin_menu', 'zeroBSCRM_CSVImporterLiteadmin_menu' ); |
| 87 | |
| 88 | function zeroBSCRM_CSVImporter_lite_admin_styles() { |
| 89 | wp_enqueue_style( 'zerobscrm-csvimporter-admcss' ); |
| 90 | } |
| 91 | |
| 92 | // ================== Admin Pages |
| 93 | |
| 94 | // } Admin Page header |
| 95 | function 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 |
| 116 | function zeroBSCRM_CSVImporterLitepages_footer() { |
| 117 | |
| 118 | ?> |
| 119 | </div> |
| 120 | <?php |
| 121 | } |
| 122 | |
| 123 | // } Main Uploader Page |
| 124 | function 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 |
| 143 | function 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 |
| 289 | function 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 | } |