Files
2026-04-28 22:27:25 +02:00

1126 lines
30 KiB
PHP

<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* 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'));
add_filter( 'userfeedback_detect_page_id', array( $this, 'userfeedback_detect_page_id' ) );
}
public function userfeedback_detect_page_id()
{
$page_id = get_queried_object_id();
if ( empty( $page_id ) ) {
if (
function_exists( 'is_shop' ) &&
is_shop() &&
function_exists( 'wc_get_page_id' )
) {
$page_id = wc_get_page_id( 'shop' );
}
}
return $page_id;
}
/**
* 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(
// translators: %s is the question ID.
__("Question [%s] can't take an array as value.", 'userfeedback-lite'),
$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-lite');
}
// 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(
// translators: %s is the value that was submitted.
__('Value "%s" is not allowed', 'userfeedback-lite'),
$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-lite'),
$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(
// translators: %s is the survey ID.
__('Survey with id %s does not exist.', 'userfeedback-lite'),
$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-lite'),
),
),
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 $post;
if (empty($post)) {
return;
}
$user_is_admin = current_user_can('administrator');
$show_surveys_to_admin = apply_filters('userfeedback_enable_surveys_for_admins', false);
// If admin is excluded from surveys, show the exclusion banner instead
if ($user_is_admin && !$show_surveys_to_admin) {
$this->render_exclusion_banner('administrator');
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
);
// For icon-choice question type, we need to extract the svg markup and send to frontend to render
foreach($surveys as &$survey) {
foreach($survey->questions as $question) {
if ($question->type === 'icon-choice') {
foreach($question->config->options as $option) {
$svg_file_path = plugin_dir_path(USERFEEDBACK_PLUGIN_FILE) . 'assets/vue/icon-choices/svgs/' . $option->icon->style . '/' . $option->icon->icon . '.svg';
if (file_exists($svg_file_path)) {
$svg_content = file_get_contents($svg_file_path);
$option->icon = $svg_content;
}
}
}
}
}
$this->register_chunc_scripts();
/*
* 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);
}
}
/**
* Process individual rules with logic handling like the JavaScript function.
*
* @param array $rules Array of rules to process (AND or OR rules).
* @return array Array of booleans for each rule.
*/
private function process_rules($rules) {
$result = [];
global $post, $current_user;
$user_roles = $current_user->roles;
foreach ($rules as $rule) {
$logic = $rule->logic;
if (empty($rule->data)) {
continue;
}
$value = isset($rule->data->id) ? $rule->data->id : $rule->data;
// Remove trailing slashes from input value
if (is_string($value)) {
$value = preg_replace('/\/+$/', '', $value);
}
switch ($logic) {
case 'user_logged_in': {
$user_logged_in = is_user_logged_in();
if ($value === 'user_logged_in') {
$result[] = $user_logged_in;
} elseif ($value === 'user_logged_out') {
$result[] = !$user_logged_in;
}
break;
}
case 'page_type': {
$page_type = userfeedback_get_type_of_page();
$result[] = isset($page_type) && $value === $page_type;
break;
}
case 'page_or_post_is': {
$post_id = (is_singular()) ? $post->ID : false;
$result[] = isset($post_id) && $value === $post_id;
break;
}
case 'page_or_post_is_not': {
$post_id = (is_singular()) ? $post->ID : false;
$result[] = isset($post_id) && $value !== $post_id;
break;
}
case 'post_type_is': {
$post_type = (is_singular()) ? get_post_type() : false;
$result[] = isset($post_type) && $value === $post_type;
break;
}
case 'post_type_is_not': {
$post_type = (is_singular()) ? get_post_type() : false;
$result[] = isset($post_type) && $value !== $post_type;
break;
}
case 'taxonomy_is': {
$taxonomy = userfeedback_get_taxonomy();
$result[] = isset($taxonomy) && $value === $taxonomy;
break;
}
case 'taxonomy_is_not': {
$taxonomy = userfeedback_get_taxonomy();
$result[] = isset($taxonomy) && $value !== $taxonomy;
break;
}
case 'taxonomy_term_is': {
$taxonomy_term_ids = userfeedback_get_current_post_terms();
$result[] = isset( $value ) && is_array( $taxonomy_term_ids ) && in_array( $value, $taxonomy_term_ids, true );
break;
}
case 'taxonomy_term_is_not': {
$taxonomy_term_ids = userfeedback_get_current_post_terms();
$result[] = isset( $value ) && is_array( $taxonomy_term_ids ) && ! in_array( $value, $taxonomy_term_ids, true );
break;
}
case 'referrer': {
$referrer = isset($_SERVER['HTTP_REFERER']) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$result[] = isset($referrer) && (strpos($referrer, $value) !== false || strpos($value, $referrer) !== false);
break;
}
case 'page_url_is': {
$current_url = userfeedback_get_current_url();
$result[] = isset($current_url) && strpos($current_url, $value) !== false;
break;
}
case 'page_url_is_not': {
$current_url = userfeedback_get_current_url();
$result[] = isset($current_url) && strpos($current_url, $value) === false;
break;
}
case 'page_url_contains': {
$current_url = userfeedback_get_current_url();
$result[] = strpos($current_url, $value) !== false;
break;
}
case 'page_url_not_contains': {
$current_url = userfeedback_get_current_url();
$result[] = strpos($current_url, $value) === false;
break;
}
case 'user_role': {
$result[] = in_array($value, $user_roles, true);
break;
}
case 'cookie_exist': {
$cookie_name = $value;
$result[] = isset($_COOKIE[$cookie_name]);
break;
}
case 'cookie_not_exist': {
$cookie_name = $value;
$result[] = !isset($_COOKIE[$cookie_name]);
break;
}
case 'query_parameter_contains': {
$query_param = $value;
$result[] = isset($_GET[$query_param]) && !empty($_GET[$query_param]); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonces are not appropriate for frontend public GET params used for survey targeting.
break;
}
case 'query_parameter_not_contains': {
$query_param = $value;
$result[] = !isset($_GET[$query_param]); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonces are not appropriate for frontend public GET params used for survey targeting.
break;
}
case 'query_parameter_regex': {
$pattern = $value;
$found = false;
foreach ($_GET as $param => $param_value) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonces are not appropriate for frontend public GET params used for survey targeting.
if (preg_match($pattern, $param_value)) {
$found = true;
break;
}
}
$result[] = $found;
break;
}
default:
// Handle unknown logic cases if needed
break;
}
}
return $result;
}
/**
* Main function to filter and return all matched surveys based on the rules.
*
* @param array $surveys Array of surveys.
* @return array Array of all matching surveys.
*/
private function filter_surveys_by_advanced_targeting($surveys) {
$matchedSurveys = [];
foreach ($surveys as $survey) {
if (!isset($survey->settings) || !is_object($survey->settings)) {
continue;
}
$settings = $survey->settings;
if (!isset($settings->targeting) || !is_object($settings->targeting)) {
continue;
}
$targeting = $settings->targeting;
if (isset($targeting->pages) && $targeting->pages === 'all') {
// This survey is for all pages
$matchedSurveys[] = $survey;
continue;
}
if (!isset($targeting->page_rules) || !is_array($targeting->page_rules)) {
continue;
}
$rules = $targeting->page_rules;
$andRules = [];
$orRules = [];
foreach ($rules as $index => $rule) {
$nextRule = isset($rules[$index + 1]) ? $rules[$index + 1] : false;
if (
(
empty($rule->operator) && $nextRule && isset($nextRule->operator) && $nextRule->operator === '||'
)
||
(
isset($rule->operator) && $rule->operator === '||'
)
) {
$orRules[] = $rule;
} else {
$andRules[] = $rule;
}
}
// Process AND rules
$processAndRules = $this->process_rules($andRules);
$andRulesResult = empty($processAndRules) || !in_array(false, $processAndRules, true);
// Process OR rules
$processOrRules = $this->process_rules($orRules);
$orRulesResult = empty($processOrRules) || in_array(true, $processOrRules, true);
// Final result: add survey if it passes both AND and OR rule conditions
if ($andRulesResult && $orRulesResult) {
$matchedSurveys[] = $survey;
}
}
return $matchedSurveys;
}
/**
* 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', 'type'))
->sort('id', 'desc')
->get();
$surveys = $this->filter_surveys_by_advanced_targeting(
$this->filter_surveys_by_geo_restrictions( $surveys )
);
$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);
}
} else if ($thank_you_config->type === 'conditional_redirect' && is_array($thank_you_config->conditions)) {
foreach ($thank_you_config->conditions as &$condition) {
if (isset($condition->page->id)) {
$page_id = $condition->page->id;
if ('publish' == get_post_status($page_id)) {
$condition->redirect_url = get_permalink($page_id);
}
}
}
}
$survey->cookie_name = self::get_survey_cookie_name($survey);
return $survey;
},
$surveys
);
return $surveys;
}
/**
* Filters an array of survey objects based on their geographic restrictions.
*
* This function iterates through each survey in the provided array and checks if it has
* geo-targeting settings. If geo-targeting is enabled and set to 'select' specific
* countries, it extracts the country codes and applies a filter hook
* 'userfeedback_survey_show_in_country' to determine if the survey should be shown
* based on the user's location (determined by the hook). Surveys without valid
* geo-targeting settings are always included in the returned array.
*
* @since 1.6.0
*
* @param array $surveys An array of survey objects. Each survey object is expected
* to potentially have a 'settings' property which is an object,
* and that 'settings' object may have a 'geo_targeting' property
* (also an object) containing targeting information.
*
* @return array An array containing only the surveys that should be displayed based
* on their geo-targeting restrictions (or those without restrictions).
*/
public function filter_surveys_by_geo_restrictions( $surveys ) {
if ( ! is_array( $surveys ) ) {
return $surveys;
}
return array_filter( $surveys, function ( $survey ) {
$should_match = true;
if (
! isset( $survey->settings ) ||
! is_object( $survey->settings ) ||
! isset( $survey->settings->geo_targeting ) ||
! is_object( $survey->settings->geo_targeting )
) {
return $should_match;
}
$geo_targeting = $survey->settings->geo_targeting;
if (
isset( $geo_targeting->target ) &&
$geo_targeting->target === 'select' &&
is_array( $geo_targeting->countries )
) {
$country_codes = array_map(
function( $country ) {
return strtolower( $country->value );
},
array_filter( $geo_targeting->countries, function( $country ) {
return (
is_object( $country ) &&
isset( $country->value ) &&
! empty( $country->value )
);
} )
);
$should_match = apply_filters(
'userfeedback_survey_show_in_country',
true,
array(
'target_countries' => $country_codes
)
);
}
return $should_match;
} );
}
/**
* 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: \'' . esc_attr( $userfeedback_settings['widget_font']['family'] ) . '\' !important; }</style>';
}
},
20
);
}
private function register_chunc_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_register_script(
'userfeedback-frontend-common',
$this->get_frontend_asset_url('/assets/vue/js/chunk-common.js'),
array(),
userfeedback_get_asset_version(),
true
);
wp_localize_script(
'userfeedback-frontend-common',
'userfeedback_addons_frontend',
array()
);
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if (
in_array(
$handle,
array(
'userfeedback-frontend-vendors',
'userfeedback-frontend-common'
),
true
)
) {
return str_replace( ' src', ' defer src', $tag );
}
return $tag;
}, 10, 2 );
}
/**
* Load frontend scripts
*
* @return void
*/
public function enqueue_base_frontend_scripts()
{
wp_register_script(
'userfeedback-frontend-widget',
$this->get_frontend_asset_url('/assets/vue/js/frontend.js'),
apply_filters('userfeedback_frontend_script_dependencies', array( 'userfeedback-frontend-vendors', 'userfeedback-frontend-common' )),
userfeedback_get_asset_version(),
true
);
// This adds `defer` attribute to js files
add_filter( 'script_loader_tag', function ( $tag, $handle ) {
if (
in_array(
$handle,
array(
'userfeedback-frontend-widget'
),
true
)
) {
return str_replace( ' src', ' defer src', $tag );
}
return $tag;
}, 10, 2 );
}
/**
* 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,
),
'disable_all_surveys' => userfeedback_disable_all_surveys(),
'show_specific_survey' => userfeedback_show_specific_survey(),
'is_singular' => is_singular(),
'is_clarity_active' => function_exists( 'clarity_on_activation' ),
)
);
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;
}
/**
* Render exclusion banner for logged-in users who are excluded from seeing surveys
*
* @since 1.x.x
* @param string $exclusion_reason Reason for exclusion (e.g., 'administrator', 'role')
* @return void
*/
private function render_exclusion_banner($exclusion_reason = 'administrator')
{
// Wyłączone: nie pokazujemy banera "Survey is Hidden for Administrators" na froncie.
return;
// Get banner configuration with filter for customization
$config = apply_filters('userfeedback_exclusion_banner_config', array(
'title' => __('Survey is Hidden for Administrators', 'userfeedback'),
'message' => __('Surveys are hidden for administrator accounts to keep your survey results accurate. To see surveys on your site, use an incognito window or log out.', 'userfeedback')
), $exclusion_reason);
// Enqueue banner assets
$this->enqueue_banner_assets($config);
}
/**
* Enqueue banner CSS and JS assets
*
* @since 1.x.x
* @param array $config Banner configuration
* @return void
*/
private function enqueue_banner_assets($config)
{
wp_enqueue_style(
'userfeedback-admin-banner',
plugins_url('/assets/css/admin-exclusion-banner.css', USERFEEDBACK_PLUGIN_FILE),
array(),
userfeedback_get_asset_version()
);
wp_enqueue_script(
'userfeedback-admin-banner',
plugins_url('/assets/js/admin-exclusion-banner.js', USERFEEDBACK_PLUGIN_FILE),
array(),
userfeedback_get_asset_version(),
true
);
// Localize banner config for JS
wp_localize_script(
'userfeedback-admin-banner',
'userfeedback_banner_config',
$config
);
// Add banner HTML to footer
add_action('wp_footer', array($this, 'render_banner_html'), 999);
}
/**
* Render banner HTML in footer
*
* @since 1.x.x
* @return void
*/
public function render_banner_html()
{
$logo_url = plugins_url('/assets/vue/img/user-feedback-logo.svg', USERFEEDBACK_PLUGIN_FILE);
?>
<div id="userfeedback-admin-banner" class="userfeedback-admin-banner" style="display:none;">
<div class="userfeedback-admin-banner__card">
<button class="userfeedback-admin-banner__close" type="button" aria-label="<?php esc_attr_e('Close', 'userfeedback'); ?>">
<svg width="12" height="12" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z" fill="currentColor"/>
</svg>
</button>
<div class="userfeedback-admin-banner__media">
<div class="userfeedback-admin-banner__logo">
<img src="<?php echo esc_url($logo_url); ?>" alt="<?php esc_attr_e('UserFeedback', 'userfeedback'); ?>">
</div>
<div class="userfeedback-admin-banner__body">
<h3 class="userfeedback-admin-banner__title"></h3>
<p class="userfeedback-admin-banner__message"></p>
</div>
</div>
</div>
</div>
<?php
}
}
// Wyłączone: cały frontend UserFeedback (widget ankiet) nie jest ładowany na stronie publicznej.
// new UserFeedback_Frontend();