PluginProbe
TablePress – Tables in WordPress made easy / 2.2.1
TablePress – Tables in WordPress made easy v2.2.1
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
tablepress / classes / class-import-legacy.php

class-import-legacy.php in TablePress – Tables in WordPress made easy 2.2.1, at classes/class-import-legacy.php

353 lines 9.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * TablePress Table Import Class
4 *
5 * @package TablePress
6 * @subpackage Export/Import
7 * @author Tobias Bäthge
8 * @since 1.0.0
9 */
10
11 // Prohibit direct script loading.
12 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13
14 /**
15 * TablePress Table Import Class
16 *
17 * @package TablePress
18 * @subpackage Export/Import
19 * @author Tobias Bäthge
20 * @since 1.0.0
21 */
22 class TablePress_Import_Legacy extends TablePress_Import_Base {
23
24 /**
25 * File/Data Formats that are available for import.
26 *
27 * @since 1.0.0
28 * @var array<string, string>
29 */
30 public $import_formats = array();
31
32 /**
33 * Whether HTML import support is available in the PHP installation on the server.
34 *
35 * @since 1.0.0
36 * @var bool
37 */
38 public $html_import_support_available = false;
39
40 /**
41 * Data to be imported.
42 *
43 * @since 1.0.0
44 * @var string
45 */
46 protected $import_data;
47
48 /**
49 * Imported table.
50 *
51 * @since 1.0.0
52 * @var array<string, mixed>|false
53 */
54 protected $imported_table = false;
55
56 /**
57 * Initialize the Import class.
58 *
59 * @since 1.0.0
60 */
61 public function __construct() {
62 if ( class_exists( 'DOMDocument', false ) && function_exists( 'simplexml_import_dom' ) && function_exists( 'libxml_use_internal_errors' ) ) {
63 $this->html_import_support_available = true;
64 }
65
66 // Initiate here, because function call not possible outside a class method.
67 $this->import_formats = array();
68 $this->import_formats['csv'] = __( 'CSV - Character-Separated Values', 'tablepress' );
69 if ( $this->html_import_support_available ) {
70 $this->import_formats['html'] = __( 'HTML - Hypertext Markup Language', 'tablepress' );
71 }
72 $this->import_formats['json'] = __( 'JSON - JavaScript Object Notation', 'tablepress' );
73 $this->import_formats['xls'] = __( 'XLS - Microsoft Excel 97-2003 (experimental)', 'tablepress' );
74 $this->import_formats['xlsx'] = __( 'XLSX - Microsoft Excel 2007-2019 (experimental)', 'tablepress' );
75 }
76
77 /**
78 * Import a table.
79 *
80 * @since 1.0.0
81 *
82 * @param string $format Import format.
83 * @param string $data Data to import.
84 * @return array<string, mixed>|false Table array on success, false on error.
85 */
86 public function import_table( string $format, string $data ) /* : array|false */ {
87 /**
88 * Filters the data that is to be imported.
89 *
90 * @since 1.8.1
91 *
92 * @param string $data Data to import.
93 * @param string $format Import format.
94 */
95 $this->import_data = apply_filters( 'tablepress_import_table_data', $data, $format );
96
97 if ( ! in_array( $format, array( 'xlsx', 'xls' ), true ) ) {
98 $this->fix_table_encoding();
99 }
100
101 switch ( $format ) {
102 case 'csv':
103 $this->import_csv();
104 break;
105 case 'html':
106 $this->import_html();
107 break;
108 case 'json':
109 $this->import_json();
110 break;
111 case 'xlsx':
112 $this->import_xlsx();
113 break;
114 case 'xls':
115 $this->import_xls();
116 break;
117 default:
118 return false;
119 }
120
121 if ( false === $this->imported_table ) {
122 return false;
123 }
124
125 // Make sure that cells are stored as strings.
126 array_walk_recursive(
127 $this->imported_table['data'],
128 static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void {
129 $cell_content = (string) $cell_content;
130 }
131 );
132
133 return $this->imported_table;
134 }
135
136 /**
137 * Import CSV data.
138 *
139 * @since 1.0.0
140 */
141 protected function import_csv(): void {
142 $csv_parser = TablePress::load_class( 'CSV_Parser', 'csv-parser.class.php', 'libraries' );
143 $csv_parser->load_data( $this->import_data );
144 $delimiter = $csv_parser->find_delimiter();
145 $data = $csv_parser->parse( $delimiter );
146 $this->pad_array_to_max_cols( $data );
147 $this->normalize_line_endings( $data );
148 $this->imported_table = array( 'data' => $data );
149 }
150
151 /**
152 * Import HTML data.
153 *
154 * @since 1.0.0
155 */
156 protected function import_html(): void {
157 if ( ! $this->html_import_support_available ) {
158 return;
159 }
160
161 TablePress::load_file( 'html-parser.class.php', 'libraries' );
162 $table = HTML_Parser::parse( $this->import_data );
163
164 if ( is_wp_error( $table ) ) {
165 $this->imported_table = false;
166 return;
167 }
168
169 $this->pad_array_to_max_cols( $table['data'] );
170 $this->normalize_line_endings( $table['data'] );
171 $this->imported_table = $table;
172 }
173
174 /**
175 * Import JSON data.
176 *
177 * @since 1.0.0
178 */
179 protected function import_json(): void {
180 $json_table = json_decode( $this->import_data, true );
181
182 // Check if JSON could be decoded.
183 if ( is_null( $json_table ) ) {
184 $json_error = json_last_error_msg();
185 $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . "</strong><br /><br />JSON error: {$json_error}<br />";
186 wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
187 }
188
189 // Specifically cast to an array again.
190 $json_table = (array) $json_table;
191
192 if ( isset( $json_table['data'] ) ) {
193 // JSON data contained a full export.
194 $table = $json_table;
195 } else {
196 // JSON data contained only the data of a table, but no options.
197 $table = array( 'data' => array() );
198 foreach ( $json_table as $row ) {
199 $table['data'][] = array_values( (array) $row );
200 }
201 }
202
203 $this->pad_array_to_max_cols( $table['data'] );
204 $this->imported_table = $table;
205 }
206
207 /**
208 * Import Microsoft Excel 97-2003 data.
209 *
210 * @since 1.1.0
211 */
212 protected function import_xls(): void {
213 $excel_reader = TablePress::load_class( 'Spreadsheet_Excel_Reader', 'excel-reader.class.php', 'libraries', $this->import_data );
214
215 // Loop through Excel file and retrieve value and colspan/rowspan properties for each cell.
216 $sheet = 0; // 0 means first sheet of the Workbook
217 $table = array();
218 $num_rows = $excel_reader->rowcount( $sheet );
219 $num_columns = $excel_reader->colcount( $sheet );
220 for ( $row = 1; $row <= $num_rows; $row++ ) {
221 $table_row = array();
222 for ( $column = 1; $column <= $num_columns; $column++ ) {
223 $cell = array();
224 $cell['rowspan'] = $excel_reader->rowspan( $row, $column, $sheet );
225 $cell['colspan'] = $excel_reader->colspan( $row, $column, $sheet );
226 $cell['val'] = $excel_reader->val( $row, $column, $sheet );
227 $table_row[] = $cell;
228 }
229 $table[] = $table_row;
230 }
231
232 // Transform colspan/rowspan properties to TablePress equivalent (cell content).
233 foreach ( $table as $row_idx => $row ) {
234 foreach ( $row as $col_idx => $cell ) {
235 if ( 1 === $cell['rowspan'] && 1 === $cell['colspan'] ) {
236 continue;
237 }
238
239 if ( 1 < $cell['colspan'] ) {
240 for ( $i = 1; $i < $cell['colspan']; $i++ ) {
241 $table[ $row_idx ][ $col_idx + $i ]['val'] = '#colspan#';
242 }
243 }
244 if ( 1 < $cell['rowspan'] ) {
245 for ( $i = 1; $i < $cell['rowspan']; $i++ ) {
246 $table[ $row_idx + $i ][ $col_idx ]['val'] = '#rowspan#';
247 }
248 }
249
250 if ( 1 < $cell['rowspan'] && 1 < $cell['colspan'] ) {
251 for ( $i = 1; $i < $cell['rowspan']; $i++ ) {
252 for ( $j = 1; $j < $cell['colspan']; $j++ ) {
253 $table[ $row_idx + $i ][ $col_idx + $j ]['val'] = '#span#';
254 }
255 }
256 }
257 }
258 }
259
260 // Flatten value property to two-dimensional array.
261 foreach ( $table as &$row ) {
262 foreach ( $row as &$cell ) {
263 $cell = $cell['val'];
264 }
265 unset( $cell );
266 }
267 unset( $row );
268
269 $this->imported_table = array( 'data' => $table );
270 }
271
272 /**
273 * Import Microsoft Excel 2007-2019 data.
274 *
275 * @since 1.1.0
276 */
277 protected function import_xlsx(): void {
278 TablePress::load_file( 'simplexlsx.class.php', 'libraries' );
279 $xlsx_file = \Shuchkin\SimpleXLSX::parse( $this->import_data, true );
280
281 if ( ! $xlsx_file ) {
282 $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . '</strong><br /><br />' . \Shuchkin\SimpleXLSX::parseError() . '<br />';
283 wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
284 }
285
286 $this->imported_table = array( 'data' => $xlsx_file->rows() );
287 }
288
289 /**
290 * Fixes the encoding to UTF-8 for the entire string that is to be imported.
291 *
292 * @since 1.0.0
293 *
294 * @link http://stevephillips.me/blog/dealing-php-and-character-encoding
295 */
296 protected function fix_table_encoding(): void {
297 // Check and remove possible UTF-8 Byte-Order Mark (BOM).
298 $bom = pack( 'CCC', 0xef, 0xbb, 0xbf );
299 if ( str_starts_with( $this->import_data, $bom ) ) {
300 $this->import_data = substr( $this->import_data, 3 );
301 // If data has a BOM, it's UTF-8, so further checks unnecessary.
302 return;
303 }
304
305 // Require the iconv() function for the following checks.
306 if ( ! function_exists( 'iconv' ) ) {
307 return;
308 }
309
310 // Check for possible UTF-16 BOMs ("little endian" and "big endian") and try to convert the data to UTF-8.
311 if ( str_starts_with( $this->import_data, "\xFF\xFE" ) || str_starts_with( $this->import_data, "\xFE\xFF" ) ) {
312 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
313 $data = @iconv( 'UTF-16', 'UTF-8', $this->import_data );
314 if ( false !== $data ) {
315 $this->import_data = $data;
316 return;
317 }
318 }
319
320 // Detect the character encoding and convert to UTF-8, if it's different.
321 if ( function_exists( 'mb_detect_encoding' ) ) {
322 $current_encoding = mb_detect_encoding( $this->import_data, 'ASCII, UTF-8, ISO-8859-1' );
323 if ( 'UTF-8' !== $current_encoding ) {
324 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
325 $data = @iconv( $current_encoding, 'UTF-8', $this->import_data ); // @phpstan-ignore-line
326 if ( false !== $data ) {
327 $this->import_data = $data;
328 return;
329 }
330 }
331 }
332 }
333
334 /**
335 * Replaces Windows line breaks \r\n with Unix line breaks \n.
336 *
337 * This function uses call by reference to save PHP memory on large arrays.
338 *
339 * @since 2.0.0
340 *
341 * @param string[] $an_array Array in which Windows line breaks should be replaced by Unix line breaks.
342 */
343 protected function normalize_line_endings( array &$an_array ): void {
344 array_walk_recursive(
345 $an_array,
346 static function ( string &$cell_content, int $col_idx ): void {
347 $cell_content = str_replace( "\r\n", "\n", $cell_content );
348 }
349 );
350 }
351
352 } // class TablePress_Import_Legacy
353