Skip to content

Commit a826556

Browse files
Merge branch 'development' of https://github.com/codeinwp/visualizer into issue-393
2 parents 6141352 + 3df57c4 commit a826556

File tree

14 files changed

+222
-46
lines changed

14 files changed

+222
-46
lines changed

classes/Visualizer/Module/Chart.php

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public function __construct( Visualizer_Plugin $plugin ) {
6161

6262
$this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
6363
$this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_DB_QUERY, 'saveQuery' );
64+
$this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
6465
}
6566

6667
/**
@@ -236,6 +237,34 @@ public function deleteChart() {
236237
exit;
237238
}
238239

240+
/**
241+
* Delete charts that are still in auto-draft mode.
242+
*/
243+
private function deleteOldCharts() {
244+
$query = new WP_Query(
245+
array(
246+
'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
247+
'post_status' => 'auto-draft',
248+
'fields' => 'ids',
249+
'update_post_meta_cache' => false,
250+
'update_post_term_cache' => false,
251+
'posts_per_page' => 50,
252+
'date_query' => array(
253+
array(
254+
'before' => 'today',
255+
),
256+
),
257+
)
258+
);
259+
260+
if ( $query->have_posts() ) {
261+
$ids = array();
262+
while ( $query->have_posts() ) {
263+
wp_delete_post( $query->next_post(), true );
264+
}
265+
}
266+
}
267+
239268
/**
240269
* Renders appropriate page for chart builder. Creates new auto draft chart
241270
* if no chart has been specified.
@@ -249,6 +278,7 @@ public function renderChartPages() {
249278
// check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
250279
$chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
251280
if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
281+
$this->deleteOldCharts();
252282
$default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
253283
$source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
254284
$source->fetch();
@@ -592,7 +622,9 @@ public function uploadData() {
592622

593623
if ( ! isset( $_POST['chart_data_src'] ) || Visualizer_Plugin::CF_SOURCE_FILTER !== $_POST['chart_data_src'] ) {
594624
// delete the filters in case this chart is being uploaded from other data sources
595-
delete_post_meta( $chart_id, 'visualizer-filter-config' );
625+
delete_post_meta( $chart_id, Visualizer_Plugin::CF_FILTER_CONFIG );
626+
delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_FILTER_CONFIG );
627+
delete_post_meta( $chart_id, '__transient-' . Visualizer_Plugin::CF_DB_QUERY );
596628

597629
// delete "import from db" specific parameters.
598630
delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
@@ -610,7 +642,7 @@ public function uploadData() {
610642
} elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
611643
$source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
612644
} elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
613-
$source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '' );
645+
$source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '', $chart_id, $_POST );
614646
} else {
615647
$render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
616648
}
@@ -847,4 +879,33 @@ public function saveQuery() {
847879
defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
848880
}
849881
}
882+
883+
884+
/**
885+
* Saves the filter query and the schedule.
886+
*
887+
* @access public
888+
*/
889+
public function saveFilter() {
890+
check_ajax_referer( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION, 'security' );
891+
892+
$chart_id = filter_input(
893+
INPUT_GET,
894+
'chart',
895+
FILTER_VALIDATE_INT,
896+
array(
897+
'options' => array(
898+
'min_range' => 1,
899+
),
900+
)
901+
);
902+
903+
$hours = $_POST['refresh'];
904+
905+
do_action( 'visualizer_save_filter', $chart_id, $hours );
906+
907+
if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
908+
defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
909+
}
910+
}
850911
}

classes/Visualizer/Module/Setup.php

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function __construct( Visualizer_Plugin $plugin ) {
4545
register_activation_hook( VISUALIZER_BASEFILE, array( $this, 'activate' ) );
4646
register_deactivation_hook( VISUALIZER_BASEFILE, array( $this, 'deactivate' ) );
4747
$this->_addAction( 'visualizer_schedule_refresh_db', 'refreshDbChart' );
48-
$this->_addFilter( 'visualizer_schedule_refresh_chart', 'refresh_db_for_chart', null, 10, 3 );
48+
$this->_addFilter( 'visualizer_schedule_refresh_chart', 'refresh_db_for_chart', 10, 3 );
4949

5050
$this->_addAction( 'activated_plugin', 'onActivation' );
5151
$this->_addAction( 'init', 'setupCustomPostTypes' );
@@ -150,6 +150,10 @@ public function activate() {
150150
* On activation of the plugin
151151
*/
152152
public function onActivation( $plugin ) {
153+
if ( defined( 'TI_UNIT_TESTING' ) ) {
154+
return;
155+
}
156+
153157
if ( $plugin === VISUALIZER_BASENAME ) {
154158
wp_redirect( admin_url( 'upload.php?page=' . Visualizer_Plugin::NAME ) );
155159
exit();
@@ -165,12 +169,11 @@ public function deactivate() {
165169
}
166170

167171
/**
168-
* Refresh the specific chart from the db. This is mostly for charts that have 0 refresh time i.e. live data.
172+
* Refresh the specific chart from the db.
169173
*
170174
* @param WP_Post $chart The chart object.
171175
* @param int $chart_id The chart id.
172-
* @param bool $force If this is true, then the chart data will be refreshed no matter if the chart requests live data or cached data.
173-
* If false, data will be refreshed only if the chart requests live data.
176+
* @param bool $force If this is true, then the chart data will be force refreshed. If false, data will be refreshed only if the chart requests live data.
174177
*
175178
* @access public
176179
*/
@@ -183,6 +186,16 @@ public function refresh_db_for_chart( $chart, $chart_id, $force = false ) {
183186
$chart = get_post( $chart_id );
184187
}
185188

189+
if ( ! $chart ) {
190+
return $chart;
191+
}
192+
193+
// check if the source is correct.
194+
$source = get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true );
195+
if ( $source !== 'Visualizer_Source_Query' ) {
196+
return $chart;
197+
}
198+
186199
// check if its a live-data chart or a cached-data chart.
187200
if ( ! $force ) {
188201
$hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, true );
@@ -192,15 +205,10 @@ public function refresh_db_for_chart( $chart, $chart_id, $force = false ) {
192205
}
193206
}
194207

195-
// check if the source is correct.
196-
$source = get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true );
197-
if ( $source !== 'Visualizer_Source_Query_Params' ) {
198-
return $chart;
199-
}
200-
201208
$params = get_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, true );
202209
$source = new Visualizer_Source_Query( $params );
203210
$source->fetch( false );
211+
204212
$error = $source->get_error();
205213
if ( empty( $error ) ) {
206214
update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
@@ -242,7 +250,8 @@ public function refreshDbChart() {
242250
continue;
243251
}
244252

245-
$this->refresh_db_for_chart( null, $chart_id, false );
253+
// if the time is nigh, we force an update.
254+
$this->refresh_db_for_chart( null, $chart_id, true );
246255
$hours = get_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, true );
247256
$new_schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
248257
}

classes/Visualizer/Plugin.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
class Visualizer_Plugin {
2929

3030
const NAME = 'visualizer';
31-
const VERSION = '3.1.2';
31+
const VERSION = '3.1.3';
3232

3333
// custom post types
3434
const CPT_VISUALIZER = 'visualizer';
@@ -41,6 +41,7 @@ class Visualizer_Plugin {
4141
const CF_SETTINGS = 'visualizer-settings';
4242

4343
const CF_SOURCE_FILTER = 'visualizer-source-filter';
44+
const CF_FILTER_CONFIG = 'visualizer-filter-config';
4445

4546
// custom actions
4647
const ACTION_GET_CHARTS = 'visualizer-get-charts';
@@ -61,6 +62,7 @@ class Visualizer_Plugin {
6162
*/
6263
const ACTION_FETCH_DB_DATA = 'visualizer-fetch-db-data';
6364
const ACTION_SAVE_DB_QUERY = 'visualizer-save-db-query';
65+
const ACTION_SAVE_FILTER_QUERY = 'visualizer-save-filter-query';
6466

6567
// custom filters
6668
const FILTER_CHART_WRAPPER_CLASS = 'visualizer-chart-wrapper-class';

classes/Visualizer/Render/Page/Data.php

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ protected function _renderSidebarContent() {
7676

7777
// this will allow us to open the correct source tab by default.
7878
$source_of_chart = strtolower( get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_SOURCE, true ) );
79+
// both import from wp and import from db have the same source so we need to differentiate.
80+
$filter_config = get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_FILTER_CONFIG, true );
81+
// if filter config is present, then its import from wp.
82+
if ( ! empty( $filter_config ) ) {
83+
$source_of_chart .= '_wp';
84+
}
7985
$type = get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
8086
?>
8187
<span id="visualizer-chart-id" data-id="<?php echo $this->chart->ID; ?>" data-chart-source="<?php echo $source_of_chart; ?>" data-chart-type="<?php echo $type; ?>"></span>
@@ -216,16 +222,49 @@ class="dashicons dashicons-lock"></span></h2>
216222
</div>
217223
</li>
218224

219-
<li class="viz-group <?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'schedule-chart' ); ?> ">
225+
<?php
226+
$save_filter = add_query_arg(
227+
array(
228+
'action' => Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY,
229+
'security' => wp_create_nonce( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY . Visualizer_Plugin::VERSION ),
230+
'chart' => $this->chart->ID,
231+
), admin_url( 'admin-ajax.php' )
232+
);
233+
?>
234+
<li class="viz-group visualizer_source_query_wp <?php echo apply_filters( 'visualizer_pro_upsell_class', 'only-pro-feature', 'schedule-chart' ); ?> ">
220235
<h2 class="viz-group-title viz-sub-group"><?php _e( 'Import from WordPress', 'visualizer' ); ?><span
221236
class="dashicons dashicons-lock"></span></h2>
222237
<div class="viz-group-content edit-data-content">
223238
<div>
224239
<p class="viz-group-description"><?php _e( 'You can import data from WordPress here.', 'visualizer' ); ?></p>
225-
<input type="button" id="filter-chart-button" class="button button-primary "
226-
value="<?php _e( 'Create Filters', 'visualizer' ); ?>" data-current="chart"
227-
data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>"
228-
data-t-chart="<?php _e( 'Create Filters', 'visualizer' ); ?>">
240+
<form id="vz-filter-wizard" action="<?php echo $save_filter; ?>" method="post" target="thehole">
241+
<p class="viz-group-description"><?php _e( 'How often do you want to refresh the data from WordPress.', 'visualizer' ); ?></p>
242+
<select name="refresh" id="vz-filter-import-time" class="visualizer-select">
243+
<?php
244+
$bttn_label = 'visualizer_source_query_wp' === $source_of_chart ? __( 'Modify Filter', 'visualizer' ) : __( 'Create Filter', 'visualizer' );
245+
$hours = get_post_meta( $this->chart->ID, Visualizer_Plugin::CF_DB_SCHEDULE, true );
246+
$schedules = apply_filters(
247+
'visualizer_schedules', array(
248+
'0' => __( 'Live', 'visualizer' ),
249+
'1' => __( 'Each hour', 'visualizer' ),
250+
'12' => __( 'Each 12 hours', 'visualizer' ),
251+
'24' => __( 'Each day', 'visualizer' ),
252+
'72' => __( 'Each 3 days', 'visualizer' ),
253+
)
254+
);
255+
foreach ( $schedules as $num => $name ) {
256+
$extra = $num == $hours ? 'selected' : '';
257+
?>
258+
<option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
259+
<?php
260+
}
261+
?>
262+
</select>
263+
264+
<input type="button" id="filter-chart-button" class="button button-secondary" value="<?php echo $bttn_label; ?>" data-current="chart" data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>" data-t-chart="<?php echo $bttn_label; ?>">
265+
<input type="button" id="db-filter-save-button" class="button button-primary" value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
266+
<?php echo apply_filters( 'visualizer_pro_upsell', '', 'db-query' ); ?>
267+
</form>
229268
<?php echo apply_filters( 'visualizer_pro_upsell', '', 'schedule-chart' ); ?>
230269
</div>
231270
</div>
@@ -264,24 +303,20 @@ class="dashicons dashicons-lock"></span></h2>
264303
foreach ( $schedules as $num => $name ) {
265304
// phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
266305
$extra = $num == $hours ? 'selected' : '';
306+
?>
307+
<option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
308+
<?php
309+
}
267310
?>
268-
<option value="<?php echo $num; ?>" <?php echo $extra; ?>><?php echo $name; ?></option>
269-
<?php
270-
}
271-
?>
272-
</select>
273-
<input type="hidden" name="params" id="viz-db-wizard-params">
274-
275-
<input type="button" id="db-chart-button" class="button button-secondary "
276-
value="<?php echo $bttn_label; ?>" data-current="chart"
277-
data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>"
278-
data-t-chart="<?php echo $bttn_label; ?>">
279-
<input type="button" id="db-chart-save-button" class="button button-primary "
280-
value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
311+
</select>
312+
<input type="hidden" name="params" id="viz-db-wizard-params">
313+
314+
<input type="button" id="db-chart-button" class="button button-secondary" value="<?php echo $bttn_label; ?>" data-current="chart" data-t-filter="<?php _e( 'Show Chart', 'visualizer' ); ?>" data-t-chart="<?php echo $bttn_label; ?>">
315+
<input type="button" id="db-chart-save-button" class="button button-primary" value="<?php _e( 'Save Schedule', 'visualizer' ); ?>">
281316
<?php echo apply_filters( 'visualizer_pro_upsell', '', 'db-query' ); ?>
282-
</form>
283-
</div>
284-
</div>
317+
</form>
318+
</div>
319+
</div>
285320
</li>
286321

287322
<?php

classes/Visualizer/Render/Sidebar.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ protected function _renderAnimationSettings() {
363363
self::_renderCheckboxItem(
364364
esc_html__( 'Animate on startup', 'visualizer' ),
365365
'animation[startup]',
366-
$this->animation['startup'],
366+
isset( $this->animation['startup'] ) ? $this->animation['startup'] : 0,
367367
true,
368368
esc_html__( 'Determines if the chart will animate on the initial draw.', 'visualizer' )
369369
);

classes/Visualizer/Source/Csv.php

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,20 @@ public function __construct( $filename = null ) {
6262
private function _fetchSeries( &$handle ) {
6363
// read column titles
6464
$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;
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+
}
6779

6880
if ( ! $labels || ! $types ) {
6981
return false;
@@ -158,4 +170,45 @@ public function getSourceName() {
158170
return __CLASS__;
159171
}
160172

173+
/**
174+
* Adds support for QueryLanguage https://developers.google.com/chart/interactive/docs/querylanguage
175+
* 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
176+
* to get the subset of data specified by the query
177+
* this will conflate the heading and the type into one value viz. for heading XXX and type string, the value will become "XXX string"
178+
* so we need to split them apart logically
179+
* also the $types variable now contains the first row because the header and the type got conflated
180+
*/
181+
private function _fetchSeriesForGoogleQueryLanguage( $labels, $types = array() ) {
182+
$new_labels = array();
183+
$new_types = array();
184+
$abort = false;
185+
foreach ( $labels as $label ) {
186+
// get the index of the last space
187+
$index = strrpos( $label, ' ' );
188+
if ( $index === false ) {
189+
// no space here? something has gone wrong; abort the entire process.
190+
$abort = true;
191+
break;
192+
}
193+
$type = trim( substr( $label, $index + 1 ) );
194+
if ( ! self::_validateTypes( array( $type ) ) ) {
195+
// some other data type? abort the entire process.
196+
$abort = true;
197+
break;
198+
}
199+
$label = substr( $label, 0, $index );
200+
$new_labels[] = $label;
201+
$new_types[] = $type;
202+
}
203+
if ( ! $abort ) {
204+
$labels = $new_labels;
205+
$types = $new_types;
206+
}
207+
208+
return array(
209+
'abort' => $abort,
210+
'labels' => $labels,
211+
'types' => $types,
212+
);
213+
}
161214
}

0 commit comments

Comments
 (0)