PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.6
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.6
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Source / Xlsx.php

Xlsx.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 4.0.6, at classes/Visualizer/Source/Xlsx.php

156 lines 4.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Source manager for local XLSX files.
4 *
5 * Reads the first sheet of an XLSX file using the OpenSpout library,
6 * which is already bundled as a dependency of this plugin.
7 * Legacy .xls (BIFF) files are not supported; OpenSpout's XLSX reader
8 * only handles the Office Open XML (.xlsx) format.
9 *
10 * Expected sheet layout (same convention as CSV import):
11 * Row 1 – column labels
12 * Row 2 – column types (string, number, boolean, date, datetime, timeofday)
13 * Row 3+ – data rows
14 *
15 * @category Visualizer
16 * @package Source
17 *
18 * @since 3.11.0
19 */
20 class Visualizer_Source_Xlsx extends Visualizer_Source {
21
22 /**
23 * The path to the XLSX file.
24 *
25 * @access protected
26 * @var string
27 */
28 protected $_filename;
29
30 /**
31 * Constructor.
32 *
33 * @access public
34 * @param string $filename Path to the XLSX file.
35 */
36 public function __construct( $filename = '' ) {
37 $this->_filename = trim( (string) $filename );
38 }
39
40 /**
41 * Fetches information from the XLSX file and builds the series/data arrays.
42 *
43 * @access public
44 * @return boolean TRUE on success, FALSE on failure.
45 */
46 public function fetch() {
47 if ( empty( $this->_filename ) ) {
48 $this->_error = esc_html__( 'No file provided. Please try again.', 'visualizer' );
49 return false;
50 }
51
52 if ( ! class_exists( 'OpenSpout\Reader\Common\Creator\ReaderEntityFactory' ) ) {
53 $this->_error = esc_html__( 'The OpenSpout library is required to import XLSX files but could not be found. Please contact support.', 'visualizer' );
54 return false;
55 }
56
57 $reader = \OpenSpout\Reader\Common\Creator\ReaderEntityFactory::createXLSXReader();
58 $file_path = $this->_get_file_path();
59 if ( ! $file_path ) {
60 return false;
61 }
62
63 try {
64 $reader->open( $file_path );
65
66 $all_rows = array();
67 foreach ( $reader->getSheetIterator() as $sheet ) {
68 foreach ( $sheet->getRowIterator() as $row ) {
69 $row_data = array();
70 foreach ( $row->getCells() as $cell ) {
71 $value = $cell->getValue();
72 // Convert non-string scalars to string for uniform handling;
73 // _normalizeData() will cast them to the correct type later.
74 $row_data[] = is_null( $value ) ? null : (string) $value;
75 }
76 $all_rows[] = $row_data;
77 }
78 break; // Only read the first sheet.
79 }
80 } catch ( \Exception $e ) {
81 $reader->close();
82 $this->_error = sprintf(
83 /* translators: %s - the exception message. */
84 esc_html__( 'Could not read the XLSX file: %s', 'visualizer' ),
85 $e->getMessage()
86 );
87 return false;
88 }
89
90 $reader->close();
91
92 if ( count( $all_rows ) < 2 ) {
93 $this->_error = esc_html__( 'File should have a heading row (1st row) and a data type row (2nd row). Please try again.', 'visualizer' );
94 return false;
95 }
96
97 $labels = array_filter( $all_rows[0] );
98 $types = array_filter( $all_rows[1] );
99
100 if ( ! $labels || ! $types ) {
101 $this->_error = esc_html__( 'File should have a heading row (1st row) and a data type row (2nd row). Please try again.', 'visualizer' );
102 return false;
103 }
104
105 $types = array_map( 'trim', $types );
106 if ( ! self::_validateTypes( $types ) ) {
107 $this->_error = esc_html__( 'Invalid data types detected in the data type row (2nd row). Please try again.', 'visualizer' );
108 return false;
109 }
110
111 // Build the series array from row 1 (labels) and row 2 (types).
112 $label_values = $all_rows[0];
113 $type_values = $all_rows[1];
114 $col_count = count( $label_values );
115
116 for ( $i = 0; $i < $col_count; $i++ ) {
117 $default_type = ( $i === 0 ) ? 'string' : 'number';
118 $label = isset( $label_values[ $i ] ) ? $this->toUTF8( (string) $label_values[ $i ] ) : '';
119 $type = isset( $type_values[ $i ] ) && ! empty( $type_values[ $i ] ) ? trim( $type_values[ $i ] ) : $default_type;
120
121 $this->_series[] = array(
122 'label' => sanitize_text_field( wp_strip_all_tags( $label ) ),
123 'type' => $type,
124 );
125 }
126
127 // Parse data rows (row 3 onwards).
128 for ( $r = 2, $total = count( $all_rows ); $r < $total; $r++ ) {
129 $this->_data[] = $this->_normalizeData( $all_rows[ $r ] );
130 }
131
132 return true;
133 }
134
135 /**
136 * Returns the file path to open with the reader.
137 * Subclasses may override this to supply a locally-downloaded copy.
138 *
139 * @access protected
140 * @return string
141 */
142 protected function _get_file_path() {
143 return $this->_filename;
144 }
145
146 /**
147 * Returns the source name.
148 *
149 * @access public
150 * @return string
151 */
152 public function getSourceName() {
153 return __CLASS__;
154 }
155 }
156