PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
mainwp / modules / cost-tracker / pages / page-cost-tracker-import.php

page-cost-tracker-import.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.2.1, at modules/cost-tracker/pages/page-cost-tracker-import.php

382 lines 16.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP Module Cost Tracker Import class.
4 *
5 * @package MainWP\Dashboard
6 * @version 4.6
7 */
8
9 namespace MainWP\Dashboard\Module\CostTracker;
10
11 use MainWP\Dashboard\MainWP_UI;
12 use MainWP\Dashboard\MainWP_System_Utility;
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Class Cost_Tracker_Import
21 */
22 class Cost_Tracker_Import {
23
24 /**
25 * Static variable to hold the single instance of the class.
26 *
27 * @static
28 *
29 * @var mixed Default null
30 */
31 private static $instance = null;
32
33 /**
34 * Get Instance
35 *
36 * Creates public static instance.
37 *
38 * @static
39 *
40 * @return Cost_Tracker_Import
41 */
42 public static function get_instance() {
43 if ( null === static::$instance ) {
44 static::$instance = new self();
45 }
46 return static::$instance;
47 }
48
49 /**
50 * Constructor
51 *
52 * Runs each time the class is called.
53 */
54 public function __construct() {
55 }
56
57 /**
58 * Renders the Import Cost form.
59 */
60 public static function render_import_costs() {
61 if ( ! \mainwp_current_user_can( 'dashboard', 'manage_cost_tracker' ) ) {
62 \mainwp_do_not_have_permissions( esc_html__( 'manage cost tracker', 'mainwp' ) );
63 return;
64 }
65
66 Cost_Tracker_Admin::render_header( 'ImportCosts' );
67
68 $title_page = esc_html__( 'Import Operational Costs', 'mainwp' );
69
70 $has_import_data = ! empty( $_POST['mainwp_cost_tracker_import_add'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
71 if ( $has_import_data && check_admin_referer( 'mainwp-admin-nonce' ) ) {
72 static::render_import_cost_modal();
73 }
74 ?>
75 <div class="ui padded segment" id="mainwp-import-costs">
76 <form method="POST" action="" enctype="multipart/form-data" id="mainwp_cost_tracker_import_form" class="ui form">
77 <div>
78 <?php $el_id_mes_zn_1 = 'mainwp-message-zone'; ?>
79 <div id="<?php echo esc_attr( $el_id_mes_zn_1 ); ?>" class="ui message" style="display:none"></div>
80 <h3 class="ui dividing header">
81 <?php echo esc_html( $title_page ); ?>
82 <div class="sub header"><?php esc_html_e( 'Import multiple costs to your Cost Tracker.', 'mainwp' ); ?></div>
83 </h3>
84 <?php MainWP_UI::generate_wp_nonce( 'mainwp-admin-nonce' ); ?>
85 <div class="ui grid field">
86 <label class="three wide column middle aligned" for="mainwp_cost_tracker_import_file_bulkupload"><?php esc_html_e( 'Upload CSV', 'mainwp' ); ?> (<a href="<?php echo esc_url( MAINWP_PLUGIN_URL . 'assets/csv/sample_costs.csv' ); ?>" target="_blank"><?php esc_html_e( 'Download Sample', 'mainwp' ); ?></a>)</label>
87 <div class="nine wide column">
88 <div class="ui file input">
89 <input type="file" name="mainwp_cost_tracker_import_file_bulkupload" id="mainwp_cost_tracker_import_file_bulkupload" accept="text/comma-separated-values"/>
90 </div>
91 </div>
92 <div class="ui toggle checkbox four wide column middle aligned">
93 <input type="checkbox" name="mainwp_cost_tracker_import_chk_header_first" checked="checked" id="mainwp_cost_tracker_import_chk_header_first" value="1"/>
94 <label for="mainwp_cost_tracker_import_chk_header_first"><?php esc_html_e( 'CSV file contains a header', 'mainwp' ); ?></label>
95 </div>
96 </div>
97 </div>
98 <div class="ui segment">
99 <div class="ui divider"></div>
100 <input type="submit" name="mainwp_cost_tracker_import_add" id="mainwp_cost_tracker_import_bulkadd" class="ui big green disabled button" value="<?php echo esc_attr( $title_page ); ?>"/>
101 </div>
102 </form>
103 <script type="text/javascript">
104 jQuery( document ).ready( function() {
105 jQuery( '#mainwp_cost_tracker_import_file_bulkupload' ).on( 'change', function() {
106 const messageZone = jQuery( '#<?php echo esc_js( $el_id_mes_zn_1 ); ?>' );
107 messageZone.hide().removeClass( 'red' ).html( '' );
108
109 if ( this.files.length > 0 ) {
110 const fileName = this.files[0].name;
111 const fileExtension = fileName.split( '.' ).pop().toLowerCase();
112
113 if ( fileExtension !== 'csv' ) {
114 messageZone.addClass( 'red' ).html( '<?php esc_html_e( 'Please select a valid CSV file.', 'mainwp' ); ?>' ).show();
115 jQuery( '#mainwp_cost_tracker_import_bulkadd' ).addClass( 'disabled' );
116 this.value = '';
117 return;
118 }
119
120 jQuery( '#mainwp_cost_tracker_import_bulkadd' ).removeClass( 'disabled' );
121 } else {
122 jQuery( '#mainwp_cost_tracker_import_bulkadd' ).addClass( 'disabled' );
123 }
124 } );
125 } );
126 </script>
127 </div>
128 <?php
129 }
130
131 /**
132 * Method render_import_cost_modal()
133 *
134 * Render HTML import cost modal.
135 */
136 public static function render_import_cost_modal() {
137 ?>
138 <div class="ui large modal mainwp-qsw-import-cost-modal" id="mainwp-import-cost-modal">
139 <i class="close icon"></i>
140 <div class="header"><?php esc_html_e( 'Import Operational Costs', 'mainwp' ); ?></div>
141 <div class="scrolling content">
142 <?php static::render_import_cost_row_modal(); ?>
143 </div>
144 <div class="actions">
145 <div class="ui two column grid">
146 <div class="left aligned middle aligned column">
147 <input type="button" name="mainwp_managecosts_btn_import" id="mainwp_managecosts_btn_import" class="ui basic button" value="<?php esc_attr_e( 'Pause', 'mainwp' ); ?>"/>
148 <input type="button" name="mainwp-import-costs-modal-try-again" id="mainwp-import-costs-modal-try-again" class="ui basic button" value="<?php esc_attr_e( 'Try Again', 'mainwp' ); ?>" style="display:none;"/>
149 </div>
150 </div>
151 </div>
152 </div>
153 <script type="text/javascript">
154 jQuery( document ).ready( function () {
155 jQuery( "#mainwp-import-cost-modal" ).modal( {
156 closable: false,
157 onHide: function() {
158 location.reload();
159 }
160 } ).modal( 'show' );
161 } );
162 </script>
163 <?php
164 }
165
166 /**
167 * Method render_import_cost_row_modal()
168 *
169 * Render HTML import cost row modal.
170 */
171 public static function render_import_cost_row_modal() {
172 ?>
173 <div id="mainwp-importing-costs" class="ui active dimmer">
174 <div class="ui double text loader"><?php esc_html_e( 'Importing...', 'mainwp' ); ?></div>
175 </div>
176 <div class="ui message" id="mainwp-import-costs-status-message">
177 <i class="notched circle loading icon"></i> <?php echo esc_html__( 'Importing...', 'mainwp' ); ?>
178 </div>
179 <?php
180 $has_file_upload = isset( $_FILES['mainwp_cost_tracker_import_file_bulkupload'] ) && isset( $_FILES['mainwp_cost_tracker_import_file_bulkupload']['error'] ) && UPLOAD_ERR_OK === $_FILES['mainwp_cost_tracker_import_file_bulkupload']['error']; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified earlier in the request.
181
182 if ( ! $has_file_upload ) {
183 ?>
184 <div class="ui red message">
185 <?php esc_html_e( 'No file was uploaded or there was an error uploading the file. Please try again.', 'mainwp' ); ?>
186 </div>
187 <script type="text/javascript">
188 jQuery(document).ready(function(){
189 jQuery('#mainwp-importing-costs').hide();
190 jQuery('#mainwp-import-costs-status-message').hide();
191 jQuery('#mainwp_managecosts_btn_import').hide();
192 jQuery('#mainwp-import-costs-modal-try-again').show();
193 });
194 </script>
195 <?php
196 return;
197 }
198
199 $uploaded_filename = isset( $_FILES['mainwp_cost_tracker_import_file_bulkupload']['name'] ) ? sanitize_file_name( $_FILES['mainwp_cost_tracker_import_file_bulkupload']['name'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- File upload field.
200
201 $file_extension = strtolower( pathinfo( $uploaded_filename, PATHINFO_EXTENSION ) );
202
203 if ( 'csv' !== $file_extension ) {
204 ?>
205 <div class="ui red message">
206 <?php esc_html_e( 'Invalid file type. Please upload a CSV file.', 'mainwp' ); ?>
207 </div>
208 <script type="text/javascript">
209 jQuery(document).ready(function(){
210 jQuery('#mainwp-importing-costs').hide();
211 jQuery('#mainwp-import-costs-status-message').hide();
212 jQuery('#mainwp_managecosts_btn_import').hide();
213 jQuery('#mainwp-import-costs-modal-try-again').show();
214 });
215 </script>
216 <?php
217 return;
218 }
219
220 $import_cost_data = static::handle_cost_import_files();
221
222 if ( empty( $import_cost_data ) || ! is_array( $import_cost_data ) || empty( $import_cost_data['data'] ) ) {
223 ?>
224 <div class="ui red message">
225 <?php esc_html_e( 'No valid cost data found in the uploaded CSV file. Please check the file format and ensure it contains valid cost entries.', 'mainwp' ); ?>
226 </div>
227 <script type="text/javascript">
228 jQuery(document).ready(function(){
229 jQuery('#mainwp-importing-costs').hide();
230 jQuery('#mainwp-import-costs-status-message').hide();
231 jQuery('#mainwp_managecosts_btn_import').hide();
232 jQuery('#mainwp-import-costs-modal-try-again').show();
233 });
234 </script>
235 <?php
236 return;
237 }
238
239 $row = 0;
240 $header_line = trim( $import_cost_data['header_line'] );
241 foreach ( $import_cost_data['data'] as $val_cost_data ) {
242 $encoded = wp_json_encode( $val_cost_data );
243 $original = implode(
244 ', ',
245 array_map(
246 function ( $item ) {
247 return is_array( $item ) ? implode( ';', $item ) : $item;
248 },
249 $val_cost_data
250 )
251 );
252 ?>
253 <input type="hidden" id="mainwp_managecosts_import_csv_line_<?php echo esc_attr( $row + 1 ); ?>" value="" encoded-data="<?php echo esc_attr( $encoded ); ?>" original="<?php echo esc_attr( $original ); ?>"/>
254 <?php
255 ++$row;
256 }
257 ?>
258 <input type="hidden" id="mainwp_managecosts_do_import" value="1"/>
259 <input type="hidden" id="mainwp_managecosts_total_import" value="<?php echo esc_attr( $row ); ?>"/>
260 <div class="mainwp_cost_tracker_import_listing" id="mainwp_cost_tracker_import_logging">
261 <span class="log ui medium text"><?php echo esc_html( $header_line ) . '<br/>'; ?></span>
262 </div>
263 <div class="mainwp_cost_tracker_import_listing" id="mainwp_cost_tracker_import_fail_logging" style="display: none;"><?php echo esc_html( $header_line ); ?> </div>
264 <?php
265 }
266
267 /**
268 * Handle cost import files.
269 *
270 * Parses uploaded CSV file and returns structured cost data.
271 *
272 * @return array Array containing 'header_line' and 'data' keys.
273 */
274 public static function handle_cost_import_files() { // phpcs:ignore -- NOSONAR - complex method.
275
276 if ( isset( $_FILES['mainwp_cost_tracker_import_file_bulkupload']['error'] ) && UPLOAD_ERR_OK !== $_FILES['mainwp_cost_tracker_import_file_bulkupload']['error'] ) { // phpcs:ignore WordPress.Security.NonceVerification
277 return array();
278 }
279
280 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified earlier in the request.
281 $tmp_path = isset( $_FILES['mainwp_cost_tracker_import_file_bulkupload']['tmp_name'] ) ? $_FILES['mainwp_cost_tracker_import_file_bulkupload']['tmp_name'] : ''; // phpcs:ignore WordPress.Security.NonceVerification,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
282 if ( empty( $tmp_path ) || ! is_uploaded_file( $tmp_path ) ) {
283 return array();
284 }
285
286 $hasWPFileSystem = MainWP_System_Utility::get_wp_file_system();
287
288 /**
289 * WordPress files system object.
290 *
291 * @global object
292 */
293 global $wp_filesystem;
294
295 if ( ! $hasWPFileSystem || empty( $wp_filesystem ) ) {
296 return array();
297 }
298
299 $content = $wp_filesystem->get_contents( $tmp_path );
300
301 if ( false === $content ) {
302 return array();
303 }
304
305 $header_line = null;
306 $lines = array();
307
308 if ( is_string( $content ) ) {
309 $content = str_replace( "\r\n", "\r", $content );
310 $content = str_replace( "\n", "\r", $content );
311 $lines = explode( "\r", $content );
312 }
313
314 $import_data = array();
315 $default_values = array(
316 'cost.name' => '',
317 'cost.url' => '',
318 'cost.type' => '',
319 'cost.product_type' => '',
320 'cost.license_type' => '',
321 'cost.price' => '',
322 'cost.payment_method' => '',
323 'cost.renewal_type' => '',
324 'cost.last_renewal' => '',
325 'cost.cost_status' => '',
326 'cost.select_sites' => '',
327 );
328
329 if ( is_array( $lines ) && ( ! empty( $lines ) ) ) {
330 foreach ( $lines as $original_line ) {
331 $line = trim( $original_line );
332 if ( \MainWP\Dashboard\MainWP_Utility::starts_with( $line, '#' ) ) {
333 continue;
334 }
335
336 $items = str_getcsv( $line, ',', '"', '' );
337
338 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified earlier in the request.
339 if ( ( null === $header_line ) && ! empty( $_POST['mainwp_cost_tracker_import_chk_header_first'] ) ) {
340 $header_line = sanitize_text_field( $line ) . "\r";
341 continue;
342 }
343 if ( 3 > count( $items ) ) {
344 continue;
345 }
346
347 $x = 0;
348 $import_fields = array();
349 foreach ( $default_values as $field => $val ) {
350 $value = isset( $items[ $x ] ) ? $items[ $x ] : $val;
351 $import_fields[ $field ] = sanitize_text_field( $value );
352 ++$x;
353 }
354 if ( empty( $import_fields['cost.name'] ) ) {
355 continue;
356 }
357 $import_data[] = $import_fields;
358 }
359 }
360
361 if ( ! empty( $import_data ) ) {
362 foreach ( $import_data as $k_import => $val_import ) {
363 if ( ! empty( $val_import['cost.last_renewal'] ) ) {
364 $timestamp = strtotime( $val_import['cost.last_renewal'] );
365 $import_data[ $k_import ]['cost.last_renewal'] = ( false !== $timestamp ) ? $timestamp : 0;
366 } else {
367 $import_data[ $k_import ]['cost.last_renewal'] = 0;
368 }
369
370 if ( ! empty( $val_import['cost.select_sites'] ) ) {
371 $import_data[ $k_import ]['cost.select_sites'] = array_map( 'esc_url', explode( ';', $val_import['cost.select_sites'] ) );
372 }
373 }
374 }
375
376 return array(
377 'header_line' => null !== $header_line ? esc_js( $header_line ) : '',
378 'data' => $import_data,
379 );
380 }
381 }
382