Files
SIGE-WEB-snapshot/wp-content/plugins/userfeedback-lite/includes/frontend/class-userfeedback-frontend.php

623 lines
16 KiB
PHP

<?php
/**
* Frontend class.
*
* Handles API calls made from the fronted by the Survey Widget
* Also handles the preview route
*
* @since 1.0.0
*
* @package UserFeedback
* @author David Paternina
*/
class UserFeedback_Frontend
{
// Helpful static variables
static $USERFEEDBACK_SURVEY_COOKIE_PREFIX = 'userfeedback-survey-';
public function __construct()
{
require_once USERFEEDBACK_PLUGIN_DIR . 'includes/lib/class-mobile-detect.php';
add_action('template_redirect', array($this, 'enqueue_styles_and_scripts_for_surveys'), 20);
add_action('rest_api_init', array($this, 'register_frontend_routes'));
}
/**
* Register frontend REST routes
*
* @return void
*/
public function register_frontend_routes()
{
register_rest_route(
'userfeedback/v1',
'/surveys/(?P<id>\w+)/responses',
array(
'methods' => 'POST',
'callback' => array($this, 'save_survey_response'),
'permission_callback' => '__return_true',
)
);
register_rest_route(
'userfeedback/v1',
'/surveys/(?P<id>\w+)/impression',
array(
'methods' => 'POST',
'callback' => array($this, 'record_survey_impression'),
'permission_callback' => '__return_true',
)
);
}
/**
* Validate individual answer
*
* @param $value
* @param $question
* @return array
*/
private function validate_individual_answer($value, $question)
{
// Skip validation for empty values on non-required questions
if (is_null($value) && !$question->settings->required) {
return array(
'is_valid' => true,
'errors' => array(),
);
}
if (is_array($value)) {
// Check if question accepts array as value
if ($question->type !== 'checkbox') {
return array(
'is_valid' => false,
'errors' => array(
sprintf(
__("Question [%s] can't take an array as value.", 'userfeedback'),
$question->id
),
),
);
}
// Recursively check each value
$errors = array();
$is_valid = true;
foreach ($value as $inner_value) {
$answer_validation = $this->validate_individual_answer($inner_value, $question);
$is_valid = $is_valid && $answer_validation['is_valid'];
$errors = array_merge($errors, $answer_validation['errors']);
}
return array(
'is_valid' => $is_valid,
'errors' => $errors,
);
} else {
$errors = array();
$is_valid = true;
// Check if string has less than 400 characters
if (strlen($value) > 400) {
$is_valid = false;
$errors[] = __('Value exceeds 400 characters.', 'userfeedback');
}
// If question accepts multiple values, check if $value is a valid option
$types_with_options = array('checkbox', 'radio-button');
if (in_array($question->type, $types_with_options)) {
$available_options = $question->config->options;
$value_is_allowed = in_array($value, $available_options);
$is_valid = $is_valid && $value_is_allowed;
if (!$value_is_allowed) {
$errors[] = sprintf(
__('Value "%s" is not allowed', 'userfeedback'),
$value
);
}
} elseif ($question->type === 'email') {
// Validate email
$filtered_email = sanitize_email($value);
if (empty($filtered_email)) {
$is_valid = false;
$errors[] = sprintf(
__('The provided email is not valid', 'userfeedback'),
$value
);
}
}
// Validations for star-rating and nps are included in the question-types addon
return apply_filters(
'userfeedback_answer_validation',
array(
'is_valid' => $is_valid,
'errors' => $errors,
),
$value,
$question
);
}
}
/**
* Validate Response answers
*
* @param $answers
* @param $survey_id
* @return array
*/
private function validate_response_answers($answers, $survey_id)
{
$errors = array();
$success = true;
$survey = UserFeedback_Survey::find($survey_id);
// Check if Survey exists
if (!$survey) {
return array(
'success' => false,
'errors' => array(
sprintf(
__('Survey with id %s does not exist.', 'userfeedback'),
$survey_id
),
),
'status' => 404,
);
}
// Basic validation
foreach ($answers as $answer) {
$question_id = $answer['question_id'];
$value = $answer['value'];
$question = UserFeedback_Survey::search_question($survey, $question_id);
$answer_validation = $this->validate_individual_answer($value, $question);
$success = $success && $answer_validation['is_valid'];
if (!$answer_validation['is_valid']) {
$errors[] = array(
'question_id' => $question_id,
'errors' => $answer_validation['errors'],
);
}
}
return array(
'success' => $success,
'errors' => $errors,
);
}
/**
* Save Survey response
*
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function save_survey_response(WP_REST_Request $request)
{
if (!wp_verify_nonce($request->get_param('nonce'), "userfeedback_survey_submission-{$request['id']}")) {
return new WP_REST_Response(null, 403);
}
$survey_id = $request['id'];
$survey = (new UserFeedback_Survey())->find($survey_id);
if('publish' !== $survey->status){
return new WP_REST_Response(
array(
'success' => false,
'errors' => array(
__('Survey is not available at the moment.', 'userfeedback'),
),
),
400
);
}
$id_address = UserFeedback_Device_Detect::ip();
$os = UserFeedback_Device_Detect::os();
$browser = UserFeedback_Device_Detect::browser();
$device = UserFeedback_Device_Detect::deviceType();
// Validate Answers
$validation = $this->validate_response_answers($request->get_json_params()['answers'], $survey_id);
if (!$validation['success']) {
$status = isset($validation['status']) ? $validation['status'] : 400;
return new WP_REST_Response(
array(
'success' => false,
'errors' => $validation['errors'],
),
$status
);
}
// Sanitize requests answers
$request_params = $request->get_json_params();
if ( ! empty( $request_params['answers'] ) ) {
foreach( $request_params['answers'] as $index => $answer ) {
// Sanitize answer's value.
$value = null;
if ( is_string( $answer['value'] ) ) {
$value = sanitize_text_field( $answer['value'] );
} elseif ( is_numeric( $answer['value'] ) ) {
$value = intval( $answer['value'] );
}
elseif (
is_array( $answer['value'] )
&& ! empty( $answer['value'] )
) {
$value = array_map( 'sanitize_text_field', $answer['value'] );
}
$request_params['answers'][ $index ]['value'] = $value;
// Sanitize answer's extras.
if ( ! empty( $answer['extra'] ) ) {
foreach ( $answer['extra'] as $key => $extra ) {
$extra = sanitize_text_field( $extra );
$request_params['answers'][ $index ]['extra'][ $key ] = $extra;
}
}
}
}
if ( ! empty( $request_params['page_submitted'] ) ) {
if ( ! empty( $request_params['page_submitted']['id'] ) ) {
$request_params['page_submitted']['id'] = intval( $request_params['page_submitted']['id'] );
}
if ( ! empty( $request_params['page_submitted']['name'] ) ) {
$request_params['page_submitted']['name'] = sanitize_text_field( $request_params['page_submitted']['name'] );
}
}
$response_id = UserFeedback_Response::create(
array_merge(
$request_params,
array(
'survey_id' => sanitize_text_field($survey_id),
'user_ip' => $id_address,
'user_browser' => $browser,
'user_os' => $os,
'user_device' => $device,
)
)
);
do_action('userfeedback_survey_response', $survey_id, $response_id, $request->get_json_params());
return new WP_REST_Response(
array(
'success' => true,
'response_id' => $response_id,
)
);
}
/**
* Record Survey impression
*
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function record_survey_impression(WP_REST_Request $request)
{
if (!wp_verify_nonce($request->get_param('nonce'), "userfeedback_survey_impression-{$request['id']}")) {
return new WP_REST_Response(null, 403);
}
$survey_id = $request['id'];
UserFeedback_Survey::record_impression($survey_id);
return new WP_REST_Response(null, 204);
}
/**
* Load frontend styles
*
* @return void
*/
public function enqueue_styles_and_scripts_for_surveys()
{
global $wp_query, $post;
$user_is_admin = current_user_can('administrator');
if (empty($post) || ($user_is_admin && !apply_filters('userfeedback_enable_surveys_for_admins', false))) {
return;
}
$surveys = $this->get_surveys_for_current_page();
$surveys = array_map(
function ($survey) {
$survey->nonces = array(
'submission' => wp_create_nonce("userfeedback_survey_submission-{$survey->id}"),
'impression' => wp_create_nonce("userfeedback_survey_impression-{$survey->id}"),
);
return $survey;
},
$surveys
);
/*
* If there are no Surveys available, we don't enqueue the scripts
*/
if (sizeof($surveys) > 0 || has_shortcode($post->post_content, UserFeedback_Shortcodes::$USERFEEDBACK_SURVEY_SHORTCODE)) {
$this->enqueue_frontend_styles();
$this->enqueue_base_frontend_scripts();
$this->enqueue_frontend_scripts_for_surveys($surveys);
}
}
/**
* Get Surveys available for the current page
*
* @return array
*/
public function get_surveys_for_current_page()
{
$surveys = UserFeedback_Survey::where(
array(
'status' => 'publish',
'publish_at' => null,
)
)->or_where(
array(
'status' => 'publish',
array(
'publish_at',
'<',
current_time('mysql', true),
),
)
)->select(array('title', 'questions', 'settings'))
->sort('id', 'desc')
->get();
$surveys = array_map(
function ($survey) {
$thank_you_config = $survey->settings->thank_you;
if ($thank_you_config->type === 'redirect') {
$page_id = $thank_you_config->redirect_to->id;
if ('publish' == get_post_status($page_id)) {
$thank_you_config->redirect_url = get_permalink($page_id);
}
}
$survey->cookie_name = self::get_survey_cookie_name($survey);
return $survey;
},
$surveys
);
return $surveys;
}
/**
* Get frontend asset full URL
*
* @since 1.0.0
* @param $path
* @return mixed|void
*/
public static function get_frontend_asset_url($path)
{
return esc_url(
apply_filters(
'userfeedback_frontend_assets_url',
plugins_url($path, USERFEEDBACK_PLUGIN_FILE),
$path
)
);
}
/**
* Enqueue frontend styles
*
* @return void
*/
public function enqueue_frontend_styles()
{
// Enqueue with super low priority so these load after theme CSS files
add_action(
'wp_head',
function () {
wp_enqueue_style(
'userfeedback-frontend-styles',
$this->get_frontend_asset_url('/assets/vue/css/frontend.css'),
array(),
userfeedback_get_asset_version()
);
$userfeedback_settings = userfeedback_get_options();
// load custom fonts for the widget
if(isset($userfeedback_settings['widget_font']['family']) && $userfeedback_settings['widget_font']['family'] !== ''){
$font_family = str_replace(' ', '+', $userfeedback_settings['widget_font']['family']);
$font_weight = (isset($userfeedback_settings['widget_font']['weight']) && $userfeedback_settings['widget_font']['weight'] !== '') ? $userfeedback_settings['widget_font']['weight'] : 'regular';
$font_url = '';
if($font_weight === 'regular'){
$font_url = "https://fonts.googleapis.com/css?family={$font_family}&display=swap";
}else {
$font_url = "https://fonts.googleapis.com/css?family={$font_family}:{$font_weight}&display=swap";
}
wp_enqueue_style(
'userfeedback-frontend-fonts',
$font_url,
array(),
userfeedback_get_asset_version()
);
echo "<style>.userfeedback-widget * {
font-family: '{$userfeedback_settings['widget_font']['family']}' !important;
}</style>";
}
},
20
);
}
/**
* Load frontend scripts
*
* @return void
*/
public function enqueue_base_frontend_scripts()
{
wp_register_script(
'userfeedback-frontend-vendors',
$this->get_frontend_asset_url('/assets/vue/js/chunk-vendors.js'),
array(),
userfeedback_get_asset_version(),
true
);
wp_enqueue_script('userfeedback-frontend-vendors');
wp_register_script(
'userfeedback-frontend-common',
$this->get_frontend_asset_url('/assets/vue/js/chunk-common.js'),
array(),
userfeedback_get_asset_version(),
true
);
wp_enqueue_script('userfeedback-frontend-common');
wp_localize_script(
'userfeedback-frontend-common',
'userfeedback_addons_frontend',
array()
);
wp_register_script(
'userfeedback-frontend-widget',
$this->get_frontend_asset_url('/assets/vue/js/frontend.js'),
apply_filters('userfeedback_frontend_script_dependencies', array()),
userfeedback_get_asset_version(),
true
);
}
/**
* Enqueue and localize script for preview
*
* @param $survey
* @return void
*/
public function enqueue_frontend_scripts_for_preview($survey)
{
wp_enqueue_script('userfeedback-frontend-widget');
$localization_object = apply_filters(
'userfeedback_frontend_script_localization',
array(
'wp_rest_nonce' => wp_create_nonce('wp_rest'),
'rest_url' => rest_url(),
'assets' => plugins_url('/assets/vue', USERFEEDBACK_PLUGIN_FILE),
'is_pro' => userfeedback_is_pro_version(),
'is_licensed' => userfeedback_is_licensed(),
'use_survey' => $survey,
'addons' => array(),
'widget_settings' => userfeedback_get_frontend_widget_settings(),
)
);
wp_localize_script(
'userfeedback-frontend-widget',
'userfeedback_frontend',
$localization_object
);
}
/**
* Enqueue Frontend scripts
*
* @return void
*/
public function enqueue_frontend_scripts_for_surveys($surveys)
{
global $post, $current_user;
if (!is_admin() && $post) {
$user_roles = [];
if (is_user_logged_in()) {
$user_roles = $current_user->roles;
}
wp_enqueue_script('userfeedback-frontend-widget');
$localization_object = apply_filters(
'userfeedback_frontend_script_localization',
array(
'wp_rest_nonce' => wp_create_nonce('wp_rest'),
'rest_url' => rest_url(),
'assets' => plugins_url('/assets/vue', USERFEEDBACK_PLUGIN_FILE),
'is_pro' => userfeedback_is_pro_version(),
'is_licensed' => userfeedback_is_licensed(),
'surveys' => $surveys,
'widget_settings' => userfeedback_get_frontend_widget_settings(),
'is_preview' => false,
'integrations' => array(),
'addons' => get_option('userfeedback_parsed_addons'),
'current_page' => array(
'id' => $post->ID,
'name' => $post->post_title,
),
'logic' => [
'user_logged_in' => is_user_logged_in(),
'page_type' => userfeedback_get_type_of_page(),
'post_type' => (is_singular()) ? get_post_type() : false,
'post_id' => (is_singular()) ? $post->ID : false,
'taxonomy' => userfeedback_get_taxonomy(),
'taxonomy_term' => userfeedback_get_term(),
'current_url' => userfeedback_get_current_url(),
'referrer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false,
],
'disable_all_surveys' => userfeedback_disable_all_surveys(),
'show_specific_survey' => userfeedback_show_specific_survey(),
'is_singular' => is_singular(),
)
);
wp_localize_script(
'userfeedback-frontend-widget',
'userfeedback_frontend',
$localization_object
);
}
}
/**
* Get cookie name for survey
*
* @param $survey
* @return string
*/
public static function get_survey_cookie_name($survey)
{
return self::$USERFEEDBACK_SURVEY_COOKIE_PREFIX . $survey->id;
}
}
new UserFeedback_Frontend();