PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.11.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.11.7
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
← All changes | classes/Visualizer/Source/Csv.php +66 -16 3.0.83.11.7 View file →
@@ -46,10 +46,10 @@
46 46 *
47 47 * @access public
48 48 * @param string $filename The path to the file.
49 49 */
50 - public function __construct( $filename = null ) {
51 - $this->_filename = trim( $filename );
50 + public function __construct( $filename = '' ) {
51 + $this->_filename = trim( (string) $filename );
52 52 }
53 53
54 54 /**
55 55 * Fetches series information.
@@ -61,29 +61,39 @@
61 61 */
62 62 private function _fetchSeries( &$handle ) {
63 63 // read column titles
64 64 $labels = fgetcsv( $handle, 0, VISUALIZER_CSV_DELIMITER, VISUALIZER_CSV_ENCLOSURE );
65 - // read series types
66 - $types = fgetcsv( $handle, 0, VISUALIZER_CSV_DELIMITER, VISUALIZER_CSV_ENCLOSURE );
65 + $types = null;
67 66
67 + if ( false !== strpos( $this->_filename, 'tqx=out:csv' ) ) {
68 + $attributes = $this->_fetchSeriesForGoogleQueryLanguage( $labels );
69 + if ( ! $attributes['abort'] ) {
70 + $labels = $attributes['labels'];
71 + $types = $attributes['types'];
72 + }
73 + }
74 +
75 + if ( is_null( $types ) ) {
76 + // read series types
77 + $types = fgetcsv( $handle, 0, VISUALIZER_CSV_DELIMITER, VISUALIZER_CSV_ENCLOSURE );
78 + }
79 +
80 + $labels = array_filter( $labels );
81 + $types = array_filter( $types );
82 +
68 83 if ( ! $labels || ! $types ) {
84 + $this->_error = esc_html__( 'File should have a heading row (1st row) and a data type row (2nd row). Please try again.', 'visualizer' );
69 85 return false;
70 86 }
71 87
72 - // if no types were setup, re read labels and empty types array
73 88 $types = array_map( 'trim', $types );
74 89 if ( ! self::_validateTypes( $types ) ) {
75 - // re open the file
76 - fclose( $handle );
77 - $handle = $this->_get_file_handle();
78 -
79 - // re read the labels and empty types array
80 - $labels = fgetcsv( $handle, 0, VISUALIZER_CSV_DELIMITER, VISUALIZER_CSV_ENCLOSURE );
81 - $types = array();
90 + $this->_error = esc_html__( 'Invalid data types detected in the data type row (2nd row). Please try again.', 'visualizer' );
91 + return false;
82 92 }
83 93
84 94 for ( $i = 0, $len = count( $labels ); $i < $len; $i++ ) {
85 - $default_type = $i == 0 ? 'string' : 'number';
95 + $default_type = $i === 0 ? 'string' : 'number';
86 96
87 97 $labels[ $i ] = $this->toUTF8( $labels[ $i ] );
88 98
89 99 $this->_series[] = array(
@@ -104,10 +114,8 @@
104 114 * @param string $filename Optional file name to get handle. If omitted, $_filename is used.
105 115 * @return resource File handle resource on success, otherwise FALSE.
106 116 */
107 117 protected function _get_file_handle( $filename = false ) {
108 - // set line endings auto detect mode
109 - ini_set( 'auto_detect_line_endings', true );
110 118 // open file and return handle
111 119 return fopen( $filename ? $filename : $this->_filename, 'rb' );
112 120 }
113 121
@@ -121,8 +129,9 @@
121 129 */
122 130 public function fetch() {
123 131 // if filename is empty return false
124 132 if ( empty( $this->_filename ) ) {
133 + $this->_error = esc_html__( 'No file provided. Please try again.', 'visualizer' );
125 134 return false;
126 135 }
127 136
128 137 // read file and fill arrays
@@ -133,10 +142,10 @@
133 142 return false;
134 143 }
135 144
136 145 // fetch data
146 + // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
137 147 while ( ( $data = fgetcsv( $handle, 0, VISUALIZER_CSV_DELIMITER, VISUALIZER_CSV_ENCLOSURE ) ) !== false ) {
138 -
139 148 $this->_data[] = $this->_normalizeData( $data );
140 149 }
141 150
142 151 // close file handle
@@ -157,5 +166,46 @@
157 166 public function getSourceName() {
158 167 return __CLASS__;
159 168 }
160 169
170 + /**
171 + * Adds support for QueryLanguage https://developers.google.com/chart/interactive/docs/querylanguage
172 + * where the user can provide something like /gviz/tq?tq=select%20A%2C%20B%20&tqx=out:csv after the URL of the raw spreadsheet
173 + * to get the subset of data specified by the query
174 + * this will conflate the heading and the type into one value viz. for heading XXX and type string, the value will become "XXX string"
175 + * so we need to split them apart logically
176 + * also the $types variable now contains the first row because the header and the type got conflated
177 + */
178 + private function _fetchSeriesForGoogleQueryLanguage( $labels, $types = array() ) {
179 + $new_labels = array();
180 + $new_types = array();
181 + $abort = false;
182 + foreach ( $labels as $label ) {
183 + // get the index of the last space
184 + $index = strrpos( $label, ' ' );
185 + if ( $index === false ) {
186 + // no space here? something has gone wrong; abort the entire process.
187 + $abort = true;
188 + break;
189 + }
190 + $type = trim( substr( $label, $index + 1 ) );
191 + if ( ! self::_validateTypes( array( $type ) ) ) {
192 + // some other data type? abort the entire process.
193 + $abort = true;
194 + break;
195 + }
196 + $label = substr( $label, 0, $index );
197 + $new_labels[] = $label;
198 + $new_types[] = $type;
199 + }
200 + if ( ! $abort ) {
201 + $labels = $new_labels;
202 + $types = $new_types;
203 + }
204 +
205 + return array(
206 + 'abort' => $abort,
207 + 'labels' => $labels,
208 + 'types' => $types,
209 + );
210 + }
161 211 }