/home/ivoiecob/www/wp-content/plugins/wp-social-reviews/app/Services/Helper.php
<?php
namespace WPSocialReviews\App\Services;
use WPSocialReviews\Framework\Support\Arr;
if (!defined('ABSPATH')) {
exit;
}
class Helper
{
static $loadedTemplates = [];
public static function getAccessPermission()
{
return apply_filters('wpsr_admin_permission', 'manage_options');
}
/**
* Get allowed HTML tags for wp_kses sanitization
* This defines what HTML is safe to display in review content
*
* @return array Allowed HTML tags and their attributes
*/
public static function allowedHtmlTags()
{
$allowed_tags = array(
'br' => [],
'p' => [
'class' => [],
],
'strong' => [],
'em' => [],
'b' => [],
'i' => [],
'ul' => [],
'ol' => [],
'li' => [],
// Span for Read More/Less buttons and other inline elements
'span' => [
'class' => [],
'aria-label' => [],
'tabindex' => [],
],
// Links with safe attributes only
'a' => [
'href' => [],
'target' => [],
'rel' => [],
],
// Images for emojis and other safe images
'img' => [
'src' => [],
'alt' => [],
'class' => [],
'draggable' => [],
'role' => [],
],
);
// Explicitly remove any event handlers and dangerous attributes
return apply_filters('wpsocialreviews/allowed_html_tags', $allowed_tags);
}
/**
* Sanitize for DISPLAY - context aware
* This is the main method to use in templates
*/
public static function sanitizeText($text, $allow_html = false)
{
if (empty($text) || !is_string($text)) {
return '';
}
if ($allow_html) {
// Allow minimal safe HTML
$allowed_tags = static::allowedHtmlTags();
$text = wp_kses($text, $allowed_tags);
} else {
// Strip all HTML and escape
$text = wp_strip_all_tags($text);
$text = esc_html($text);
}
return $text;
}
/**
* Sanitize user-generated content for STORAGE
* Use this when saving data from API/external sources
*/
public static function sanitizeForStorage($text)
{
if (empty($text) || !is_string($text)) {
return '';
}
// Remove obvious dangerous content before storage
$text = static::removeObfuscatedScripts($text);
$text = static::removeEventHandlers($text);
$text = static::removeDangerousProtocols($text);
// Use strict allowed tags for social content
$allowed_tags = static::allowedHtmlTags();
$text = wp_kses($text, $allowed_tags);
return trim($text);
}
/**
* Remove obfuscated script tags like <scr<script>ipt>
*/
public static function removeObfuscatedScripts($text)
{
// Remove other dangerous tags
$dangerous_tags = [
'iframe', 'object', 'embed', 'applet', 'meta',
'link', 'style', 'form', 'input', 'button',
'textarea', 'select', 'svg', 'math', 'base'
];
foreach ($dangerous_tags as $tag) {
$text = preg_replace('/<\/?' . $tag . '[^>]*>/i', '', $text);
}
return $text;
}
/**
* Remove ALL event handlers (onclick, onmouseover, onerror, etc.)
*/
public static function removeEventHandlers($text)
{
// Remove event handlers with double quotes
$text = preg_replace('/\s*on\w+\s*=\s*"[^"]*"/i', '', $text);
// Remove event handlers with single quotes
$text = preg_replace('/\s*on\w+\s*=\s*\'[^\']*\'/i', '', $text);
// Remove event handlers without quotes
$text = preg_replace('/\s*on\w+\s*=\s*[^\s>]+/i', '', $text);
// Remove with spaces (on mouse over = "...")
$text = preg_replace('/\s*on\s+\w+\s*=\s*"[^"]*"/i', '', $text);
$text = preg_replace('/\s*on\s+\w+\s*=\s*\'[^\']*\'/i', '', $text);
return $text;
}
/**
* Remove dangerous protocols (javascript:, data:, vbscript:)
*/
public static function removeDangerousProtocols($text)
{
$text = preg_replace('/javascript\s*:/i', '', $text);
$text = preg_replace('/data\s*:/i', '', $text);
$text = preg_replace('/vbscript\s*:/i', '', $text);
$text = preg_replace('/file\s*:/i', '', $text);
return $text;
}
public static function shortNumberFormat($number)
{
if(empty($number)){
return 0;
}
$units = ['', 'K', 'M', 'B', 'T'];
for ($i = 0; $number >= 1000; $i++) {
$number /= 1000;
}
return round($number, 1) . $units[$i];
}
public static function getVideoDuration($duration)
{
$di = new \DateInterval($duration);
$hour = '';
if ($di->h > 0) {
$hour .= $di->h . ':';
}
return $hour . $di->i . ':' . sprintf('%02s', $di->s);
}
public static function numberWithCommas($number)
{
return number_format($number, 0, '.', ',');
}
public static function shortcodeAllowedPlatforms(){
return [
'reviews',
'twitter',
'youtube',
'instagram',
'facebook',
'tiktok',
'facebook_feed',
'testimonial'
];
}
/**
* Get total count of published templates
*
* @return int
*/
public static function getTemplateCount()
{
static $templateCount = null;
if (null === $templateCount) {
$templates = get_posts([
'post_type' => ['wp_social_reviews', 'wpsr_reviews_notify', 'wpsr_social_chats', 'wpsr_custom_source'],
'post_status' => 'publish',
'numberposts' => -1
]);
$templateCount = count($templates);
}
return $templateCount;
}
public static function getPostTypes($formatted = true){
$post_types = get_post_types(
[
'public' => true,
'show_in_nav_menus' => true
],
'objects'
);
$post_types = wp_list_pluck($post_types, 'label', 'name');
$post_types = array_diff_key($post_types, ['attachment']);
$post_types_list = [];
if (!empty($post_types) && !is_wp_error($post_types)) {
foreach ($post_types as $key => $post_type) {
if($formatted) {
$post_types_list[] = array(
'name' => $key,
'title' => $post_type
);
} else {
$post_types_list[$key] = $key;
}
}
}
return apply_filters('wpsocialreviews/available_post_types', $post_types_list);
}
/**
* Get all pages list
*
* @return array
* @since 1.1.0
*/
public static function getPagesList()
{
$pages = get_posts([
'post_type' => static::getPostTypes(false),
'post_status' => 'publish',
'numberposts' => -1,
'orderby' => 'title',
'order' => 'ASC',
]);
$page_list = array(array('id' => '-1', 'title' => __('Everywhere', 'wp-social-reviews')));
if (!empty($pages) && !is_wp_error($pages)) {
foreach ($pages as $page) {
$lang = '';
if(defined('POLYLANG_VERSION')){
$lang = pll_get_post_language( $page->ID, 'name');
$lang = $lang ? ' ('. $lang .')' : '';
}
$page_list[] = array('id' => $page->ID . '',
'title' => $page->post_title ? $page->post_title . $lang : __('Untitled',
'wp-social-reviews') . $lang,
'url' => get_permalink($page->ID)
);
}
}
return $page_list;
}
public static function getPostsByPostType($postType = 'post')
{
$posts = get_posts([
'post_type' => $postType,
'post_status' => 'publish',
'numberposts' => -1
]);
$post_lists = array(array('id' => '-1', 'title' => __('Everywhere', 'wp-social-reviews')));
if (!empty($posts) && !is_wp_error($posts)) {
foreach ($posts as $post) {
$post_lists[] = array(
'id' => $post->ID . '',
'title' => $post->post_title ? $post->post_title : __('Untitled',
'wp-social-reviews'),
'url' => get_permalink($post->ID)
);
}
}
return $post_lists;
}
public static function getShortCodeIds($content, $tag = 'wp_social_ninja', $selector = 'id')
{
if (false === strpos($content, '['.$tag)) {
return [];
}
preg_match_all('/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER);
if (empty($matches)) {
return [];
}
$ids = [];
foreach ($matches as $shortcode) {
if (count($shortcode) >= 2 && $tag === $shortcode[2]) {
// Replace braces with empty string.
$parsedCode = str_replace(['[', ']', '[', ']'], '', $shortcode[0]);
$result = shortcode_parse_atts($parsedCode);
if (!empty($result[$selector])) {
$ids[$result[$selector]] = $result[$selector];
}
}
}
return $ids;
}
public static function isTemplateMatched($settings)
{
global $post;
// hiding on desktop device
$hide_on_desktop = Arr::get($settings, 'hide_on_desktop');
if(!wp_is_mobile() && $hide_on_desktop === 'true'){
return false;
}
// hiding on mobile device
$hide_on_mobile = Arr::get($settings, 'hide_on_mobile');
if($hide_on_mobile === 'true' && wp_is_mobile()){
return false;
}
// let's check by post type
$postTypes = Arr::get($settings, 'post_types');
$pageList = Arr::get($settings, 'page_list', []);
$chat_lang = Arr::get($settings, 'chat_lang', '');
if(empty($postTypes) && empty($pageList) && $chat_lang && $chat_lang === get_locale()){
return true;
} else if(!empty($post) && !empty($postTypes)) {
if($postTypes && in_array($post->post_type, $postTypes) && $chat_lang && $chat_lang === get_locale()){
return true;
}
if ($chat_lang === '' && $postTypes && in_array($post->post_type, $postTypes)) {
return true;
}
} else {
$excludePages = Arr::get($settings, 'exclude_page_list', []);
if(!empty($post)) {
if (in_array($post->ID, $excludePages) || in_array('-1', $excludePages)) {
return false;
}
if(defined('WC_VERSION') && is_shop()){
$page_id = wc_get_page_id('shop');
} else {
$page_id = $post->ID;
}
// Validate if the config is valid for the current request
if((in_array($page_id, $pageList) || in_array('-1', $pageList)) && $chat_lang && $chat_lang === get_locale()){
return true;
}
if ((in_array($page_id, $pageList) || in_array('-1', $pageList)) && $chat_lang === '') {
return true;
}
}
}
return false;
}
public static function hasColumn( $table_name, $column_name ) {
global $wpdb;
// Create a cache key for this specific table and column check
$cache_key = 'wpsr_has_column_' . md5( $table_name . '_' . $column_name );
// Try to get the result from cache first
$cached_result = wp_cache_get( $cache_key, 'wpsr_column_checks' );
if ( $cached_result !== false ) {
return $cached_result;
}
// Validate DB_NAME is defined and safe
$db_name = defined( 'DB_NAME' ) ? DB_NAME : '';
if ( empty( $db_name )) {
return false;
}
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Schema check requires direct query
$column_exists = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s",
$db_name,
$table_name,
$column_name
));
$result = ! empty( $column_exists );
// Cache the result for 1 hour (3600 seconds)
wp_cache_set( $cache_key, $result, 'wpsr_column_checks', 3600 );
return $result;
}
/**
* Print internal content (not user input) without escaping.
*/
public static function printInternalString( $string ) {
echo $string; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
public static function customEncodeEmoji($content) {
$emoji = ['🩷'];
foreach ( $emoji as $emojum ) {
$emoji_char = html_entity_decode( $emojum );
if ( false !== strpos( $content, $emoji_char ) ) {
$content = preg_replace( "/$emoji_char/", $emojum, $content );
}
}
return $content;
}
public static function getSiteDefaultDateFormat($created_at)
{
$date_format = get_option('date_format');
return date_i18n($date_format, $created_at);
}
public static function isLocalUrl($url)
{
if(empty($url)){
return false;
}
return strpos($url, "wp-content") !== false ? true : false;
}
public static function isCustomFeedForTiktokInstalled()
{
$plugins = get_plugins();
$plugin_path = 'custom-feed-for-tiktok/custom-feed-for-tiktok.php';
if (isset($plugins[$plugin_path])) {
return true;
}
return false;
}
public static function initializeUploadDirectory($uploadDir)
{
if (!is_dir($uploadDir)) {
wp_mkdir_p($uploadDir);
}
$subDirIndexFile = $uploadDir . DIRECTORY_SEPARATOR . 'index.html';
if (!file_exists($subDirIndexFile)) {
@file_put_contents($subDirIndexFile, '');
}
$root = wp_upload_dir()['basedir'] . DIRECTORY_SEPARATOR . WPSOCIALREVIEWS_UPLOAD_DIR_NAME;
$rootIndexFile = $root . DIRECTORY_SEPARATOR . 'index.html';
if (!file_exists($rootIndexFile)) {
@file_put_contents($rootIndexFile, '');
}
$htaccessFile = $root . DIRECTORY_SEPARATOR . '.htaccess';
// Replace only if .htaccess exists and contains only "deny from all"
if (file_exists($htaccessFile)) {
$content = trim(file_get_contents($htaccessFile));
if (strtolower($content) === 'deny from all') {
wp_delete_file($htaccessFile);
}
}
// Create .htaccess if it doesn't exist (either newly or just deleted)
if (!file_exists($htaccessFile)) {
@file_put_contents(
$root. '/.htaccess', // Intentionally using $root to ensure the .htaccess is created in the correct directory, so do not replace with $htaccessFile as it may point to an unintended path
file_get_contents(__DIR__ . '/Libs/Stubs/htaccess.stub')
);
}
}
public static function getOptimizeImageFormat()
{
$image_format = get_option('wpsr_global_settings', []);
return Arr::get($image_format, 'global_settings.advance_settings.optimize_image_format', 'jpg');
}
public static function getEncryptionErrorData()
{
$site_url = sprintf( '<a href="%s">%s<a/>', esc_url( 'https://wpsocialninja.com/docs/fixing-access-token-decryption-failed-in-wp-social-ninja/' ), __( 'More Information', 'wp-social-reviews' ) );
return [
// translators: %1$s is a link to the documentation for fixing the decryption issue.
'message' => sprintf(__('Your access token could not be decrypted on this website. Need help? Visit our website for easy steps to prevent this issue in the future. %1$s.', 'wp-social-reviews'), $site_url),
'code' => 999
];
}
/**
* Safely unserialize data.
*
* @param string $data The serialized data.
* @return mixed The unserialized data or the original data if not serialized.
*/
public static function safeUnserialize($data)
{
if (is_serialized($data)) { // Don't attempt to unserialize data that wasn't serialized going in.
return @unserialize(trim($data), ['allowed_classes' => false]);
}
return $data;
}
/**
* Normalize text for robust matching:
* - Trim whitespace
* - Unicode normalize (NFKD) when intl is available
* - Transliterate to ASCII when possible
* - Lowercase with multibyte support
* - Handle hashtags and mentions
*
* This improves matching of "fancy" unicode text like πΎπππππ.
*
* @param string $text
* @return string
*/
public static function normalizeText($text)
{
$text = trim((string) $text);
// Normalize hashtags and mentions to match the processed format
// The feed processing converts # to HASHTAG and @ to MENTION
$text = str_replace(array('#', '@'), array('HASHTAG', 'MENTION'), $text);
// Normalize Unicode to NFKD if intl Normalizer is available
if (class_exists('Normalizer')) {
$text = \Normalizer::normalize($text, \Normalizer::FORM_KD);
}
// Try to transliterate to ASCII for better matching of stylized characters
if (function_exists('transliterator_transliterate')) {
$transliterated = transliterator_transliterate('Any-Latin; Latin-ASCII', $text);
if ($transliterated !== null) {
$text = $transliterated;
}
} else {
// Fallback to iconv if available
$converted = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
if ($converted !== false) {
$text = $converted;
}
}
// Lowercase with multibyte support
if (function_exists('mb_strtolower')) {
$text = mb_strtolower($text, 'UTF-8');
} else {
$text = strtolower($text);
}
return $text;
}
}