PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.8
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.8
4.0.8 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 All 149 releases
← All changes | classes/Visualizer/Module/AIBuilder.php +33 -19 4.0.44.0.8 View file →
@@ -94,8 +94,19 @@
94 94 }
95 95 }
96 96
97 97 /**
98 + * Verify that the current user can edit a chart.
99 + *
100 + * @param int $chart_id Chart ID.
101 + */
102 + private function _verify_chart_access( $chart_id ): void {
103 + if ( ! self::can_edit_chart( $chart_id ) ) {
104 + wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'visualizer' ) ), 403 );
105 + }
106 + }
107 +
108 + /**
98 109 * Persist chart data + series from a source.
99 110 *
100 111 * @param int $chart_id Chart ID.
101 112 * @param Visualizer_Source $source Data source instance.
@@ -158,8 +169,9 @@
158 169 $chart_id = intval( isset( $_POST['chart_id'] ) ? $_POST['chart_id'] : 0 );
159 170 if ( ! $chart_id || ! get_post( $chart_id ) ) {
160 171 wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
161 172 }
173 + $this->_verify_chart_access( $chart_id );
162 174 wp_send_json_success(
163 175 array(
164 176 'upload_nonce' => wp_create_nonce( 'visualizer-ai-upload-' . $chart_id ),
165 177 )
@@ -179,8 +191,9 @@
179 191 $chart = $chart_id ? get_post( $chart_id ) : null;
180 192 if ( ! $chart || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
181 193 wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
182 194 }
195 + $this->_verify_chart_access( $chart_id );
183 196
184 197 $series = get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true );
185 198 $data = Visualizer_Module::get_chart_data( $chart, '', false );
186 199 $code = get_post_meta( $chart_id, self::CF_D3_CODE, true );
@@ -205,9 +218,9 @@
205 218
206 219 /**
207 220 * Determines whether a remote URL serves an XLSX file.
208 221 *
209 - * Uses wp_safe_remote_get() and checks ZIP magic number (PK\x03\x04).
222 + * Uses the shared remote-fetch policy and checks ZIP magic number (PK\x03\x04).
210 223 *
211 224 * @access private
212 225 * @param string $url The remote URL to probe.
213 226 * @return bool TRUE if the file appears to be XLSX, FALSE otherwise.
@@ -217,16 +230,17 @@
217 230 if ( ! $tmpfile ) {
218 231 return false;
219 232 }
220 233
221 - $response = wp_safe_remote_get(
234 + $response = Visualizer_Remote_Fetch::request(
222 235 $url,
223 236 array(
224 - 'timeout' => 15,
225 - 'redirection' => 5,
226 - 'stream' => true,
227 - 'filename' => $tmpfile,
228 - 'headers' => array( 'Range' => 'bytes=0-3' ),
237 + 'timeout' => 15,
238 + 'redirection' => 5,
239 + 'stream' => true,
240 + 'filename' => $tmpfile,
241 + 'headers' => array( 'Range' => 'bytes=0-3' ),
242 + 'limit_response_size' => 4,
229 243 )
230 244 );
231 245
232 246 if ( is_wp_error( $response ) ) {
@@ -257,8 +271,9 @@
257 271 }
258 272 if ( ! get_post( $chart_id ) ) {
259 273 wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
260 274 }
275 + $this->_verify_chart_access( $chart_id );
261 276
262 277 $source_type = isset( $_POST['source_type'] ) ? sanitize_key( $_POST['source_type'] ) : 'csv_string';
263 278 $source = null;
264 279 $tmp_files = array();
@@ -296,19 +311,8 @@
296 311 wp_send_json_error( array( 'message' => __( 'No URL provided.', 'visualizer' ) ) );
297 312 }
298 313 $url = wp_unslash( $_POST['file_url'] );
299 314
300 - // Allow local absolute paths in dev (same CSVs used by Classic).
301 - if ( is_string( $url ) && file_exists( $url ) && is_readable( $url ) ) {
302 - $ext = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
303 - if ( 'xlsx' === $ext && class_exists( 'Visualizer_Source_Xlsx' ) ) {
304 - $source = new Visualizer_Source_Xlsx( $url );
305 - } else {
306 - $source = new Visualizer_Source_Csv( $url );
307 - }
308 - break;
309 - }
310 -
311 315 if ( function_exists( 'wp_http_validate_url' ) ) {
312 316 $validated_url = wp_http_validate_url( (string) $url );
313 317 $url = false === $validated_url ? false : (string) $validated_url;
314 318 } else {
@@ -364,9 +368,9 @@
364 368 break;
365 369
366 370 // ── Database query ────────────────────────────────────────────────
367 371 case 'db_query':
368 - if ( ! current_user_can( 'manage_options' ) && ! is_super_admin() ) {
372 + if ( ! current_user_can( 'manage_options' ) || ! is_super_admin() || ! Visualizer_Module::is_pro() ) {
369 373 wp_send_json_error( array( 'message' => __( 'Action not allowed for this user.', 'visualizer' ) ), 403 );
370 374 }
371 375 if ( empty( $_POST['db_query'] ) ) {
372 376 wp_send_json_error( array( 'message' => __( 'No query provided.', 'visualizer' ) ) );
@@ -434,8 +438,9 @@
434 438 $chart_id = intval( isset( $_POST['chart_id'] ) ? $_POST['chart_id'] : 0 );
435 439 if ( ! $chart_id || ! get_post( $chart_id ) ) {
436 440 wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
437 441 }
442 + $this->_verify_chart_access( $chart_id );
438 443
439 444 $prompt = isset( $_POST['prompt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['prompt'] ) ) : '';
440 445 $series = isset( $_POST['series'] ) ? wp_unslash( $_POST['series'] ) : '';
441 446 $data = isset( $_POST['data'] ) ? wp_unslash( $_POST['data'] ) : '';
@@ -496,8 +501,12 @@
496 501 $workflow_id = $response_body['data']['workflowId'];
497 502 }
498 503 }
499 504
505 + if ( ! empty( $workflow_id ) ) {
506 + set_transient( 'viz_ai_wf_' . $workflow_id, get_current_user_id(), 6 * HOUR_IN_SECONDS );
507 + }
508 +
500 509 wp_send_json_success(
501 510 array(
502 511 'workflow_id' => $workflow_id,
503 512 )
@@ -518,8 +527,12 @@
518 527 if ( empty( $workflow_id ) ) {
519 528 wp_send_json_error( array( 'message' => __( 'Missing workflow ID.', 'visualizer' ) ) );
520 529 }
521 530
531 + if ( (int) get_transient( 'viz_ai_wf_' . $workflow_id ) !== get_current_user_id() ) {
532 + wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'visualizer' ) ), 403 );
533 + }
534 +
522 535 $agents_url = VISUALIZER_AGENTS_URL;
523 536 $workflow_slug = $this->_get_workflow_slug();
524 537 $headers = $this->_get_agents_headers();
525 538
@@ -559,8 +572,9 @@
559 572
560 573 if ( ! $chart_id || ! get_post( $chart_id ) ) {
561 574 wp_send_json_error( array( 'message' => __( 'Chart not found.', 'visualizer' ) ) );
562 575 }
576 + $this->_verify_chart_access( $chart_id );
563 577 if ( empty( $code ) ) {
564 578 wp_send_json_error( array( 'message' => __( 'No chart code found. Generate a chart first.', 'visualizer' ) ) );
565 579 }
566 580