PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.8.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.8.0
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 / Render / Layout.php

Layout.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.8.0, at classes/Visualizer/Render/Layout.php

1,035 lines 44.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // +----------------------------------------------------------------------+
4 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
5 // +----------------------------------------------------------------------+
6 // | This program is free software; you can redistribute it and/or modify |
7 // | it under the terms of the GNU General Public License, version 2, as |
8 // | published by the Free Software Foundation. |
9 // | |
10 // | This program is distributed in the hope that it will be useful, |
11 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 // | GNU General Public License for more details. |
14 // | |
15 // | You should have received a copy of the GNU General Public License |
16 // | along with this program; if not, write to the Free Software |
17 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
18 // | MA 02110-1301 USA |
19 // +----------------------------------------------------------------------+
20 // | Author: Eugene Manuilov <eugene@manuilov.org> |
21 // +----------------------------------------------------------------------+
22 /**
23 * Layout rendering class.
24 *
25 * @category Visualizer
26 * @package Render
27 */
28 class Visualizer_Render_Layout extends Visualizer_Render {
29
30 /**
31 * Renders template.
32 *
33 * @since 1.0.0
34 *
35 * @abstract
36 * @access protected
37 */
38 protected function _toHTML() {
39 // empty.
40 }
41
42 /**
43 * Show the layout by delegating the call to the layout-specific method with the params.
44 *
45 * @access public
46 */
47 public static function show( $layout ) {
48 return call_user_func( array( __CLASS__, '_render' . str_replace( ' ', '', ucwords( str_replace( '-', ' ', $layout ) ) ) ), func_get_args() );
49 }
50
51 /**
52 * Show the DB query box.
53 *
54 * @access public
55 */
56 public static function _renderDbQuery( $args ) {
57 $query = $args[1];
58 ?>
59 <div id='visualizer-db-query' style="display: none">
60 <div class="visualizer-db-query-form">
61 <div>
62 <form id='db-query-form'>
63 <input type="hidden" name="chart_id" value="<?php echo $args[2]; ?>">
64 <?php do_action( 'visualizer_db_query_add_layout', $args ); ?>
65 <textarea name='query' class='visualizer-db-query' placeholder="<?php _e( 'Your query goes here', 'visualizer' ); ?>"><?php echo $query; ?></textarea>
66 </form>
67 <div class='db-wizard-error'></div>
68 </div>
69 <div>
70 <input type="button" class="button button-primary" id='visualizer-query-fetch' value='<?php _e( 'Show Results', 'visualizer' ); ?>'>
71 </div>
72 </div>
73 <div class='db-wizard-hints'>
74 <ul>
75 <li><?php echo sprintf( __( 'For examples of queries and links to resources that you can use with this feature, please click %1$shere%2$s', 'visualizer' ), '<a href="' . VISUALIZER_DB_QUERY_DOC_URL . '" target="_blank">', '</a>' ); ?></li>
76 <li><?php echo sprintf( __( 'Use %1$sControl+Space%2$s for autocompleting keywords or table names.', 'visualizer' ), '<span class="visualizer-emboss">', '</span>' ); ?></li>
77 <?php do_action( 'visualizer_db_query_add_hints', $args ); ?>
78 </ul>
79 </div>
80 <div class='db-wizard-results'></div>
81
82 </div>
83 <?php
84 }
85
86 /**
87 * Show the DB wizard's results table.
88 *
89 * @access public
90 */
91 public static function _renderDbWizardResults( $args ) {
92 $headers = $args[1];
93 $results = $args[2];
94 ob_start();
95 ?>
96 <table cellspacing="0" width="100%" id="results">
97 <thead>
98 <tr>
99 <?php
100 foreach ( $headers as $header ) {
101 echo '<th>' . $header['label'] . '</th>';
102 }
103 ?>
104 </tr>
105 </thead>
106 <tfoot>
107 </tfoot>
108 <tbody>
109 <?php
110 foreach ( $results as $result ) {
111 echo '<tr>';
112 foreach ( $result as $r ) {
113 echo '<td>' . $r . '</td>';
114 }
115 echo '</tr>';
116 }
117 ?>
118 </tbody>
119 </table>
120 <?php
121 return ob_get_clean();
122 }
123
124 /**
125 * Show the JSON/REST parameters boxes.
126 *
127 * @access public
128 */
129 public static function _renderJsonScreen( $args ) {
130 $id = $args[1];
131 $action = esc_url(
132 add_query_arg(
133 array(
134 'action' => Visualizer_Plugin::ACTION_JSON_SET_DATA,
135 'security' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION ),
136 'chart' => $id,
137 ),
138 admin_url( 'admin-ajax.php' )
139 )
140 );
141
142 $url = get_post_meta( $id, Visualizer_Plugin::CF_JSON_URL, true );
143 $root = get_post_meta( $id, Visualizer_Plugin::CF_JSON_ROOT, true );
144 $paging = get_post_meta( $id, Visualizer_Plugin::CF_JSON_PAGING, true );
145 $headers = get_post_meta( $id, Visualizer_Plugin::CF_JSON_HEADERS, true );
146 if ( empty( $headers ) ) {
147 $headers = array();
148 }
149 if ( $headers && empty( $headers['method'] ) ) {
150 $headers['method'] = 'get';
151 }
152 $methods = apply_filters( 'visualizer_json_request_methods', array( 'GET', 'POST' ) );
153
154 // open the headers by default?
155 $headers_open = $headers && array_key_exists( 'auth', $headers ) && ( array_key_exists( 'username', $headers['auth'] ) && ! empty( $headers['auth']['username'] ) ) || ( ! empty( $headers['auth'] ) && is_string( $headers['auth'] ) );
156 ?>
157 <div id="visualizer-json-screen" style="display: none">
158 <div class="visualizer-json-form">
159 <h3 class="viz-step step1"><?php _e( 'STEP 1: Specify the JSON endpoint/URL', 'visualizer' ); ?></h3>
160 <div>
161 <form id="json-endpoint-form">
162 <div class="json-wizard-hints">
163 <ul class="info">
164 <li><?php echo sprintf( __( 'If you want to add authentication or headers to the endpoint or change the request in any way, please refer to our document %1$shere%2$s.', 'visualizer' ), '<a href="https://docs.themeisle.com/article/1043-visualizer-how-to-extend-rest-endpoints-with-json-response" target="_blank">', '</a>' ); ?></li>
165 </ul>
166 </div>
167
168 <input
169 type="url"
170 id="vz-import-json-url"
171 name="url"
172 value="<?php echo esc_url( $url ); ?>"
173 placeholder="<?php esc_html_e( 'Please enter the URL', 'visualizer' ); ?>"
174 class="visualizer-input json-form-element">
175 <button class="button button-secondary button-small" id="visualizer-json-fetch"><?php esc_html_e( 'Fetch Endpoint', 'visualizer' ); ?></button>
176
177 <div class="visualizer-json-subform">
178 <h3 class="viz-substep <?php echo $headers_open ? 'open' : ''; ?>"><?php _e( 'Headers', 'visualizer' ); ?></h3>
179 <div class="json-wizard-headers">
180 <div class="json-wizard-header">
181 <div><?php _e( 'Request Type', 'visualizer' ); ?></div>
182 <div>
183 <select name="method" class="json-form-element">
184 <?php foreach ( $methods as $method ) { ?>
185 <option value="<?php echo $method; ?>" <?php $headers && selected( $headers['method'], $method ); ?>><?php echo $method; ?></option>
186 <?php } ?>
187 </select>
188 </div>
189 </div>
190 <div class="json-wizard-header">
191 <div><?php _e( 'Credentials', 'visualizer' ); ?></div>
192 <div>
193 <input
194 type="text"
195 id="vz-import-json-username"
196 name="username"
197 value="<?php echo ( array_key_exists( 'auth', $headers ) && array_key_exists( 'username', $headers['auth'] ) ? $headers['auth']['username'] : '' ); ?>"
198 placeholder="<?php esc_html_e( 'Username/Access Key', 'visualizer' ); ?>"
199 class="json-form-element">
200 &
201 <input
202 type="password"
203 id="vz-import-json-password"
204 name="password"
205 value="<?php echo ( array_key_exists( 'auth', $headers ) && array_key_exists( 'password', $headers['auth'] ) ? $headers['auth']['password'] : '' ); ?>"
206 placeholder="<?php esc_html_e( 'Password/Secret Key', 'visualizer' ); ?>"
207 class="json-form-element">
208 </div>
209 </div>
210 <div class="json-wizard-header">
211 <div></div>
212 <div><?php esc_html_e( 'OR', 'visualizer' ); ?></div>
213 </div>
214 <div class="json-wizard-header">
215 <div><?php esc_html_e( 'Authorization', 'visualizer' ); ?></div>
216 <div>
217 <input
218 type="text"
219 id="vz-import-json-auth"
220 name="auth"
221 value="<?php echo ( array_key_exists( 'auth', $headers ) && is_string( $headers['auth'] ) ? $headers['auth'] : '' ); ?>"
222 placeholder="<?php esc_html_e( 'e.g. SharedKey <AccountName>:<Signature>', 'visualizer' ); ?>"
223 class="visualizer-input json-form-element">
224 </div>
225 </div>
226 <div class="json-wizard-header">
227 <div class="field-title"><?php esc_html_e( 'Additional headers', 'visualizer' ); ?></div>
228 <div>
229 <textarea name="additional_headers" class="visualizer-input" placeholder="<?php esc_html_e( 'Key:Value, Key2:Value2,...', 'visualizer' ); ?>"><?php echo isset( $headers['additional_headers'] ) ? $headers['additional_headers'] : ''; ?></textarea>
230 </div>
231 </div>
232 </div>
233 </div>
234 </form>
235 </div>
236 <h3 class="viz-step step2 <?php echo empty( $url ) ? 'ui-state-disabled' : ''; ?> json-root-form"><?php _e( 'STEP 2: Choose the JSON root', 'visualizer' ); ?></h3>
237 <div>
238 <form id="json-root-form">
239 <input type="hidden" name="chart" value="<?php echo $id; ?>">
240 <select name="root" id="vz-import-json-root" class="json-form-element">
241 <?php
242 if ( ! empty( $root ) ) {
243 ?>
244 <option value="<?php echo esc_attr( $root ); ?>"><?php echo str_replace( Visualizer_Source_Json::TAG_SEPARATOR, Visualizer_Source_Json::TAG_SEPARATOR_VIEW, $root ); ?></option>
245 <?php
246 }
247 ?>
248 </select>
249 <button class="button button-secondary button-small" id="visualizer-json-parse"><?php esc_html_e( 'Parse Endpoint', 'visualizer' ); ?></button>
250 </form>
251 </div>
252 <h3 class="viz-step step3 <?php echo empty( $root ) ? 'ui-state-disabled' : ''; ?>"><?php _e( 'STEP 3: Specify miscellaneous parameters', 'visualizer' ); ?></h3>
253 <div>
254 <form id="json-conclude-form-helper">
255 <div class="<?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature' ); ?>">
256 <div class="json-pagination">
257 <select name="paging" id="vz-import-json-paging" class="json-form-element" data-template='<?php echo sprintf( 'Get results from the first %d pages using %s', apply_filters( 'visualizer_json_fetch_pages', 5, $url ), '?' ); ?>'>
258 <option value="0" class="static"><?php _e( 'Get results from the first page only', 'visualizer' ); ?></option>
259 <?php
260 if ( ! empty( $paging ) ) {
261 ?>
262 <option value="<?php echo esc_attr( $paging ); ?>"><?php echo sprintf( 'Get results from the first %d pages using %s', apply_filters( 'visualizer_json_fetch_pages', 5, $url ), str_replace( Visualizer_Source_Json::TAG_SEPARATOR, Visualizer_Source_Json::TAG_SEPARATOR_VIEW, $paging ) ); ?></option>
263 <?php
264 }
265 ?>
266 </select>
267 <?php
268 if ( ! Visualizer_Module::is_pro() ) {
269 ?>
270 <br/>
271 <br/>
272 <br/>
273 <br/>
274 <?php
275 }
276 ?>
277 <?php echo apply_filters( 'visualizer_pro_upsell', '' ); ?>
278 </div>
279 </div>
280 </form>
281 </div>
282 <h3 class="viz-step step4 ui-state-disabled"><?php _e( 'STEP 4: Select the data to display in the chart', 'visualizer' ); ?></h3>
283 <div>
284 <form id="json-conclude-form" action="<?php echo $action; ?>" method="post" target="thehole">
285 <div class="json-wizard-hints html-table-editor-hints">
286 <ul class="info">
287 <li><?php _e( 'If you see Invalid Data in the table, you may have selected the wrong root to fetch data from. Please select an alternative from the JSON root dropdown.', 'visualizer' ); ?></li>
288 <li><?php _e( 'Select whether to include the data in the chart. Each column selected will form one series.', 'visualizer' ); ?></li>
289 <li><?php _e( 'If a column is selected to be included, specify its data type.', 'visualizer' ); ?></li>
290 <li><?php _e( 'You can use drag/drop to reorder the columns but this column position is not saved. So when you reload the table, you may have to reorder again.', 'visualizer' ); ?></li>
291 <li><?php _e( 'You can select any number of columns but the chart type selected will determine how many will display in the chart.', 'visualizer' ); ?></li>
292 <li><?php _e( 'Once you have made your selection, click \'Show Chart\' on the right to view the chart.', 'visualizer' ); ?></li>
293 </ul>
294 </div>
295 <div class="json-table"></div>
296 <button class="button button-primary" style="display: none" id="visualizer-json-conclude"></button>
297 </form>
298 </div>
299 </div>
300 </div>
301 <?php
302 }
303
304 /**
305 * Show the simple editor(s).
306 *
307 * @access public
308 */
309 public static function _renderSimpleEditorScreen( $args ) {
310 $chart_id = $args[1];
311 $action = esc_url(
312 add_query_arg(
313 array(
314 'action' => Visualizer_Plugin::ACTION_UPLOAD_DATA,
315 'nonce' => wp_create_nonce(),
316 'chart' => $chart_id,
317 ),
318 admin_url( 'admin-ajax.php' )
319 )
320 );
321 ?>
322 <div class="viz-simple-editor">
323 <div class="viz-simple-editor-type viz-table-editor">
324 <form id="table-editor-form" action="<?php echo $action; ?>" method="post" target="thehole">
325 <div class="html-table-editor-hints">
326 <ul class="info">
327 <li><?php _e( 'Select whether to include the data in the chart. Each column selected will form one series.', 'visualizer' ); ?></li>
328 <li><?php _e( 'If a column is selected to be included, specify its data type.', 'visualizer' ); ?></li>
329 <li><?php _e( 'You can use drag/drop to reorder the columns but this column position is not saved. So when you reload the table, you may have to reorder again.', 'visualizer' ); ?></li>
330 <li><?php _e( 'You can select any number of columns but the chart type selected will determine how many will display in the chart.', 'visualizer' ); ?></li>
331 </ul>
332 </div>
333 <div class="viz-html-table">
334 <?php Visualizer_Render_Layout::show( 'editor-table', null, $chart_id, 'viz-html-table', true, true ); ?>
335 </div>
336 <input type="hidden" name="table_data" value="yes">
337 <input type="hidden" name="editor-type" value="table">
338 </form>
339 </div>
340
341 <?php Visualizer_Render_Layout::show( 'text-editor', $chart_id ); ?>
342
343 </div>
344 <?php
345
346 }
347
348 /**
349 * Show the text area editor.
350 *
351 * @access public
352 */
353 public static function _renderTextEditor( $args ) {
354 $chart_id = $args[1];
355 $csv = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA_AS, array(), $chart_id, 'csv-display' );
356 $data = '';
357 if ( ! empty( $csv ) && isset( $csv['string'] ) ) {
358 $data = str_replace( PHP_EOL, "\n", stripslashes( $csv['string'] ) );
359 }
360 ?>
361 <div class="viz-simple-editor-type viz-text-editor">
362 <textarea id="edited_text"><?php echo $data; ?></textarea>
363 </div>
364 <?php
365 }
366
367 /**
368 * Show the JSON endpoint's parsed table.
369 *
370 * @access public
371 */
372 public static function _renderEditorTable( $args ) {
373 $data = $args[1];
374 $chart_id = $args[2];
375 $class = $args[3];
376 $echo = $args[4];
377 $editable_data = $args[5];
378 $series = get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true );
379 $headers = array();
380
381 if ( is_null( $data ) ) {
382 foreach ( $series as $column ) {
383 $headers[] = $column['label'];
384 }
385 $chart = get_post( $chart_id );
386 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
387 $data = Visualizer_Module::get_chart_data( $chart, $type );
388 } else {
389 $headers = array_keys( $data[0] );
390 }
391
392 $classes = implode( ' ', array( 'viz-editor-table', $class ) );
393
394 if ( $series ) {
395 $temp = $series;
396 $series = array();
397 foreach ( $temp as $array ) {
398 $series[ $array['label'] ] = $array['type'];
399 }
400 }
401 if ( ! $echo ) {
402 ob_start();
403 }
404 ?>
405 <table cellspacing="0" width="100%" class="results cell-border stripe <?php echo $classes; ?>">
406 <thead>
407 <tr>
408 <th><?php _e( 'Label', 'visualizer' ); ?></th>
409 <?php
410 foreach ( $headers as $header ) {
411 if ( $editable_data ) {
412 echo '<th><input type="text" name="header[]" value="' . esc_attr( $header ) . '"></th>';
413 } else {
414 echo '<th>' . $header . '</th>';
415 }
416 }
417 ?>
418 </tr>
419 </thead>
420 <tbody>
421 <tr>
422 <th><?php _e( 'Data Type', 'visualizer' ); ?></th>
423 <?php
424 $index = 0;
425 foreach ( $headers as $header ) {
426 echo '<td>';
427 if ( $editable_data ) {
428 echo '<select name="type[]">';
429 } else {
430 echo '<input name="header[]" type="hidden" value="' . $header . '">';
431 echo '<select name="type[' . $header . ']" class="viz-select-data-type">';
432 }
433 echo '<option value="" title="' . __( 'Exclude from chart', 'visualizer' ) . '">' . __( 'Exclude', 'visualizer' ) . '</option>';
434 echo '<option value="0" disabled title="' . __( 'Include in chart and select data type', 'visualizer' ) . '">--' . __( 'OR', 'visualizer' ) . '--</option>';
435
436 foreach ( Visualizer_Source::getAllowedTypes() as $type ) {
437 $selected = array_key_exists( $header, $series ) && $type === $series[ $header ] ? 'selected' : '';
438 echo '<option value="' . $type . '" ' . $selected . '>' . $type . '</option>';
439 }
440
441 echo '</select></td>';
442 }
443 ?>
444 </tr>
445 <?php
446 // for remote sources, the data exists inside 'data'.
447 if ( array_key_exists( 'data', $data ) ) {
448 $data = $data['data'];
449 }
450
451 foreach ( $data as $row ) {
452 echo '<tr>';
453 echo '<th>' . __( 'Value', 'visualizer' ) . '</th>';
454 $index = 0;
455 if ( empty( $row ) ) {
456 echo '<td></td>';
457 continue;
458 }
459 foreach ( array_values( $row ) as $value ) {
460 if ( $editable_data ) {
461 echo '<td><input type="text" name="data' . $index++ . '[]" value="' . esc_attr( stripslashes( $value ) ) . '"></td>';
462 } else {
463 echo '<td>' . ( is_array( $value ) ? __( 'Invalid Data', 'visualizer' ) : $value ) . '</td>';
464 }
465 }
466
467 echo '</tr>';
468 }
469 ?>
470 </tbody>
471 </table>
472 <?php
473 if ( ! $echo ) {
474 return ob_get_clean();
475 }
476 }
477
478 /**
479 * Generates the permissions tab.
480 *
481 * @access private
482 */
483 private static function _renderPermissions( $args ) {
484 $chart_id = $args[1];
485
486 // ignore for unit tests because Travis throws the error "Indirect modification of overloaded property Visualizer_Render_Page_Data::$permissions has no effect".
487 if ( defined( 'WP_TESTS_DOMAIN' ) ) {
488 return;
489 }
490
491 $permissions = apply_filters( 'visualizer_pro_get_permissions', null, $chart_id );
492 if ( is_array( $permissions ) ) {
493 $permissions = $permissions['permissions'];
494 } else {
495 $permissions = array();
496 }
497 if ( ! isset( $permissions['read'] ) ) {
498 $permissions['read'] = 'all';
499 }
500 if ( ! isset( $permissions['edit'] ) ) {
501 $permissions['edit'] = 'roles';
502 $permissions['edit-specific'] = 'administrator';
503 }
504
505 Visualizer_Render_Sidebar::_renderGroupStart( esc_html__( 'Permissions', 'visualizer' ) . '<span class="dashicons dashicons-lock"></span>', '', apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'chart-permissions' ), 'vz-permissions' );
506 echo '<div style="position: relative">';
507 Visualizer_Render_Sidebar::_renderSectionStart();
508 Visualizer_Render_Sidebar::_renderSectionDescription( esc_html__( 'Configure permissions for the chart.', 'visualizer' ) );
509 Visualizer_Render_Sidebar::_renderSectionEnd();
510
511 Visualizer_Render_Sidebar::_renderSectionStart( esc_html__( 'Who can see this chart?', 'visualizer' ), false );
512 Visualizer_Render_Sidebar::_renderSelectItem(
513 '',
514 'permissions[read]',
515 $permissions['read'],
516 array(
517 'all' => esc_html__( 'All Users', 'visualizer' ),
518 'users' => esc_html__( 'Select Users', 'visualizer' ),
519 'roles' => esc_html__( 'Select Roles', 'visualizer' ),
520 ),
521 '',
522 false,
523 array( 'visualizer-permission', 'visualizer-permission-type', 'visualizer-permission-read' ),
524 array(
525 'permission-type' => 'read',
526 )
527 );
528
529 $options = apply_filters( 'visualizer_pro_get_permissions_data', array(), isset( $permissions['read'] ) ? $permissions['read'] : 'roles' );
530
531 Visualizer_Render_Sidebar::_renderSelectItem(
532 '',
533 'permissions[read-specific][]',
534 isset( $permissions['read-specific'] ) ? $permissions['read-specific'] : array(),
535 $options,
536 '',
537 true,
538 array( 'visualizer-permission', 'visualizer-permission-specific', 'visualizer-permission-read-specific' )
539 );
540 Visualizer_Render_Sidebar::_renderSectionEnd();
541
542 Visualizer_Render_Sidebar::_renderSectionStart( esc_html__( 'Who can edit this chart?', 'visualizer' ), false );
543 Visualizer_Render_Sidebar::_renderSelectItem(
544 '',
545 'permissions[edit]',
546 $permissions['edit'],
547 array(
548 'all' => esc_html__( 'All Users', 'visualizer' ),
549 'users' => esc_html__( 'Select Users', 'visualizer' ),
550 'roles' => esc_html__( 'Select Roles', 'visualizer' ),
551 ),
552 '',
553 false,
554 array( 'visualizer-permission', 'visualizer-permission-type', 'visualizer-permission-edit' ),
555 array(
556 'permission-type' => 'edit',
557 )
558 );
559
560 $options = apply_filters( 'visualizer_pro_get_permissions_data', array(), isset( $permissions['edit'] ) ? $permissions['edit'] : 'roles' );
561
562 Visualizer_Render_Sidebar::_renderSelectItem(
563 '',
564 'permissions[edit-specific][]',
565 isset( $permissions['edit-specific'] ) ? $permissions['edit-specific'] : array(),
566 $options,
567 '',
568 true,
569 array( 'visualizer-permission', 'visualizer-permission-specific', 'visualizer-permission-edit-specific' )
570 );
571 Visualizer_Render_Sidebar::_renderSectionEnd();
572 echo apply_filters( 'visualizer_pro_upsell', '', 'chart-permissions' );
573 echo '</div>';
574 Visualizer_Render_Sidebar::_renderGroupEnd();
575 }
576
577 /**
578 * Show the Settings tab for this chart.
579 *
580 * @access public
581 */
582 public static function _renderTabAdvanced( $args ) {
583 $sidebar = $args[2];
584 ?>
585 <ul class="viz-group-wrapper full-height">
586 <li class="viz-group open" id="vz-chart-settings">
587 <ul class="viz-group-content">
588 <ul class="viz-group-wrapper">
589 <form id="settings-form" action="<?php echo esc_url( add_query_arg( 'nonce', wp_create_nonce() ) ); ?>" method="post">
590 <input type="hidden" id="chart-img" name="chart-img">
591 <?php echo $sidebar; ?>
592 <?php self::_renderPermissions( $args ); ?>
593 <input type="hidden" name="save" value="1">
594 </form>
595 <form id="cancel-form" action="<?php echo esc_url( add_query_arg( 'nonce', wp_create_nonce() ) ); ?>" method="post">
596 <input type="hidden" name="cancel" value="1">
597 </form>
598 </ul>
599 </ul>
600 </li>
601 <?php
602 }
603
604 /**
605 * Show the Docs tab for this chart.
606 *
607 * @access public
608 */
609 public static function _renderTabHelp( $args ) {
610 $chart_id = $args[1];
611 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
612 switch ( $type ) {
613 case 'tabular':
614 $type = 'table';
615 break;
616 case 'polarArea':
617 $type = 'polar-area';
618 break;
619 case 'radar':
620 $type = 'radar-spider';
621 break;
622 }
623
624 $displayType = str_replace( '-', '/', $type );
625 ?>
626 <ul class="viz-group-wrapper full-height">
627 <li class="viz-group open" id="vz-chart-help">
628 <ul class="viz-group-wrapper">
629 <?php
630 // open this tab by default.
631 Visualizer_Render_Sidebar::_renderGroupStart( esc_html__( 'Documentation', 'visualizer' ), '', 'open' );
632 Visualizer_Render_Sidebar::_renderSectionStart( esc_html__( 'General', 'visualizer' ), true );
633 ?>
634 <h4><span class="dashicons dashicons-editor-help"></span><a href="<?php echo VISUALIZER_MAIN_DOC; ?>" target="_blank"><?php _e( 'Main documentation page', 'visualizer' ); ?></a></h4>
635 <h4><span class="dashicons dashicons-media-code"></span><a href="<?php echo VISUALIZER_CODE_SNIPPETS_URL; ?>" target="_blank"><?php _e( 'Custom code snippets', 'visualizer' ); ?></a></h4>
636 <?php
637 Visualizer_Render_Sidebar::_renderSectionEnd();
638 Visualizer_Render_Sidebar::_renderSectionStart( sprintf( __( '%s chart', 'visualizer' ), ucwords( $displayType ) ), true );
639 ?>
640 <h4><span class="dashicons dashicons-video-alt2"></span>&nbsp;<a href="<?php echo str_replace( '#', "$type-chart", VISUALIZER_DEMO_URL ); ?>" target="_blank"><?php _e( 'View demo', 'visualizer' ); ?></a></h4>
641 <h4><span class="dashicons dashicons-search"></span><a href="<?php echo str_replace( '#', $type, VISUALIZER_DOC_COLLECTION ); ?>" target="_blank"><?php echo sprintf( __( 'Articles containing "%s"', 'visualizer' ), $displayType ); ?></a></h4>
642 <?php
643 Visualizer_Render_Sidebar::_renderSectionEnd();
644 Visualizer_Render_Sidebar::_renderGroupEnd();
645 ?>
646 </ul>
647 </li>
648 </ul>
649
650 <?php
651 }
652
653 /**
654 * Show the Data Source tab for this chart.
655 *
656 * @access public
657 */
658 public static function _renderTabBasic( $args ) {
659 $chart_id = $args[1];
660
661 $upload_link = esc_url(
662 add_query_arg(
663 array(
664 'action' => Visualizer_Plugin::ACTION_UPLOAD_DATA,
665 'nonce' => wp_create_nonce(),
666 'chart' => $chart_id,
667 ),
668 admin_url( 'admin-ajax.php' )
669 )
670 );
671
672 // this will allow us to open the correct source tab by default.
673 $source_of_chart = strtolower( get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
674 // both import from wp and import from db have the same source so we need to differentiate.
675 $filter_config = get_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG, true );
676 // if filter config is present, then its import from wp.
677 if ( ! empty( $filter_config ) ) {
678 $source_of_chart .= '_wp';
679 }
680 $editor_type = get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, true );
681 if ( $editor_type ) {
682 $source_of_chart = 'visualizer_source_manual';
683 }
684
685 $type = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true );
686 $lib = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, true );
687 ?>
688 <ul class="viz-group-wrapper full-height">
689 <li class="viz-group open" id="vz-chart-source">
690 <ul class="viz-group-content">
691 <ul class="viz-group-wrapper">
692 <!-- import from file -->
693 <li class="viz-group visualizer_source_csv">
694 <h2 class="viz-group-title viz-sub-group visualizer-src-tab"><?php _e( 'Import data from file', 'visualizer' ); ?></h2>
695 <div class="viz-group-content">
696 <p class="viz-group-description"><?php esc_html_e( 'Select and upload your data CSV file here. The first row of the CSV file should contain the column headings. The second one should contain series type (string, number, boolean, date, datetime, timeofday).', 'visualizer' ); ?></p>
697 <p class="viz-group-description viz-info-msg"><b><?php echo sprintf( __( 'If you are unsure about how to format your data CSV then please take a look at this sample: %1$s %2$s%3$s. If you are using non-English characters, please make sure you save the file in UTF-8 encoding.', 'visualizer' ), '<a href="' . VISUALIZER_ABSURL . 'samples/' . $type . '.csv" target="_blank">', $type, '.csv</a>' ); ?></b></p>
698 <form id="vz-csv-file-form" action="<?php echo $upload_link; ?>" method="post"
699 target="thehole" enctype="multipart/form-data">
700 <input type="hidden" id="remote-data" name="remote_data">
701 <div class="">
702 <input type="file" id="csv-file" name="local_data">
703 </div>
704 <input type="button" class="button button-primary" id="vz-import-file"
705 value="<?php _e( 'Import', 'visualizer' ); ?>">
706 </form>
707 </div>
708 </li>
709 <!-- import from url -->
710 <li class="viz-group visualizer-import-url visualizer_source_csv_remote visualizer_source_json">
711 <h2 class="viz-group-title viz-sub-group visualizer-src-tab"><?php _e( 'Import data from URL', 'visualizer' ); ?></h2>
712 <ul class="viz-group-content">
713 <!-- import from csv url -->
714 <li class="viz-subsection">
715 <span class="viz-section-title"><?php _e( 'Import from CSV', 'visualizer' ); ?></span>
716 <div class="viz-section-items section-items">
717 <p class="viz-group-description"><?php echo sprintf( __( 'You can use this to import data from a remote CSV file or %1$sGoogle Spreadsheet%2$s.', 'visualizer' ), '<a href="https://docs.themeisle.com/article/607-how-can-i-populate-data-from-google-spreadsheet" target="_blank" >', '</a>' ); ?> </p>
718 <p class="viz-group-description viz-info-msg"><b><?php echo sprintf( __( 'If you are unsure about how to format your data CSV then please take a look at this sample: %1$s %2$s%3$s. If you are using non-English characters, please make sure you save the file in UTF-8 encoding.', 'visualizer' ), '<a href="' . VISUALIZER_ABSURL . 'samples/' . $type . '.csv" target="_blank">', $type, '.csv</a>' ); ?></b></p>
719 <form id="vz-one-time-import" action="<?php echo $upload_link; ?>" method="post"
720 target="thehole" enctype="multipart/form-data">
721 <div class="remote-file-section">
722 <input type="url" id="vz-schedule-url" name="remote_data" value="<?php echo esc_attr( get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_URL, true ) ); ?>" placeholder="<?php esc_html_e( 'Please enter the URL of CSV file', 'visualizer' ); ?>" class="visualizer-input visualizer-remote-url">
723 </div>
724 <select name="vz-import-time" id="vz-import-time" class="visualizer-select">
725 <?php
726 $hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_SCHEDULE, true );
727 $schedules = apply_filters(
728 'visualizer_chart_schedules', array(
729 '-1' => __( 'One-time', 'visualizer' ),
730 ),
731 'csv',
732 $chart_id
733 );
734 foreach ( $schedules as $num => $name ) {
735 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
736 $extra = $num == $hours ? 'selected' : '';
737 ?>
738 <option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
739 <?php
740 }
741 do_action( 'visualizer_chart_schedules_spl', 'csv', $chart_id, 1 );
742 ?>
743 </select>
744
745 <?php
746 if ( ! Visualizer_Module::is_pro() ) {
747 ?>
748 <input type="button" id="view-remote-file" class="button button-primary" value="<?php _e( 'Import', 'visualizer' ); ?>">
749 <?php
750 } else {
751 ?>
752 <input type="button" id="vz-save-schedule" class="button button-primary" value="<?php _e( 'Import & Save schedule', 'visualizer' ); ?>">
753 <?php
754 }
755 ?>
756 </form>
757 </div>
758 </li>
759 <!-- import from json url -->
760 <li class="viz-subsection">
761 <span class="viz-section-title visualizer_source_json"><?php _e( 'Import from JSON', 'visualizer' ); ?></span>
762 <div class="viz-section-items section-items">
763 <p class="viz-group-description"><?php _e( 'You can choose here to import/synchronize your chart data with a remote JSON source. For more info check <a href="https://docs.themeisle.com/article/1052-how-to-generate-charts-from-json-data-rest-endpoints" target="_blank" >this</a> tutorial', 'visualizer' ); ?></p>
764 <form id="vz-import-json" action="<?php echo $upload_link; ?>" method="post" target="thehole" enctype="multipart/form-data">
765 <div class="remote-file-section">
766 <?php
767 $bttn_label = 'visualizer_source_json' === $source_of_chart ? __( 'Modify Parameters', 'visualizer' ) : __( 'Create Parameters', 'visualizer' );
768 if ( Visualizer_Module::is_pro() ) {
769 ?>
770 <p class="viz-group-description"><?php _e( 'How often do you want to check the URL', 'visualizer' ); ?></p>
771 <select name="time" id="vz-json-time" class="visualizer-select json-form-element" data-chart="<?php echo $chart_id; ?>">
772 <?php
773 $hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, true );
774 $schedules = apply_filters(
775 'visualizer_chart_schedules', array(
776 '-1' => __( 'One-time', 'visualizer' ),
777 ),
778 'json',
779 $chart_id
780 );
781 foreach ( $schedules as $num => $name ) {
782 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
783 $extra = $num == $hours ? 'selected' : '';
784 ?>
785 <option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
786 <?php
787 }
788 do_action( 'visualizer_chart_schedules_spl', 'json', $chart_id, 1 );
789 ?>
790 </select>
791 <?php
792 }
793 ?>
794 </div>
795
796 <input type="button" id="json-chart-button" class="button button-secondary show-chart-toggle"
797 value="<?php echo $bttn_label; ?>" data-current="chart"
798 data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>"
799 data-t-chart="<?php echo $bttn_label; ?>">
800 <?php
801 if ( Visualizer_Module::is_pro() ) {
802 ?>
803 <input type="button" id="json-chart-save-button" class="button button-primary "
804 value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
805 <?php
806 }
807 ?>
808 </form>
809 </div>
810 </li>
811 </ul>
812 </li>
813 <!-- import from chart -->
814 <li class="viz-group viz-import-from-other <?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature' ); ?>">
815 <h2 class="viz-group-title viz-sub-group"
816 data-current="chart"><?php _e( 'Import from other chart', 'visualizer' ); ?><span
817 class="dashicons dashicons-lock"></span></h2>
818 <div class="viz-group-content edit-data-content">
819 <div>
820 <p class="viz-group-description"><?php _e( 'You can import here data from your previously created charts', 'visualizer' ); ?></p>
821 <form>
822 <select name="vz-import-from-chart" id="chart-id" class="visualizer-select">
823 <?php
824 $fetch_link = esc_url(
825 add_query_arg(
826 array(
827 'action' => Visualizer_Module::is_pro() ? Visualizer_Pro::ACTION_FETCH_DATA : '',
828 'nonce' => wp_create_nonce(),
829 ),
830 admin_url( 'admin-ajax.php' )
831 )
832 );
833 $query_args_charts = array(
834 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
835 'posts_per_page' => 300,
836 'no_found_rows' => true,
837 );
838 $charts = array();
839 $query = new WP_Query( $query_args_charts );
840 while ( $query->have_posts() ) {
841 $chart = $query->next_post();
842 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
843
844 $title = '#' . $chart->ID;
845 if ( ! empty( $settings['title'] ) ) {
846 $title = $settings['title'];
847 }
848 // for ChartJS, title is an array.
849 if ( is_array( $title ) && isset( $title['text'] ) ) {
850 $title = $title['text'];
851 }
852 if ( empty( $title ) ) {
853 $title = '#' . $chart->ID;
854 }
855
856 ?>
857 <option value="<?php echo $chart->ID; ?>"><?php echo $title; ?></option>
858 <?php
859 }
860 ?>
861
862 </select>
863 </form>
864 <input type="button" id="existing-chart" class="button button-primary"
865 value="<?php _e( 'Import Chart', 'visualizer' ); ?>"
866 data-viz-link="<?php echo $fetch_link; ?>">
867 <?php echo apply_filters( 'visualizer_pro_upsell', '' ); ?>
868 </div>
869 </div>
870 </li>
871
872 <?php
873 $save_filter = esc_url(
874 add_query_arg(
875 array(
876 'action' => Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY,
877 'security' => wp_create_nonce( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION ),
878 'chart' => $chart_id,
879 ), admin_url( 'admin-ajax.php' )
880 )
881 );
882 ?>
883 <!-- import from WordPress -->
884 <li class="viz-group visualizer_source_query_wp <?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'import-wp' ); ?> ">
885 <h2 class="viz-group-title viz-sub-group"><?php _e( 'Import from WordPress', 'visualizer' ); ?><span
886 class="dashicons dashicons-lock"></span></h2>
887 <div class="viz-group-content edit-data-content">
888 <div>
889 <p class="viz-group-description"><?php _e( 'You can import data from WordPress here.', 'visualizer' ); ?></p>
890 <form id="vz-filter-wizard" action="<?php echo $save_filter; ?>" method="post" target="thehole">
891 <p class="viz-group-description"><?php _e( 'How often do you want to refresh the data from WordPress.', 'visualizer' ); ?></p>
892 <select name="refresh" id="vz-filter-import-time" class="visualizer-select">
893 <?php
894 $bttn_label = 'visualizer_source_query_wp' === $source_of_chart ? __( 'Modify Filter', 'visualizer' ) : __( 'Create Filter', 'visualizer' );
895 $hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, true );
896 $schedules = apply_filters(
897 'visualizer_chart_schedules', array(
898 '-1' => __( 'One-time', 'visualizer' ),
899 ),
900 'wp',
901 $chart_id
902 );
903 foreach ( $schedules as $num => $name ) {
904 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
905 $extra = $num == $hours ? 'selected' : '';
906 ?>
907 <option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
908 <?php
909 }
910 do_action( 'visualizer_chart_schedules_spl', 'wp', $chart_id, 1 );
911 ?>
912 </select>
913
914 <input type="button" id="filter-chart-button" class="button button-secondary show-chart-toggle" value="<?php echo $bttn_label; ?>" data-current="chart" data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>" data-t-chart="<?php echo $bttn_label; ?>">
915 <input type="button" id="db-filter-save-button" class="button button-primary" value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
916 <?php echo apply_filters( 'visualizer_pro_upsell', '', 'db-query' ); ?>
917 </form>
918 <?php echo apply_filters( 'visualizer_pro_upsell', '', 'import-wp' ); ?>
919 </div>
920 </div>
921 </li>
922
923 <?php
924 $save_query = esc_url(
925 add_query_arg(
926 array(
927 'action' => Visualizer_Plugin::ACTION_SAVE_DB_QUERY,
928 'security' => wp_create_nonce( Visualizer_Plugin::ACTION_SAVE_DB_QUERY . Visualizer_Plugin::VERSION ),
929 'chart' => $chart_id,
930 ), admin_url( 'admin-ajax.php' )
931 )
932 );
933 ?>
934 <!-- import from db -->
935 <li class="viz-group visualizer_source_query <?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'db-query' ); ?>">
936 <h2 class="viz-group-title viz-sub-group"><?php _e( 'Import from database', 'visualizer' ); ?><span
937 class="dashicons dashicons-lock"></span></h2>
938 <div class="viz-group-content edit-data-content">
939 <div>
940 <p class="viz-group-description"><?php _e( 'You can import data from the database here.', 'visualizer' ); ?></p>
941 <form id="vz-db-wizard" action="<?php echo $save_query; ?>" method="post" target="thehole">
942 <p class="viz-group-description"><?php _e( 'How often do you want to refresh the data from the database.', 'visualizer' ); ?></p>
943 <select name="refresh" id="vz-db-import-time" class="visualizer-select">
944 <?php
945 $bttn_label = 'visualizer_source_query' === $source_of_chart ? __( 'Modify Query', 'visualizer' ) : __( 'Create Query', 'visualizer' );
946 $hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, true );
947 $schedules = apply_filters(
948 'visualizer_chart_schedules', array(
949 '-1' => __( 'One-time', 'visualizer' ),
950 ),
951 'db',
952 $chart_id
953 );
954 foreach ( $schedules as $num => $name ) {
955 // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
956 $extra = $num == $hours ? 'selected' : '';
957 ?>
958 <option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
959 <?php
960 }
961 do_action( 'visualizer_chart_schedules_spl', 'db', $chart_id, 1 );
962 ?>
963 </select>
964 <input type="hidden" name="params" id="viz-db-wizard-params">
965
966 <input type="button" id="db-chart-button" class="button button-secondary show-chart-toggle" value="<?php echo $bttn_label; ?>" data-current="chart" data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>" data-t-chart="<?php echo $bttn_label; ?>">
967 <input type="button" id="db-chart-save-button" class="button button-primary" value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
968 <?php echo apply_filters( 'visualizer_pro_upsell', '', 'db-query' ); ?>
969 </form>
970 </div>
971 </div>
972 </li>
973
974 <!-- manual -->
975 <li class="viz-group visualizer_source_manual <?php echo ! Visualizer_Module_Admin::proFeaturesLocked() ? apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'db-query' ) : ''; ?>">
976 <h2 class="viz-group-title viz-sub-group visualizer-editor-tab" data-current="chart"><?php _e( 'Manual Data', 'visualizer' ); ?>
977 <span class="dashicons dashicons-lock"></span>
978 </h2>
979 <div class="viz-group-content edit-data-content">
980 <form id="editor-form" action="<?php echo $upload_link; ?>" method="post" target="thehole">
981 <input type="hidden" id="chart-data" name="chart_data">
982 <input type="hidden" id="chart-data-src" name="chart_data_src">
983
984 <?php if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) { ?>
985 <div>
986 <p class="viz-group-description viz-editor-selection">
987 <?php _e( 'Use the', 'visualizer' ); ?>
988 <select name="editor-type" id="viz-editor-type">
989 <?php
990 if ( empty( $editor_type ) ) {
991 $editor_type = Visualizer_Module::is_pro() ? 'excel' : 'text';
992 }
993 foreach ( apply_filters( 'visualizer_editors', array( 'text' => __( 'Text', 'visualizer' ), 'table' => __( 'Simple', 'visualizer' ) ) ) as $e_type => $e_label ) {
994 ?>
995 <option value="<?php echo $e_type; ?>" <?php selected( $editor_type, $e_type ); ?> ><?php echo $e_label; ?></option>
996 <?php } ?>
997 </select>
998 <?php _e( 'editor to manually edit the chart data.', 'visualizer' ); ?>
999 </p>
1000 <input type="button" id="editor-undo" class="button button-secondary" style="display: none" value="<?php _e( 'Undo Changes', 'visualizer' ); ?>">
1001 <input type="button" id="editor-button" class="button button-primary "
1002 value="<?php _e( 'Edit Data', 'visualizer' ); ?>" data-current="chart"
1003 data-t-editor="<?php _e( 'Show Chart', 'visualizer' ); ?>"
1004 data-t-chart="<?php _e( 'Edit Data', 'visualizer' ); ?>"
1005 >
1006 <p class="viz-group-description viz-info-msg"><?php echo sprintf( __( 'Please make sure you click \'Show Chart\' before you save the chart.', 'visualizer' ) ); ?></p>
1007 </div>
1008 <?php } else { ?>
1009 <input type="button" id="editor-undo" class="button button-secondary" style="display: none" value="<?php _e( 'Undo Changes', 'visualizer' ); ?>">
1010 <input type="button" id="editor-chart-button" class="button button-primary "
1011 value="<?php _e( 'View Editor', 'visualizer' ); ?>" data-current="chart"
1012 data-t-editor="<?php _e( 'Show Chart', 'visualizer' ); ?>"
1013 data-t-chart="<?php _e( 'View Editor', 'visualizer' ); ?>"
1014 >
1015 <p class="viz-group-description viz-info-msg"><?php echo sprintf( __( 'Please make sure you click \'Show Chart\' before you save the chart.', 'visualizer' ) ); ?></p>
1016 <?php } ?>
1017 <?php
1018 if ( ! Visualizer_Module_Admin::proFeaturesLocked() ) {
1019 echo apply_filters( 'visualizer_pro_upsell', '', 'db-query' );
1020 }
1021 ?>
1022 </form>
1023 </div>
1024 </li>
1025 </ul>
1026 </li>
1027 </ul>
1028 </ul>
1029
1030 <span id="visualizer-chart-id" data-id="<?php echo $chart_id; ?>" data-chart-source="<?php echo esc_attr( $source_of_chart ); ?>" data-chart-type="<?php echo esc_attr( $type ); ?>" data-chart-lib="<?php echo esc_attr( $lib ); ?>"></span>
1031 <iframe id="thehole" name="thehole"></iframe>
1032 <?php
1033 }
1034 }
1035