File "FrmProGraphsController.php"
Full path: /home/bud/public_html/swamp/wp-admin/wp-content/plugins/formidable-pro/classes/controllers/FrmProGraphsController.php
File size: 65.59 KB
MIME-type: text/x-php
Charset: utf-8
<?php
if ( ! defined( 'ABSPATH' ) ) {
die( 'You are not allowed to call this page directly.' );
}
class FrmProGraphsController {
/**
* Do the frm-graph shortcode
*
* @param array $atts
* @return string
*/
public static function graph_shortcode( $atts ) {
self::convert_old_atts_to_new_atts( $atts );
self::combine_defaults_and_user_defined_attributes( $atts );
self::format_atts( $atts );
if ( empty( $atts['fields'] ) && empty( $atts['form'] ) ) {
return __( 'You must include a field id or key in your graph shortcode.', 'formidable-pro' );
}
$graph_data = self::generate_google_graph_data( $atts );
$html = self::get_graph_html( $graph_data, $atts );
return $html;
}
/**
* Convert old, deprecated attributes to the new attributes to maintain reverse compatibility
*
* @since 2.02.05
* @param array $atts
*/
private static function convert_old_atts_to_new_atts( &$atts ) {
self::convert_id_parameter_to_fields_parameter( $atts );
if ( isset( $atts['min'] ) ) {
$atts['y_min'] = $atts['min'];
unset( $atts['min'] );
}
if ( isset( $atts['max'] ) ) {
$atts['y_max'] = $atts['max'];
unset( $atts['max'] );
}
self::adjust_deprecated_date_parameters( $atts );
if ( isset( $atts['entry_id'] ) ) {
$atts['entry'] = $atts['entry_id'];
unset( $atts['entry_id'] );
}
if ( isset( $atts['x_order'] ) && ! $atts['x_order'] ) {
$atts['x_order'] = 'field_opts';
}
if ( isset( $atts['include_js'] ) ) {
unset( $atts['include_js'] );
}
if ( isset( $atts['truncate_label'] ) ) {
unset( $atts['truncate_label'] );
}
if ( isset( $atts['response_count'] ) ) {
unset( $atts['response_count'] );
}
}
/**
* Convert the old id and ids parameters to the new fields parameter
*
* @since 2.02.05
* @param array $atts
*/
private static function convert_id_parameter_to_fields_parameter( &$atts ) {
if ( ! is_array( $atts ) ) {
return;
}
$id = '';
if ( ! empty( $atts['id'] ) ) {
$id_array = explode( ',', $atts['id'] );
if ( count( $id_array ) > 1 ) {
esc_html_e( 'Using multiple values in the id graph parameter has been removed as of version 2.02.04', 'formidable-pro' );
}
$id = reset( $id_array ) . ',';
unset( $atts['id'] );
}
$ids = '';
if ( isset( $atts['ids'] ) ) {
if ( $atts['ids'] ) {
$ids = $atts['ids'];
}
unset( $atts['ids'] );
}
if ( ! isset( $atts['fields'] ) ) {
$atts['fields'] = $id;
if ( ! empty( $ids ) ) {
$atts['fields'] .= $ids;
}
}
}
/**
* Convert the deprecated x_start, x_end, start_date, and end_date parameters to the new
* x_greater_than, x_less_than, created_at_greater_than, and created_at_less_than parameters
*
* @since 2.02.05
* @param array $atts
*/
private static function adjust_deprecated_date_parameters( &$atts ) {
if ( isset( $atts['x_start'] ) ) {
$atts['start_date'] = $atts['x_start'];
unset( $atts['x_start'] );
}
if ( isset( $atts['x_end'] ) ) {
$atts['end_date'] = $atts['x_end'];
unset( $atts['x_end'] );
}
if ( ! isset( $atts['start_date'] ) && ! isset( $atts['end_date'] ) ) {
return;
}
if ( isset( $atts['x_axis'] ) ) {
$x_axis_field = self::get_x_axis_field( $atts['x_axis'] );
} else {
$x_axis_field = '';
}
self::convert_old_date_parameters_to_new_parameters( 'start_date', $x_axis_field, $atts );
self::convert_old_date_parameters_to_new_parameters( 'end_date', $x_axis_field, $atts );
}
/**
* Convert start_date and end_date to new parameters
*
* @since 2.02.05
* @param string $old_key
* @param string|object $x_axis_field
* @param array $atts
*/
private static function convert_old_date_parameters_to_new_parameters( $old_key, $x_axis_field, &$atts ) {
if ( isset( $atts[ $old_key ] ) ) {
if ( $old_key === 'start_date' ) {
$operator_text = '_greater_than';
} elseif ( $old_key === 'end_date' ) {
$operator_text = '_less_than';
} else {
return;
}
if ( self::is_date_field( $x_axis_field ) ) {
$atts[ $x_axis_field->id . $operator_text ] = $atts[ $old_key ];
} else {
$atts[ 'created_at' . $operator_text ] = $atts[ $old_key ];
}
unset( $atts[ $old_key ] );
}
}
/**
* Combine the graph defaults with the user-defined attributes
* Removes defaults with a blank value
*
* @since 2.02.05
* @param array $atts
*/
private static function combine_defaults_and_user_defined_attributes( &$atts ) {
$defaults = self::get_graph_defaults();
if ( isset( $atts['type'] ) && $atts['type'] === 'table' && ! isset( $atts['height'] ) ) {
$defaults['height'] = 'auto';
}
$combined_atts = array();
foreach ( $defaults as $k => $value ) {
if ( isset( $atts[ $k ] ) ) {
$combined_atts[ $k ] = $atts[ $k ];
unset( $atts[ $k ] );
} elseif ( $value !== '' || in_array( $k, array( 'fields', 'form' ) ) ) {
$combined_atts[ $k ] = $value;
}
}
$combined_atts['filters'] = $atts;
$atts = $combined_atts;
}
/**
* Get the default graph attributes
*
* @since 2.02.05
* @return array
*/
private static function get_graph_defaults() {
$defaults = array(
'fields' => '',
'form' => '',
'type' => 'column',
'data_type' => 'count',
'limit' => '',
'include_zero' => false,
'created_at_greater_than' => '',
'created_at_less_than' => '',
'group_by' => '',
'user_id' => '',
'entry' => '',
'drafts' => '',
'title' => '',
'title_size' => 14,
'title_font' => '',
'title_bold' => false,
'title_color' => '#666',
'truncate' => 40,
'tooltip_label' => '',
'show_key' => false,
'legend_size' => '',
'legend_position' => '',
'x_axis' => '',
'x_title' => '',
'x_title_size' => 13,
'x_title_color' => '#666',
'x_labels_size' => '',
'x_slanted_text' => true,
'x_text_angle' => 20,
'x_min' => '',
'x_max' => '',
'x_order' => 'default',
'x_show_text_every' => '',
'y_title' => '',
'y_title_size' => 13,
'y_title_color' => '#666',
'y_labels_size' => '',
'y_min' => '',
'y_max' => '',
'pagesize' => 0,
'sort_column' => -1,
'sort_ascending' => true,
'colors' => self::get_default_colors(),
'grid_color' => '#CCC',
'x_grid_color' => '#CCC',
'bg_color' => '#FFFFFF',
'is3d' => false,
'height' => 400,
'width' => 400,
'chart_area' => '',
'is_stacked' => false,
'pie_hole' => '',
'curve_type' => '',
'no_data' => __( 'No data', 'formidable-pro' ),
);
return $defaults;
}
/**
* Get the default graph colors
*
* @since 2.0
*
* @return string
*/
private static function get_default_colors() {
$colors = '#00bbde,#fe6672,#eeb058,#8a8ad6,#ff855c,#00cfbb,#5a9eed,#73d483,#c879bb,#0099b6';
$colors = (string) apply_filters( 'frm_graph_default_colors', $colors );
return $colors;
}
/**
* Format the user-defined attributes
*
* @since 2.02.05
* @param array $atts
*/
private static function format_atts( &$atts ) {
self::convert_field_keys_to_ids( $atts );
if ( ! empty( $atts['fields'] ) ) {
$atts['fields'] = FrmField::getAll( array( 'fi.id' => $atts['fields'] ) );
} elseif ( ! empty( $atts['form'] ) ) {
$atts['form'] = FrmForm::getOne( $atts['form'] );
if ( ! $atts['form'] ) {
return;
}
$atts['form_id'] = $atts['form']->id;
} else {
return;
}
if ( isset( $atts['created_at_greater_than'] ) ) {
$atts['created_at_greater_than'] = FrmAppHelper::replace_quotes( $atts['created_at_greater_than'] );
}
if ( isset( $atts['created_at_less_than'] ) ) {
$atts['created_at_less_than'] = FrmAppHelper::replace_quotes( $atts['created_at_less_than'] );
}
// If limit is set, get only the top results
if ( isset( $atts['limit'] ) ) {
$atts['x_order'] = 'desc';
}
if ( isset( $atts['user_id'] ) ) {
$atts['user_id'] = FrmAppHelper::get_user_id_param( $atts['user_id'] );
}
if ( isset( $atts['drafts'] ) ) {
$atts['is_draft'] = $atts['drafts'];
unset( $atts['drafts'] );
}
self::convert_entry_keys_to_ids( $atts );
}
/**
* Convert field keys to IDs
*
* @since 2.02.05
* @param array $atts
*/
private static function convert_field_keys_to_ids( &$atts ) {
$atts['fields'] = self::convert_keys_to_ids( $atts['fields'], 'field' );
}
/**
* Convert entry keys to IDs
*
* @since 2.02.05
* @param array $atts
*/
private static function convert_entry_keys_to_ids( &$atts ) {
if ( isset( $atts['entry'] ) ) {
$atts['entry_ids'] = self::convert_keys_to_ids( $atts['entry'], 'entry' );
unset( $atts['entry'] );
}
}
/**
* Convert field or entry keys to ids
*
* @since 2.02.05
* @param array $keys
* @param string $type
* @return array
*/
private static function convert_keys_to_ids( $keys, $type ) {
if ( $keys ) {
$keys = explode( ',', $keys );
foreach ( $keys as $i => $k ) {
if ( ! is_numeric( $k ) ) {
if ( 'entry' === $type ) {
$id = FrmEntry::get_id_by_key( $k );
} else {
$id = FrmField::get_id_by_key( $k );
}
if ( $id ) {
$keys[ $i ] = $id;
} else {
unset( $keys[ $i ] );
}
}
}
}
return $keys;
}
/**
* Get the HTML for a graph
*
* @since 2.02.05
* @param array $graph_data
* @param array $atts
* @return string
*/
private static function get_graph_html( $graph_data, $atts ) {
if ( empty( $graph_data['data'] ) ) {
$html = apply_filters( 'frm_no_data_graph', '<div class="frm_no_data_graph">' . $atts['no_data'] . '</div>' );
} else {
$html = '<div id="chart_' . $graph_data['graph_id'] . '" style="height:' . $atts['height'] . ';';
$html .= 'width:' . $atts['width'] . '"></div>';
}
return $html;
}
/**
* Generate the data, options, package, etc. for a google graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function generate_google_graph_data( $atts ) {
global $frm_vars;
self::prepare_frm_vars( $frm_vars );
$type = self::get_graph_type( $atts );
$data = self::get_graph_data( $atts );
$options = self::get_graph_options( $type, $atts );
$graph_package = self::get_graph_package( $type );
$graph_auto_id = count( $frm_vars['google_graphs']['graphs'] ) + 1;
$graph_id = '_frm_' . strtolower( $type ) . $graph_auto_id;
/**
* Filter the ID of the graph.
*
* @since 5.4
*
* @param string $graph_id
* @param array $args {
* @type int $graph_auto_id Graph Auto ID.
* @type string $type Graph type.
* }
*/
$graph_id = apply_filters( 'frm_graph_id', $graph_id, compact( 'graph_auto_id', 'type' ) );
$graph_data = array(
'type' => $type,
'data' => $data,
'options' => $options,
'package' => $graph_package,
'graph_id' => $graph_id,
);
$frm_vars['google_graphs']['graphs'][] = $graph_data;
return $graph_data;
}
/**
* Prepare frm_vars for the graphs to be loaded
*
* @since 2.02.05
* @param array $frm_vars
*/
private static function prepare_frm_vars( &$frm_vars ) {
$frm_vars['forms_loaded'][] = true;
if ( ! isset( $frm_vars['google_graphs'] ) ) {
$frm_vars['google_graphs'] = array();
}
if ( ! isset( $frm_vars['google_graphs']['graphs'] ) ) {
$frm_vars['google_graphs']['graphs'] = array();
}
}
/**
* Get the graph type
*
* @since 2.02.05
* @param array $atts
* @return string
*/
private static function get_graph_type( $atts ) {
$type = strtolower( $atts['type'] );
if ( 'bar' === $type ) {
$type = 'column';
} elseif ( 'hbar' === $type ) {
$type = 'bar';
} elseif ( $type == 'stepped_area' || $type == 'steppedarea' ) {
$type = 'steppedArea';
}
$allowed_types = array(
'pie',
'line',
'column',
'area',
'steppedArea',
'geo',
'bar',
'scatter',
'histogram',
'table',
);
if ( ! in_array( $type, $allowed_types ) ) {
$type = 'column';
}
return $type;
}
/**
* Get the row and column data for a google graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_graph_data( $atts ) {
if ( $atts['form'] ) {
$data = self::get_data_for_form_graph( $atts );
} elseif ( isset( $atts['x_axis'] ) ) {
$data = self::get_data_for_x_axis_graph( $atts );
} elseif ( count( $atts['fields'] ) > 1 ) {
$data = self::get_data_for_multi_field_graph( $atts );
} else {
$data = self::get_data_for_single_field_graph( $atts );
}
self::apply_deprecated_filters();
$data = apply_filters( 'frm_graph_data', $data, $atts );
return $data;
}
/**
* Get the options for a google graph
*
* @param string $type
* @param array $atts
* @return array
*/
private static function get_graph_options( $type, $atts ) {
$options = array();
self::add_title_options( $atts, $options );
self::add_legend_options( $atts, $options );
self::add_tooltip_options( $options );
self::add_size_options( $atts, $options );
self::add_color_options( $atts, $options );
self::add_pie_graph_options( $type, $atts, $options );
self::add_line_graph_options( $type, $atts, $options );
self::add_table_options( $type, $atts, $options );
if ( $type !== 'pie' && $type !== 'geo' ) {
if ( isset( $atts['is_stacked'] ) && $atts['is_stacked'] ) {
$options['isStacked'] = true;
}
self::add_axis_options( $atts, $options );
}
$options = apply_filters( 'frm_google_chart', $options, array( 'atts' => $atts, 'type' => $type ) );
return $options;
}
/**
* Get the package type for a given graph type
*
* @since 2.02.05
* @param string $type
* @return string
*/
private static function get_graph_package( $type ) {
if ( 'geo' == $type ) {
$graph_package = 'geochart';
} elseif ( 'table' == $type ) {
$graph_package = 'table';
} else {
$graph_package = 'corechart';
}
return $graph_package;
}
/**
* Set up the title for a google graph
*
* @since 2.02.05
* @param array $atts
* @param array $options
*/
private static function add_title_options( $atts, &$options ) {
// title
$options['title'] = self::get_graph_title( $atts );
$options['titleTextStyle'] = array();
// bold title
self::convert_shortcode_att_to_bool_google_att( 'title_bold', 'bold', $atts, $options['titleTextStyle'] );
// title size
self::convert_shortcode_att_to_google_att( 'title_size', 'fontSize', $atts, $options['titleTextStyle'] );
// title font
self::convert_shortcode_att_to_google_att( 'title_font', 'fontName', $atts, $options['titleTextStyle'] );
// title color
self::convert_shortcode_att_to_google_att( 'title_color', 'color', $atts, $options['titleTextStyle'] );
}
/**
* Get the graph title
*
* @since 2.02.05
* @param array $atts
* @return string
*/
private static function get_graph_title( $atts ) {
if ( isset( $atts['title'] ) ) {
// Title defined by user
$title = $atts['title'];
} elseif ( isset( $atts['form'] ) && is_object( $atts['form'] ) ) {
// Title is form name for form graphs
$title = preg_replace( '/&#?[a-z0-9]{2,8};/i', '', FrmAppHelper::truncate( $atts['form']->name, $atts['truncate'], 0 ) );
} elseif ( ! empty( $atts['fields'] ) ) {
// Title is field name if single field, otherwise set to "Submissions"
if ( count( $atts['fields'] ) > 1 ) {
$title = __( 'Submissions', 'formidable-pro' );
} else {
$first_field = reset( $atts['fields'] );
$title = preg_replace( '/&#?[a-z0-9]{2,8};/i', '', FrmAppHelper::truncate( $first_field->name, $atts['truncate'], 0 ) );
}
} else {
// Default to blank
$title = '';
}
return html_entity_decode( $title );
}
/**
* Convert graph shortcode attributes to google options
*
* @since 2.02.05
* @param string $shortcode_att
* @param string $google_att
* @param array $atts
* @param array $options
*/
private static function convert_shortcode_att_to_google_att( $shortcode_att, $google_att, $atts, &$options ) {
if ( isset( $atts[ $shortcode_att ] ) ) {
$options[ $google_att ] = $atts[ $shortcode_att ];
}
}
/**
* Convert graph shortcode attributes to boolean google options
*
* @since 2.02.05
* @param string $shortcode_att
* @param string $google_att
* @param array $atts
* @param array $options
*/
private static function convert_shortcode_att_to_bool_google_att( $shortcode_att, $google_att, $atts, &$options ) {
if ( isset( $atts[ $shortcode_att ] ) ) {
if ( $atts[ $shortcode_att ] ) {
$options[ $google_att ] = true;
} else {
$options[ $google_att ] = false;
}
}
}
/**
* Add width, height, and chart area options
*
* @since 2.02.05
* @param array $atts
* @param array $options
*/
private static function add_size_options( $atts, &$options ) {
$options['width'] = $atts['width'];
$options['height'] = $atts['height'];
if ( isset( $atts['chart_area'] ) ) {
$chart_area_parts = explode( ',', $atts['chart_area'] );
foreach ( $chart_area_parts as $chart_area ) {
$single_item = explode( ':', $chart_area );
if ( count( $single_item ) !== 2 ) {
continue;
}
$key = trim( $single_item[0] );
$value = trim( $single_item[1] );
$options['chartArea'][ $key ] = $value;
}
}
}
/**
* Add legend options
*
* @since 2.02.05
* @param array $atts
* @param array $options
*/
private static function add_legend_options( $atts, &$options ) {
if ( $atts['show_key'] ) {
$options['legend'] = array();
// legend size
if ( isset( $atts['legend_size'] ) ) {
$options['legend']['textStyle'] = array( 'fontSize' => $atts['legend_size'] );
} elseif ( is_numeric( $atts['show_key'] ) && $atts['show_key'] >= 10 ) {
// reverse compatibility with show_key=fontSize
$options['legend']['textStyle'] = array( 'fontSize' => $atts['show_key'] );
}
// legend position
if ( isset( $atts['legend_position'] ) ) {
$options['legend']['position'] = $atts['legend_position'];
} else {
$options['legend']['position'] = 'right';
}
} elseif ( '0' === $atts['show_key'] ) {
$options['legend'] = 'none';
} else {
$options['legend'] = array( 'position' => 'none' );
}
}
/**
* Add tooltip options
*
* @since 2.02.05
* @param array $options
*/
private static function add_tooltip_options( &$options ) {
$options['tooltip'] = array( 'isHtml' => true );
}
/**
* Add color options
*
* @since 2.02.05
* @param array $atts
* @param array $options
*/
private static function add_color_options( $atts, &$options ) {
if ( $atts['colors'] ) {
$options['colors'] = explode( ',', $atts['colors'] );
$options['colors'] = array_map( 'trim', $options['colors'] );
}
$options['backgroundColor'] = $atts['bg_color'];
// is3D
self::convert_shortcode_att_to_bool_google_att( 'is3d', 'is3D', $atts, $options );
}
/**
* Add pie-specific options
*
* @since 2.02.05
* @param string $type
* @param array $atts
* @param array $options
*/
private static function add_pie_graph_options( $type, $atts, &$options ) {
if ( $type === 'pie' ) {
self::convert_shortcode_att_to_google_att( 'pie_hole', 'pieHole', $atts, $options );
}
}
/**
* Add line graph-specific options
*
* @since 2.02.05
* @param string $type
* @param array $atts
* @param array $options
*/
private static function add_line_graph_options( $type, $atts, &$options ) {
if ( $type === 'line' ) {
self::convert_shortcode_att_to_google_att( 'curve_type', 'curveType', $atts, $options );
}
}
/**
* @since 3.06.06
* @param string $type
* @param array $atts
* @param array $options
*/
private static function add_table_options( $type, $atts, &$options ) {
if ( $type !== 'table' ) {
return;
}
if ( $atts['pagesize'] ) {
$options['page'] = 'enable';
$options['pageSize'] = (int) $atts['pagesize'];
}
if ( $atts['sort_column'] >= 0 ) {
$options['sortColumn'] = (int) $atts['sort_column'];
if ( isset( $atts['sort_ascending'] ) ) {
$options['sortAscending'] = $atts['sort_ascending'] ? true : false;
}
}
}
/**
* Add axis options
*
* @since 2.02.05
* @param array $atts
* @param array $options
*/
private static function add_axis_options( $atts, &$options ) {
$options['hAxis'] = self::set_up_x_axis_options( $atts );
$options['vAxis'] = self::set_up_y_axis_options( $atts );
}
/**
* Set up the x-axis options
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function set_up_x_axis_options( $atts ) {
$x_axis = array(
'titleTextStyle' => array( 'italic' => false ),
'textStyle' => array(),
'gridlines' => array( 'color' => $atts['x_grid_color'] ),
);
// x min and max
if ( isset( $atts['x_min'] ) || isset( $atts['x_max'] ) ) {
$x_axis['viewWindow'] = self::add_axis_max_or_min( 'x_min', 'x_max', $atts );
}
// x title
self::convert_shortcode_att_to_google_att( 'x_title', 'title', $atts, $x_axis );
// showTextEvery
self::convert_shortcode_att_to_google_att( 'x_show_text_every', 'showTextEvery', $atts, $x_axis );
// slantedTextAngle
self::convert_shortcode_att_to_google_att( 'x_text_angle', 'slantedTextAngle', $atts, $x_axis );
// slantedText
$x_axis['slantedText'] = ( $atts['x_slanted_text'] ) ? true : false;
// x-axis title size
self::convert_shortcode_att_to_google_att( 'x_title_size', 'fontSize', $atts, $x_axis['titleTextStyle'] );
// x-axis color
self::convert_shortcode_att_to_google_att( 'x_title_color', 'color', $atts, $x_axis['titleTextStyle'] );
// x axis labels size
self::convert_shortcode_att_to_google_att( 'x_labels_size', 'fontSize', $atts, $x_axis['textStyle'] );
return $x_axis;
}
/**
* Set up the y-axis options
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function set_up_y_axis_options( $atts ) {
$y_axis = array(
'titleTextStyle' => array( 'italic' => false ),
'gridlines' => array( 'color' => $atts['grid_color'] ),
'textStyle' => array(),
);
// y min and max
if ( isset( $atts['y_min'] ) || isset( $atts['y_max'] ) ) {
$y_axis['viewWindow'] = self::add_axis_max_or_min( 'y_min', 'y_max', $atts );
}
// y-axis title
self::convert_shortcode_att_to_google_att( 'y_title', 'title', $atts, $y_axis );
// y-axis title size
self::convert_shortcode_att_to_google_att( 'y_title_size', 'fontSize', $atts, $y_axis['titleTextStyle'] );
// y-axis color
self::convert_shortcode_att_to_google_att( 'y_title_color', 'color', $atts, $y_axis['titleTextStyle'] );
// y axis labels size
self::convert_shortcode_att_to_google_att( 'y_labels_size', 'fontSize', $atts, $y_axis['textStyle'] );
return $y_axis;
}
/**
* Add min and/or max axis limit
*
* @since 2.02.05
* @param string $min_key
* @param string $max_key
* @param array $atts
* @return array
*/
private static function add_axis_max_or_min( $min_key, $max_key, $atts ) {
$viewWindow = array();
// add axis minimum
self::convert_shortcode_att_to_google_att( $min_key, 'min', $atts, $viewWindow );
// add axis maximum
self::convert_shortcode_att_to_google_att( $max_key, 'max', $atts, $viewWindow );
return $viewWindow;
}
/**
* Get the data for a single field graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_data_for_single_field_graph( $atts ) {
$field = reset( $atts['fields'] );
$field_values = self::get_meta_values_for_single_field( $field, $atts );
$graph_data = self::format_meta_values_for_single_field( $field, $field_values, $atts );
if ( ! empty( $graph_data ) ) {
$graph_label = $field->name;
$first_row = array( $graph_label );
if ( count( reset( $graph_data ) ) > 1 ) {
$tooltip_text = self::get_tooltip_text( $atts );
$first_row[] = $tooltip_text;
}
array_unshift( $graph_data, $first_row );
self::add_user_defined_column_colors( $atts, $graph_data );
}
return $graph_data;
}
/**
* Get the meta values for a single field
*
* @since 2.02.05
* @since 5.0 Make this method public.
*
* @param object $field
* @param array $atts
* @return array
*/
public static function get_meta_values_for_single_field( $field, $atts ) {
$atts['form_id'] = $field->form_id;
self::check_field_filters( $atts );
// If there are field filters and entry IDs is empty, stop now
if ( ! empty( $atts['filters'] ) && empty( $atts['entry_ids'] ) ) {
return array();
}
$meta_args = self::package_filtering_arguments_for_query( $atts );
$field_values = FrmProEntryMeta::get_all_metas_for_field( $field, $meta_args );
self::clean_field_values( $field, $atts, $field_values );
return $field_values;
}
/**
* Package specific filtering arguments in an array
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function package_filtering_arguments_for_query( $atts ) {
$pass_args = array(
'entry_ids' => 'entry_ids',
'user_id' => 'user_id',
'created_at_greater_than' => 'start_date',
'created_at_less_than' => 'end_date',
'is_draft' => 'is_draft',
'form_id' => 'form_id',
);
$meta_args = array();
foreach ( $pass_args as $atts_key => $arg_key ) {
if ( isset( $atts[ $atts_key ] ) ) {
$meta_args[ $arg_key ] = $atts[ $atts_key ];
}
}
return $meta_args;
}
/**
* Check all field, created_at, and updated_at filters
*
* @since 2.02.05
* @param array $atts
*/
private static function check_field_filters( &$atts ) {
if ( ! empty( $atts['filters'] ) ) {
if ( ! isset( $atts['entry_ids'] ) ) {
$atts['entry_ids'] = array();
$atts['after_where'] = false;
} else {
$atts['after_where'] = true;
}
foreach ( $atts['filters'] as $key => $value ) {
$atts['entry_ids'] = self::get_entry_ids_for_field_filter( $key, $value, $atts );
$atts['after_where'] = true;
if ( ! $atts['entry_ids'] ) {
return;
}
}
}
}
/**
* Get the entry IDs for a field filter
*
* @since 2.02.05
* @param string $key
* @param string $value
* @param array $args
* @return array
*/
private static function get_entry_ids_for_field_filter( $key, $value, $args ) {
$pass_args = array(
'orig_f' => $key,
'val' => $value,
'entry_ids' => $args['entry_ids'],
'after_where' => $args['after_where'],
'drafts' => isset( $args['is_draft'] ) ? $args['is_draft'] : 0,
'form_id' => $args['form_id'],
'form_posts' => self::get_form_posts( $args ),
);
return FrmProStatisticsController::get_field_matches( $pass_args );
}
/**
* Get the entry and attached post ID for all entries that have posts attached
*
* @since 2.02.05
* @param array $args
* @return array
*/
private static function get_form_posts( $args ) {
$where_post = array(
'form_id' => $args['form_id'],
'post_id >' => 1,
);
if ( isset( $args['is_draft'] ) && $args['is_draft'] != 'both' ) {
$where_post['is_draft'] = $args['is_draft'];
}
if ( isset( $args['user_id'] ) ) {
$where_post['user_id'] = $args['user_id'];
}
return FrmDb::get_results( 'frm_items', $where_post, 'id,post_id' );
}
/**
* Format a single field's meta values
*
* @since 2.02.05
* @param object $field
* @param array $meta_values
* @param array $atts
* @return array
*/
private static function format_meta_values_for_single_field( $field, $meta_values, $atts ) {
if ( ! $meta_values ) {
return array();
}
$count_values = array();
foreach ( $meta_values as $meta ) {
$meta = self::get_displayed_value( $field, $meta );
if ( isset( $count_values[ $meta ] ) ) {
$count_values[ $meta ]++;
} else {
$count_values[ $meta ] = 1;
}
}
self::order_values_for_single_field_graph( $field, $atts, $count_values );
// Get slice of array
if ( isset( $atts['limit'] ) && is_numeric( $atts['limit'] ) ) {
$count_values = array_slice( $count_values, 0, $atts['limit'] );
}
$graph_data = array();
$is_numeric_hist = false;
foreach ( $count_values as $meta_value => $count ) {
if ( $meta_value === '' ) {
continue;
}
if ( $atts['type'] == 'pie' ) {
$meta_value = (string) $meta_value;
} elseif ( $atts['type'] === 'histogram' && is_numeric( $meta_value ) && ( empty( $graph_data ) || $is_numeric_hist ) ) {
$is_numeric_hist = true;
$meta_value = (float) $meta_value;
for ( $i = 1; $i <= $count; $i++ ) {
$graph_data[] = array( $meta_value );
}
continue;
}
$graph_data[] = array( $meta_value, $count );
}
return $graph_data;
}
/**
* Order the values for a single field graph
*
* @since 2.02.05
* @param object $field
* @param array $atts
* @param array $count_values
*/
private static function order_values_for_single_field_graph( $field, $atts, &$count_values ) {
$order_opts = $atts['x_order'] === 'field_opts' || ( $atts['x_order'] === 'default' && $atts['include_zero'] );
if ( $order_opts && in_array( $field->type, array( 'radio', 'checkbox', 'select', 'data' ), true ) ) {
// Sort values by order of field options
self::sort_data_by_field_options( $field, $atts, $count_values );
} elseif ( $atts['x_order'] === 'desc' ) {
// Sort by descending count
arsort( $count_values );
} else {
// Sort alphabetically by default
ksort( $count_values );
}
}
/**
* Get the tooltip text
*
* @since 2.02.05
* @param array $atts
* @return string
*/
private static function get_tooltip_text( $atts ) {
if ( isset( $atts['tooltip_label'] ) ) {
$tooltip_text = $atts['tooltip_label'];
} elseif ( 'total' === $atts['data_type'] ) {
$tooltip_text = __( 'Total', 'formidable-pro' );
} elseif ( 'average' === $atts['data_type'] ) {
$tooltip_text = __( 'Average', 'formidable-pro' );
} else {
$tooltip_text = __( 'Submissions', 'formidable-pro' );
}
return $tooltip_text;
}
/**
* Get the data for a multi-field graph without an x-axis
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_data_for_multi_field_graph( $atts ) {
$tooltip_text = self::get_tooltip_text( $atts );
$graph_data = array( array( __( 'Fields', 'formidable-pro' ), $tooltip_text ) );
foreach ( $atts['fields'] as $field ) {
$meta_values = self::get_meta_values_for_single_field( $field, $atts );
if ( 'total' === $atts['data_type'] ) {
// get total
$y_value = array_sum( $meta_values );
} elseif ( 'average' === $atts['data_type'] ) {
if ( ! $meta_values ) {
// avoid division by zero
$y_value = 0;
} else {
// get average
$y_value = array_sum( $meta_values ) / count( $meta_values );
}
} else {
// get count
$y_value = count( $meta_values );
}
$graph_data[] = array(
$field->name,
$y_value,
);
}
self::apply_column_colors( $atts, $graph_data );
return $graph_data;
}
/**
* Get the data for a form graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_data_for_form_graph( $atts ) {
self::prepare_atts_for_form_graph( $atts );
if ( ! self::can_continue_with_form_graph( $atts ) ) {
return array();
}
$x_axis_data = self::get_associative_values_for_x_axis( $atts['x_axis_field'], $atts );
$y_axis_data = $x_axis_data;
self::order_x_axis_values( $atts, $x_axis_data );
$graph_data = self::combine_data_by_id( $x_axis_data, array( $y_axis_data ), $atts );
self::maybe_add_zero_value_dates( $atts, $graph_data );
self::add_first_row_to_graph_data( $atts, $graph_data );
return $graph_data;
}
/**
* Prepare a few items in the $atts array for a form graph
*
* @since 2.02.05
* @param array $atts
*/
private static function prepare_atts_for_form_graph( &$atts ) {
$atts['x_axis'] = 'created_at';
$atts['x_axis_field'] = $atts['x_axis'];
if ( ! $atts['include_zero'] ) {
$atts['include_zero'] = true;
}
self::get_default_start_date_for_form_graph( $atts );
self::get_default_end_date_for_form_graph( $atts );
self::check_field_filters( $atts );
}
/**
* Check if the necessary atts are present to continue with a form graph
*
* @since 2.02.05
* @param array $atts
* @return bool
*/
private static function can_continue_with_form_graph( $atts ) {
$continue = true;
if ( ! isset( $atts['form_id'] ) ) {
// If there is no form
$continue = false;
} elseif ( ! empty( $atts['filters'] ) && empty( $atts['entry_ids'] ) ) {
// If there are field filters and entry IDs is empty, stop now
$continue = false;
}
return $continue;
}
/**
* Get the default start date for a form graph
*
* @since 2.02.05
* @param array $atts
*/
private static function get_default_start_date_for_form_graph( &$atts ) {
if ( ! isset( $atts['created_at_greater_than'] ) || ! $atts['created_at_greater_than'] ) {
$group_by = isset( $atts['group_by'] ) ? $atts['group_by'] : '';
if ( $group_by == 'month' ) {
$atts['created_at_greater_than'] = '-1 year';
} elseif ( $group_by == 'quarter' ) {
$atts['created_at_greater_than'] = '-2 years';
} elseif ( $group_by == 'year' ) {
$atts['created_at_greater_than'] = '-10 years';
} else {
$atts['created_at_greater_than'] = '-1 month';
}
}
$atts['x_start'] = $atts['created_at_greater_than'];
}
/**
* Get the default end date for a form graph
*
* @since 2.02.05
* @param array $atts
*/
private static function get_default_end_date_for_form_graph( &$atts ) {
if ( ! isset( $atts['created_at_less_than'] ) || ! $atts['created_at_less_than'] ) {
$atts['created_at_less_than'] = 'NOW';
}
$atts['x_end'] = $atts['created_at_less_than'];
}
/**
* Add the zero values for all dates between specified start and end date
*
* @since 2.02.05
* @param array $atts
* @param array $graph_data
*/
private static function maybe_add_zero_value_dates( $atts, &$graph_data ) {
if ( ! $atts['include_zero'] ) {
return;
}
if ( ! self::is_created_at_or_updated_at( $atts['x_axis_field'] ) && ! self::is_date_field( $atts['x_axis_field'] ) ) {
return;
}
$start_date = self::get_start_date_for_x_axis_date_include_zero_graph( $atts, $graph_data );
$end_date = self::get_end_date_for_x_axis_date_include_zero_graph( $atts, $graph_data );
$group_by = isset( $atts['group_by'] ) ? $atts['group_by'] : '';
$all_dates = self::get_all_dates_for_period( $start_date, $end_date, $group_by );
$new_graph_data = array();
$count = 0;
foreach ( $all_dates as $date_str ) {
if ( isset( $graph_data[ $count ] ) && $graph_data[ $count ][0] == $date_str ) {
$new_graph_data[] = $graph_data[ $count ];
$count++;
} else {
$add_row = array( $date_str );
if ( is_array( $atts['fields'] ) && ! empty( $atts['fields'] ) ) {
$field_count = count( $atts['fields'] );
for ( $i = 1; $i <= $field_count; $i++ ) {
$add_row[] = 0;
}
} else {
$add_row[] = 0;
}
$new_graph_data[] = $add_row;
}
}
$graph_data = $new_graph_data;
}
/**
* Get the start date for a date x-axis when include_zero is set
*
* @since 2.02.05
* @param array $atts
* @param array $graph_data
* @return string
*/
private static function get_start_date_for_x_axis_date_include_zero_graph( $atts, $graph_data ) {
if ( isset( $atts['x_start'] ) && $atts['x_start'] ) {
$start_date = $atts['x_start'];
} else {
$first_row = reset( $graph_data );
$start_date = $first_row[0];
$start_date = self::convert_formatted_date_to_y_m_d( $start_date );
}
return $start_date;
}
/**
* Get the end date for a date x-axis when include_zero is set
*
* @param $atts
* @param $graph_data
* @return mixed
*/
private static function get_end_date_for_x_axis_date_include_zero_graph( $atts, $graph_data ) {
if ( isset( $atts['x_end'] ) && $atts['x_end'] ) {
$end_date = $atts['x_end'];
} else {
$final_row = end( $graph_data );
$end_date = $final_row[0];
$end_date = self::convert_formatted_date_to_y_m_d( $end_date );
}
return $end_date;
}
/**
* Get all dates for a given start and end date
*
* @since 2.02.05
* @param string $start_date
* @param string $end_date
* @param string $group_by
* @return array
*/
private static function get_all_dates_for_period( $start_date, $end_date, $group_by ) {
$start_timestamp = strtotime( $start_date );
$end_timestamp = strtotime( $end_date ) + 86399;
$all_dates = array();
if ( $group_by === 'month' ) {
for ( $d = $start_timestamp; $d <= $end_timestamp; $d += 60 * 60 * 24 * 25 ) {
self::add_formatted_date_to_array( 'F Y', $d, $all_dates );
}
} elseif ( $group_by === 'quarter' ) {
for ( $d = $start_timestamp; $d <= $end_timestamp; $d += 60 * 60 * 24 * 80 ) {
self::add_formatted_date_to_array( 'quarter', $d, $all_dates );
}
} elseif ( $group_by === 'year' ) {
for ( $d = $start_timestamp; $d <= $end_timestamp; $d += 60 * 60 * 24 * 364 ) {
self::add_formatted_date_to_array( 'Y', $d, $all_dates );
}
} else {
$date_format = get_option( 'date_format' );
for ( $d = $start_timestamp; $d <= $end_timestamp; $d += 60 * 60 * 24 ) {
$all_dates[] = date_i18n( $date_format, $d, true );
}
}
return $all_dates;
}
/**
* Add a formatted date to an array
*
* @since 2.02.05
* @param string $format
* @param string $date
* @param array $all_dates
*/
private static function add_formatted_date_to_array( $format, $date, &$all_dates ) {
if ( 'quarter' === $format ) {
$date = self::convert_date_to_quarter( $date );
} else {
$date = date_i18n( $format, $date, true );
}
if ( ! in_array( $date, $all_dates, true ) ) {
$all_dates[] = $date;
}
}
/**
* Get the data for an x-axis graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_data_for_x_axis_graph( $atts ) {
self::prepare_atts_for_x_axis_graph( $atts );
if ( ! self::can_continue_with_x_axis_graph( $atts ) ) {
return array();
}
$x_axis_data = self::get_associative_values_for_x_axis( $atts['x_axis_field'], $atts );
if ( ! $x_axis_data ) {
return array();
}
self::order_x_axis_values( $atts, $x_axis_data );
$field_data = self::get_associative_values_for_fields( $atts );
$graph_data = self::combine_data_by_id( $x_axis_data, $field_data, $atts );
self::maybe_add_zero_value_dates( $atts, $graph_data );
self::add_first_row_to_graph_data( $atts, $graph_data );
self::add_user_defined_column_colors( $atts, $graph_data );
return $graph_data;
}
/**
* Prepare the $atts array for an x-axis graph
*
* @since 2.02.05
* @param array $atts
*/
private static function prepare_atts_for_x_axis_graph( &$atts ) {
$atts['x_axis_field'] = self::get_x_axis_field( $atts['x_axis'] );
if ( ! $atts['x_axis_field'] ) {
return;
}
$atts['form_id'] = $atts['fields'][0]->form_id;
self::maybe_add_x_start_and_x_end( $atts );
self::check_field_filters( $atts );
}
/**
* Add x_start and x_end if start_date and end_date are set
*
* @since 2.02.05
* @param array $atts
*/
private static function maybe_add_x_start_and_x_end( &$atts ) {
if ( self::is_date_field( $atts['x_axis_field'] ) && ! empty( $atts['filters'] ) ) {
// copy date field filters to x_start and x_end
foreach ( $atts['filters'] as $filter_key => $filter_value ) {
if ( strpos( $filter_key, $atts['x_axis_field']->id . '_greater_than' ) !== false ||
strpos( $filter_key, $atts['x_axis_field']->field_key . '_greater_than' ) !== false
) {
$atts['x_start'] = $filter_value;
} elseif ( strpos( $filter_key, $atts['x_axis_field']->id . '_less_than' ) !== false ||
strpos( $filter_key, $atts['x_axis_field']->field_key . '_less_than' ) !== false
) {
$atts['x_end'] = $filter_value;
}
}
} elseif ( self::is_created_at_or_updated_at( $atts['x_axis_field'] ) ) {
// copy created_at filters to x_start and x_end
if ( isset( $atts['created_at_greater_than'] ) ) {
$atts['x_start'] = $atts['created_at_greater_than'];
}
if ( isset( $atts['created_at_less_than'] ) ) {
$atts['x_end'] = $atts['created_at_less_than'];
}
}
}
/**
* Check if the necessary atts are present to continue with an x-axis graph
*
* @since 2.02.05
* @param array $atts
* @return bool
*/
private static function can_continue_with_x_axis_graph( $atts ) {
$continue = true;
if ( ! $atts['x_axis_field'] ) {
// If no x-axis field
$continue = false;
} elseif ( ! empty( $atts['filters'] ) && empty( $atts['entry_ids'] ) ) {
// If there are field filters and entry IDs is empty, stop now
$continue = false;
}
return $continue;
}
/**
* Order x-axis values
*
* @since 2.02.05
* @param array $atts
* @param array $x_axis_data
*/
private static function order_x_axis_values( $atts, &$x_axis_data ) {
if ( self::is_created_at_or_updated_at( $atts['x_axis'] ) || self::is_date_field( $atts['x_axis_field'] ) ) {
usort( $x_axis_data, array( 'FrmProGraphsController', 'date_compare' ) );
} elseif ( is_object( $atts['x_axis_field'] ) && $atts['x_axis_field']->type === 'number' ) {
usort( $x_axis_data, array( 'FrmProGraphsController', 'number_compare' ) );
} elseif ( ! empty( $atts['x_axis_field']->options ) && 'field_opts' === $atts['x_order'] ) {
$x_axis_data = self::sort_x_axis_data_by_field_options( $atts['x_axis_field'], $atts, $x_axis_data );
} elseif ( 'default' === $atts['x_order'] ) {
usort( $x_axis_data, array( 'FrmProGraphsController', 'string_compare' ) );
}
}
/**
* @param stdClass $field
* @param array $atts
* @param array $x_axis_data
* @return array
*/
private static function sort_x_axis_data_by_field_options( $field, $atts, $x_axis_data ) {
if ( empty( $field->options ) ) {
return;
}
$labels = self::get_field_option_labels( $field );
$index_by_opt = array_flip( $labels );
$compare = function( $a, $b ) use ( $index_by_opt ) {
$index_a = $index_by_opt[ $a->meta_value ];
$index_b = $index_by_opt[ $b->meta_value ];
return $index_a - $index_b;
};
usort( $x_axis_data, $compare );
return $x_axis_data;
}
/**
* Compare two dates
*
* @since 2.02.05
* @param stdClass $a
* @param stdClass $b
* @return int
*/
private static function date_compare( $a, $b ) {
$t1 = strtotime( $a->meta_value);
$t2 = strtotime( $b->meta_value );
return $t1 - $t2;
}
/**
* Compare two numbers as floats
*
* @since 2.02.05
* @param stdClass $a
* @param stdClass $b
* @return float
*/
private static function number_compare( $a, $b ) {
$n1 = (float) $a->meta_value;
$n2 = (float) $b->meta_value;
return $n1 - $n2;
}
/**
* Compare two strings
*
* @since 5.0.07
* @param stdClass $a
* @param stdClass $b
* @return int
*/
private static function string_compare( $a, $b ) {
return strcasecmp( $a->meta_value, $b->meta_value );
}
/**
* Check if string value is created_at or updated_at
*
* @since 2.02.05
* @param string|object $value
* @return bool
*/
private static function is_created_at_or_updated_at( $value ) {
return ( is_string( $value ) && in_array( $value, array( 'created_at', 'updated_at' ) ) );
}
/**
* Check if a variable is an object and has a type of date
*
* @since 2.02.05
* @param mixed $value
* @return bool
*/
private static function is_date_field( $value ) {
return ( is_object( $value ) && $value->type == 'date' );
}
/**
* Get the x axis field object
*
* @since 2.02.05
* @param string $x_axis
* @return mixed
*/
private static function get_x_axis_field( $x_axis ) {
if ( self::is_created_at_or_updated_at( $x_axis ) ) {
$x_axis_field = $x_axis;
} else {
$x_axis_field = FrmField::getOne( $x_axis );
}
return $x_axis_field;
}
/**
* Get associative array values for a specific field
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_associative_values_for_fields( $atts ) {
$field_data = array();
foreach ( $atts['fields'] as $field ) {
$field_data[] = FrmProEntryMeta::get_associative_array_values_for_field( $field, $atts );
}
return $field_data;
}
/**
* Combine x and y axis data by ID
*
* @since 2.02.05
* @param array $x_axis_data
* @param array $field_data
* @param array $atts
* @return array
*/
private static function combine_data_by_id( $x_axis_data, $field_data, $atts ) {
$graph_data = array();
$data_counts = array();
foreach ( $x_axis_data as $x_data ) {
$entry_id = $x_data->id;
$x_value = self::get_x_axis_displayed_value( $x_data->meta_value, $atts );
if ( $x_value === '' ) {
continue;
}
if ( isset( $graph_data[ $x_value ] ) ) {
$data_counts[ $x_value ]++;
self::update_existing_row_of_graph_data( $entry_id, $field_data, $graph_data[ $x_value ], $data_counts[ $x_value ], $atts );
} else {
$data_counts[ $x_value ] = 1;
$graph_data[ $x_value ] = self::generate_new_row_of_graph_data( $entry_id, $x_value, $field_data, $atts );
}
}
$graph_data = array_values( $graph_data );
return $graph_data;
}
/**
* Add the first row of data to a graph
*
* @since 2.02.05
* @param array $atts
* @param array $graph_data
*/
private static function add_first_row_to_graph_data( $atts, &$graph_data ) {
if ( $atts['form'] ) {
$first_row = self::get_first_row_labels_for_form_graph();
array_unshift( $graph_data, $first_row );
} elseif ( $atts['x_axis_field'] && ! empty( $atts['fields'] ) ) {
$first_row = self::get_first_row_labels_for_x_axis_graph( $atts );
array_unshift( $graph_data, $first_row );
}
}
/**
* Get the first row of labels for x-axis graph
*
* @since 2.02.05
* @param array $atts
* @return array
*/
private static function get_first_row_labels_for_x_axis_graph( $atts ) {
if ( 'created_at' === $atts['x_axis_field'] ) {
$x_label = __( 'Creation Date', 'formidable-pro' );
} elseif ( 'updated_at' === $atts['x_axis_field'] ) {
$x_label = __( 'Updated At', 'formidable-pro' );
} elseif ( is_object( $atts['x_axis_field'] ) ) {
$x_label = $atts['x_axis_field']->name;
} else {
$x_label = __( 'Invalid x-axis', 'formidable-pro' );
}
$first_row = array( $x_label );
$count = 0;
$tooltip_label = isset( $atts['tooltip_label'] ) ? explode( ',', $atts['tooltip_label'] ) : array();
foreach ( $atts['fields'] as $field ) {
if ( isset( $tooltip_label[ $count ] ) && ! empty( $tooltip_label[ $count ] ) ) {
$first_row[] = $tooltip_label[ $count ];
} else {
$first_row[] = $field->name;
}
$count++;
}
return $first_row;
}
/**
* Get the first row of labels for a form graph
*
* @since 2.02.05
* @return array
*/
private static function get_first_row_labels_for_form_graph() {
$x_label = __( 'Creation Date', 'formidable-pro' );
return array( $x_label, __( 'Submissions', 'formidable-pro' ) );
}
/**
* Create a new row of graph data
*
* @since 2.02.05
* @param int $entry_id
* @param string $x_value
* @param array $all_field_data
* @param array $atts
* @return array
*/
private static function generate_new_row_of_graph_data( $entry_id, $x_value, $all_field_data, $atts ) {
self::adjust_x_axis_value_type( $atts, $x_value );
$new_row = array( $x_value );
foreach ( $all_field_data as $single_field_data ) {
if ( isset( $single_field_data[ $entry_id ] ) ) {
if ( $atts['data_type'] == 'total' || $atts['data_type'] == 'average' ) {
if ( is_numeric( $single_field_data[ $entry_id ]->meta_value ) ) {
$new_row[] = (float) $single_field_data[ $entry_id ]->meta_value;
} else {
$new_row[] = 0;
}
} else {
$new_row[] = 1;
}
} else {
$new_row[] = 0;
}
}
return $new_row;
}
/**
* Convert number field values to float for x-axis
*
* @since 2.02.05
* @param array $atts
* @param string $x_value
*/
private static function adjust_x_axis_value_type( $atts, &$x_value ) {
if ( is_object( $atts['x_axis_field'] ) && $atts['x_axis_field']->type == 'number' ) {
$x_value = (float) $x_value;
}
}
/**
* Update an existing row of graph data
*
* @since 2.02.05
* @param int $entry_id
* @param array $all_field_data
* @param array $current_data
* @param array $data_count
* @param array $atts
*/
private static function update_existing_row_of_graph_data( $entry_id, $all_field_data, &$current_data, $data_count, $atts ) {
$count = 0;
foreach ( $all_field_data as $single_field_data ) {
$count++;
if ( isset( $single_field_data[ $entry_id ] ) ) {
if ( $atts['data_type'] == 'total' ) {
if ( is_numeric( $single_field_data[ $entry_id ]->meta_value ) ) {
$current_data[ $count ] += $single_field_data[ $entry_id ]->meta_value;
}
} elseif ( $atts['data_type'] == 'average' ) {
if ( is_numeric( $single_field_data[ $entry_id ]->meta_value ) ) {
$current_data[ $count ] = (
( ( $current_data[ $count ] * ( $data_count - 1 ) ) + $single_field_data[ $entry_id ]->meta_value ) /
$data_count );
}
} else {
$current_data[ $count ]++;
}
}
}
}
/**
* Get the associative values for the x-axis field
*
* @since 2.02.05
* @param string|object $x_axis_field
* @param array $atts
* @return array
*/
private static function get_associative_values_for_x_axis( $x_axis_field, $atts ) {
if ( ! $x_axis_field ) {
$x_axis_data = array();
} elseif ( self::is_created_at_or_updated_at( $x_axis_field ) ) {
$query_args = self::package_filtering_arguments_for_query( $atts );
$x_axis_data = FrmProEntryMeta::get_associative_array_values_for_frm_items_column( $x_axis_field, $query_args );
} else {
$query_args = self::package_filtering_arguments_for_query( $atts );
$x_axis_data = FrmProEntryMeta::get_associative_array_values_for_field( $x_axis_field, $query_args );
}
return $x_axis_data;
}
/**
* Get the displayed x-axis value
*
* @since 2.02.05
* @param string $x_value
* @param array $atts
* @return string
*/
private static function get_x_axis_displayed_value( $x_value, $atts ) {
self::convert_db_date_to_localized_date( $atts, $x_value );
if ( isset( $atts['group_by'] ) ) {
if ( ! self::is_valid_date( $x_value ) ) {
return '';
}
if ( $atts['group_by'] === 'month' ) {
$x_value = date_i18n( 'F Y', strtotime( $x_value ), true );
} elseif ( $atts['group_by'] === 'quarter' ) {
$x_value = self::convert_date_to_quarter( $x_value );
} elseif ( $atts['group_by'] === 'year' ) {
$x_value = gmdate( 'Y', strtotime( $x_value ) );
} else {
$x_value = self::get_displayed_value( $atts['x_axis_field'], $x_value );
}
} else {
$x_value = self::get_displayed_value( $atts['x_axis_field'], $x_value );
}
return $x_value;
}
/**
* Convert a creation date or update date to the localized date
*
* @since 2.02.05
* @param array $atts
* @param string $x_value
*/
private static function convert_db_date_to_localized_date( $atts, &$x_value ) {
if ( self::is_created_at_or_updated_at( $atts['x_axis'] ) ) {
$x_value = FrmAppHelper::get_localized_date( 'Y-m-d H:i:s', $x_value );
}
}
/**
* Check if a value is a valid date
*
* @since 2.02.05
* @param string $value
* @return bool
*/
private static function is_valid_date( $value ) {
return ( gmdate( 'Y', strtotime( $value ) ) > 0 );
}
/**
* Convert a date to the correct quarter string
*
* @since 2.02.05
* @param string $date
* @return string
*/
private static function convert_date_to_quarter( $date ) {
$value = gmdate( 'Y-m-d', strtotime( $date ) );
$y = gmdate( 'Y', strtotime( $value ) );
if ( preg_match('/-(01|02|03)-/', $value ) ) {
$value = __( 'Q1', 'formidable-pro' ) . ' ' . $y;
} elseif ( preg_match('/-(04|05|06)-/', $value) ) {
$value = __( 'Q2', 'formidable-pro' ) . ' ' . $y;
} elseif ( preg_match('/-(07|08|09)-/', $value) ) {
$value = __( 'Q3', 'formidable-pro' ) . ' ' . $y;
} elseif ( preg_match('/-(10|11|12)-/', $value) ) {
$value = __( 'Q4', 'formidable-pro' ) . ' ' . $y;
}
return $value;
}
/**
* Get the displayed value for a given field and meta value
*
* @since 2.02.05
* @param string|object $field
* @param string $value
* @return string|int
*/
private static function get_displayed_value( $field, $value ) {
if ( self::is_created_at_or_updated_at( $field ) ) {
$displayed_value = self::convert_date_for_graph_display( $value );
} elseif ( is_object( $field ) ) {
$displayed_value = self::get_displayed_value_for_specific_fields( $value, $field );
} else {
$displayed_value = $value;
}
$displayed_value = apply_filters( 'frm_graph_value', $displayed_value, $field );
if ( is_array( $displayed_value ) ) {
$displayed_value = reset( $displayed_value );
}
return $displayed_value;
}
/**
* Gets displayed value for specific fields.
*
* @since 5.4.2
*
* @param mixed $value Field value.
* @param object $field Field object.
*/
private static function get_displayed_value_for_specific_fields( $value, $field ) {
if ( ! is_array( $value ) ) {
if ( $field->type === 'date' ) {
$displayed_value = self::convert_date_for_graph_display( $value );
} elseif ( ! empty( $field->field_options['separate_value'] ) || FrmField::is_option_true( $field, 'other' ) ) {
$displayed_value = self::get_option_label_for_value( $field, $value );
} elseif ( $field->type === 'user_id' ) {
$displayed_value = FrmFieldsHelper::get_user_display_name( $value, 'display_name' );
} elseif ( $field->type === 'data' && $field->field_options['form_select'] !== 'taxonomy' ) {
$displayed_value = FrmFieldsHelper::get_unfiltered_display_value( compact( 'value', 'field' ) );
} elseif ( FrmField::is_option_true_in_array( $field->field_options, 'post_field' ) && $field->field_options['post_field'] === 'post_category' && $field->field_options['taxonomy'] ) {
$displayed_value = FrmProPost::get_taxonomy_term_name_from_id( $value, $field->field_options['taxonomy'] );
} elseif ( is_numeric( $value ) ) {
$displayed_value = $value;
} elseif ( $field->type === 'textarea' || $field->type === 'rte' ) {
$displayed_value = strip_tags( $value );
} else {
$displayed_value = ucfirst( $value );
}
} elseif ( 'name' === $field->type ) {
$field_obj = FrmFieldFactory::get_field_object( $field );
$displayed_value = $field_obj->get_display_value( $value );
} else {
$displayed_value = $value;
}
return $displayed_value;
}
/**
* Convert a date to the WordPress format
*
* @since 2.02.05
* @param string $value
* @return string
*/
private static function convert_date_for_graph_display( $value ) {
if ( self::is_valid_date( $value ) ) {
$date_format = get_option( 'date_format' );
$value = date_i18n( $date_format, strtotime( $value ), true );
} else {
$value = '';
}
return $value;
}
/**
* Convert a date formatted in the WordPress settings date format to Y-m-d
*
* @since 2.02.11
* @param string $date
* @return string
*/
private static function convert_formatted_date_to_y_m_d( $date ) {
$date_format = get_option( 'date_format' );
$date = DateTime::createFromFormat( $date_format, $date );
return $date->format( 'Y-m-d' );
}
/**
* Get the option label for a given value
*
* @since 2.02.05
* @param object $field
* @param string
* @return string
*/
private static function get_option_label_for_value( $field, $value ) {
$option_label = $value;
foreach ( $field->options as $opt_key => $opt ) {
if ( ! $opt ) {
continue;
}
if ( $value === $opt ) {
$option_label = $opt;
break;
} elseif ( is_array( $opt ) && $value == $opt['value'] ) {
$option_label = $opt['label'];
break;
} elseif ( FrmFieldsHelper::is_other_opt( $opt_key ) ) {
if ( FrmField::is_field_with_multiple_values( $field ) ) {
if ( $opt_key == $value ) {
$option_label = $opt;
break;
}
} else {
$option_label = $opt;
}
}
}
return $option_label;
}
/**
* Strip slashes and get rid of multi-dimensional arrays in inputs
*
* @since 2.0
*
* @param object $field
* @param array $atts
* @param array $field_values
*/
private static function clean_field_values( $field, $atts, &$field_values ) {
if ( ! $field_values ) {
return;
}
// Flatten multi-dimensional array
if ( count( $atts['fields'] ) === 1 && FrmField::is_field_with_multiple_values( $field ) ) {
FrmProStatisticsController::flatten_multi_dimensional_arrays_for_stats( $field, true, $field_values );
}
$field_values = wp_unslash( $field_values );
}
/**
* Order values so they match the field options order
*
* @since 2.0
*
* @param stdClass $field
* @param array $atts
* @param array $count_values
*/
private static function sort_data_by_field_options( $field, $atts, &$count_values ) {
if ( empty( $field->options ) ) {
return;
}
$lower_case_keys = array();
foreach ( $count_values as $key => $count ) {
$lower_case_key = strtolower( $key );
$lower_case_keys[ $lower_case_key ] = $key;
}
$ordered_values = array();
$labels = self::get_field_option_labels( $field );
foreach ( $labels as $label ) {
if ( array_key_exists( strtolower( $label ), $lower_case_keys ) ) {
$key = $lower_case_keys[ strtolower( $label ) ];
$ordered_values[ $key ] = $count_values[ $key ];
} elseif ( $atts['include_zero'] ) {
$ordered_values[ $label ] = 0;
}
}
$count_values = $ordered_values;
}
/**
* @param stdClass $field
* @return array
*/
private static function get_field_option_labels( $field ) {
$labels = array();
foreach ( $field->options as $opt ) {
if ( ! $opt ) {
continue;
}
if ( is_array( $opt ) ) {
if ( empty( $opt['label'] ) ) {
continue;
}
$opt = $opt['label'];
}
$labels[] = $opt;
}
return $labels;
}
/**
* Only add column colors if colors is defined by the user
*
* @since 2.02.05
* @param array $atts
* @param array $graph_data
*/
private static function add_user_defined_column_colors( $atts, &$graph_data ) {
if ( ! isset( $atts['x_axis'] ) && $atts['colors'] !== self::get_default_colors() ) {
self::apply_column_colors( $atts, $graph_data );
}
}
/**
* If colors is not empty, apply a different color to each bar
*
* @since 2.02.05
* @param array $atts
* @param array $graph_data
*/
private static function apply_column_colors( $atts, &$graph_data ) {
if ( ! empty( $atts['colors'] ) && in_array( $atts['type'], array( 'column', 'bar', 'hbar', 'scatter' ) ) ) {
$colors = explode( ',', $atts['colors'] );
$color_upper_limit = count( $colors ) - 1;
$count = -1;
foreach ( $graph_data as $key => $item ) {
if ( $count < 0 ) {
$graph_data[ $key ][] = array( 'role' => 'style' );
} else {
$graph_data[ $key ][] = $colors[ $count ];
}
if ( $count < $color_upper_limit ) {
$count++;
} else {
$count = 0;
}
}
}
}
/**
* Show the graphs on the form's Reports page
*
* @since 2.02.05
*/
public static function show_reports() {
global $wpdb;
add_filter( 'frm_form_stop_action_reports', '__return_true' );
FrmAppHelper::permission_check( 'frm_view_reports' );
$form = self::get_form_for_reports();
if ( ! $form ) {
require(FrmProAppHelper::plugin_path() . '/classes/views/frmpro-statistics/select.php');
return;
}
$entries = FrmDb::get_col( 'frm_items', array( 'form_id' => $form->id ), 'created_at' );
if ( empty( $entries ) ) {
$fields = array();
include( FrmProAppHelper::plugin_path() . '/classes/views/frmpro-statistics/show.php' );
return;
}
$fields = self::get_fields_for_reports( $form->id );
$data = self::generate_graphs_for_reports( $form, $fields );
foreach ( $fields as $field ) {
if ( ! isset( $data[ $field->id ] ) ) {
continue;
}
if ( 'user_id' === $field->type ) {
$user_ids = FrmDb::get_col( $wpdb->users, array(), 'ID', 'display_name ASC' );
$submitted_user_ids = FrmEntryMeta::get_entry_metas_for_field( $field->id, '', '', array( 'unique' => true ) );
break;
}
}
include( FrmProAppHelper::plugin_path() . '/classes/views/frmpro-statistics/show.php' );
}
/**
* Get a list of boxes to list with the graph on the reports page.
*
* @since 5.0.02
*/
public static function get_field_boxes( $args ) {
$field = $args['field'];
$total = FrmProStatisticsController::stats_shortcode(
array(
'id' => $field->id,
'type' => 'count',
)
);
if ( ! $total ) {
return array();
}
$post_boxes = array(
array(
'label' => __( 'Answered', 'formidable-pro' ),
'stat' => $total . ' (' . round( ( $total / count( $args['entries'] ) ) * 100, 2 ) . '%)',
),
);
self::add_average_box( $args, $post_boxes );
return apply_filters( 'frm_pro_reports_boxes', $post_boxes, $args );
}
/**
* Add the Average and Median reports for some field types.
*
* @since 5.0.02
*/
private static function add_average_box( $args, &$post_boxes ) {
$field = $args['field'];
if ( ! in_array( $field->type, array( 'number', 'hidden', 'scale' ) ) ) {
return;
}
$post_boxes[] = array(
'label' => __( 'Average', 'formidable-pro' ),
'stat' => FrmProStatisticsController::stats_shortcode(
array(
'id' => $field->id,
'type' => 'average',
)
),
);
$post_boxes[] = array(
'label' => __( 'Median', 'formidable-pro' ),
'stat' => FrmProStatisticsController::stats_shortcode(
array(
'id' => $field->id,
'type' => 'median',
)
),
);
}
/**
* Get the form for the reports
*
* @since 2.02.05
* @return bool|object
*/
private static function get_form_for_reports() {
$form = false;
if ( isset( $_REQUEST['form'] ) ) {
$form = FrmForm::getOne( $_REQUEST['form'] );
}
return $form;
}
/**
* Get all fields for the reports page
*
* @since 2.02.05
* @param int $form_id
* @return mixed
*/
private static function get_fields_for_reports( $form_id ) {
$exclude_types = FrmField::no_save_fields();
$exclude_types = array_merge(
$exclude_types,
array( 'file', 'grid', 'password', 'credit_card', 'address', 'signature', 'form', 'table', 'name' )
);
$fields = FrmField::getAll( array( 'fi.form_id' => $form_id, 'fi.type not' => $exclude_types ), 'field_order' );
/**
* Allows changing fields in the Reports page.
*
* @since 5.0
*
* @param array $fields Array of fields.
* @param array $args The arguments. Contains `$args`.
*/
return apply_filters( 'frm_fields_in_reports', $fields, compact( 'form_id' ) );
}
/**
* Generate the graphs for the Reports page
*
* @since 2.02.05
* @param object $form
* @param array $fields
* @return array
*/
private static function generate_graphs_for_reports( $form, $fields ) {
$data = array();
$common_atts = array(
'form' => $form->id,
'type' => 'line',
'bg_color' => 'transparent',
'width' => '100%',
'y_min' => 0,
'title' => '',
'chart_area' => 'top:30;height:90%',
'colors' => '#3177c7',
);
$atts = $common_atts + array( 'created_at_greater_than' => '-1 month' );
$data['time'] = self::graph_shortcode( $atts );
$atts = $common_atts + array(
'created_at_greater_than' => '-1 year',
'created_at_less_than' => '+1 month',
'group_by' => 'month',
);
$data['month'] = self::graph_shortcode( $atts );
self::add_field_graphs_for_reports( $fields, $data );
return $data;
}
/**
* Add all the field graphs for the reports page
*
* @since 2.02.05
* @param array $fields
* @param array $data
*/
private static function add_field_graphs_for_reports( $fields, &$data ) {
$atts = array(
'y_min' => 0,
'width' => '100%',
'height' => 'auto',
'bg_color' => 'transparent',
'title' => '',
'chart_area' => 'top:30;height:90%',
'x_slanted_text' => 0,
'x_order' => 'field_opts',
'include_zero' => 1,
'x_grid_color' => '#fff',
'colors' => '#3177c7',
'pagesize' => 10,
'sort_column' => 1,
'sort_ascending' => false,
);
$table_types = self::table_graph_types();
$add_table = array( 'radio', 'checkbox', 'select' );
foreach ( $fields as $field ) {
$atts['id'] = $field->id;
if ( $field->type === 'user_id' ) {
$atts['height'] = '400';
$atts['type'] = 'pie';
} elseif ( in_array( $field->type, $table_types, true ) ) {
$atts['type'] = 'table';
$atts['height'] = 'auto';
} else {
$atts['type'] = 'column';
$atts['height'] = '400';
}
if ( in_array( $field->type, array( 'radio', 'checkbox', 'select' ), true ) ) {
$atts['x_order'] = 'field_opts';
} elseif ( isset( $atts['x_order'] ) ) {
unset( $atts['x_order'] );
}
if ( $field->type === 'scale' ) {
$atts['x_min'] = FrmField::get_option( $field, 'minnum' ) - 1;
$atts['x_max'] = FrmField::get_option( $field, 'maxnum' ) + 1;
} elseif ( isset( $atts['x_min'] ) ) {
unset( $atts['x_min'], $atts['x_max'] );
}
$this_data = self::graph_shortcode( $atts );
if ( strpos( $this_data, 'frm_no_data_graph' ) === false ) {
$data[ $field->id ] = $this_data;
if ( in_array( $field->type, $add_table, true ) ) {
$atts['type'] = 'table';
$atts['height'] = 'auto';
$this_data = self::graph_shortcode( $atts );
$data[ $field->id . '_table' ] = $this_data;
}
}
}
}
/**
* Which field types show a graph by default?
*
* @since 4.0
*/
public static function table_graph_types() {
/**
* Allows modifying table graph types.
*
* @since 5.0
*
* @param array $types Table graph types.
*/
return apply_filters( 'frm_table_graph_types', array( 'url', 'text', 'textarea', 'rte', 'email' ) );
}
/**
* Apply deprecated filters
*
* @since 2.02.05
* @codeCoverageIgnore
*/
private static function apply_deprecated_filters() {
$placeholder = array();
apply_filters( 'frm_graph_values', $placeholder );
if ( has_filter( 'frm_graph_values' ) ) {
_deprecated_function( 'The frm_graph_values filter', '2.02.05', 'the frm_graph_data filter' );
}
apply_filters( 'frm_graph_labels', $placeholder );
if ( has_filter( 'frm_graph_labels' ) ) {
_deprecated_function( 'The frm_graph_labels filter', '2.02.05', 'the frm_graph_data filter' );
}
apply_filters( 'frm_final_graph_values', $placeholder );
if ( has_filter( 'frm_final_graph_values' ) ) {
_deprecated_function( 'The frm_final_graph_values filter', '2.02.05', 'the frm_graph_data filter' );
}
}
}