File "Utils.php"

Full Path: /home/lacostenacom/public_html/wp/wp-content/plugins/siteleads/lib/Utils.php
File size: 11.67 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace SiteLeads;

use SiteLeads\Admin\Admin;
use SiteLeads\Core\AssetsRegistry;
use SiteLeads\Core\Flags;

class Utils {


	public static function getFilePath( $path ) {
		return SITELEADS_ROOT_DIR . "$path";
	}

	public static function getUrl( $path ) {
		return SITELEADS_ROOT_URL . "/$path";
	}


	public static function getAssetName( $name ) {
		return AssetsRegistry::getAssetHandle( $name );
	}

	public static function isSiteLeadsAdminPage() {
		global $pagenow;

		if ( substr( $pagenow, 0, - 4 ) === 'admin' ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$page = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : false;

			$available_pages = apply_filters(
				'siteleads_admin_pages',
				array(
					'siteleads',
					'siteleads-analytics',
					'siteleads-messages',
					'siteleads-leads',
					'siteleads-my-account',
					'siteleads-upgrade',
				)
			);

			return in_array( $page, $available_pages, true );
		}

		return false;
	}

	public static function getIconAsset( $filename ) {

		$svg_path = SITELEADS_ROOT_DIR . 'assets/icons/' . $filename;
		$ret      = '<!-- SVG not found -->';
		if ( file_exists( $svg_path ) ) {
			$ret = file_get_contents( $svg_path );
		}

		return $ret;
	}

	public static function printIconAsset( $filename ) {
		// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- We trust our own assets
		echo self::getIconAsset( $filename );
	}

	public static function getAssetFile( $filename ) {

		$file_path = SITELEADS_ROOT_DIR . 'assets/' . $filename;

		if ( file_exists( $file_path ) ) {
			return file_get_contents( $file_path );
		} else {
			return '<!-- FILE not found -->';
		}
	}

	/**
	 * Returns the widget ID if in preview mode, null if preview mode but no widget ID, false if not in preview mode
	 *
	 * @return string|null|false
	 */
	public static function getPreviewedWidgetId() {

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we don't verify for nonce, as this is only used to detect if we're in preview mode, and the presence of a valid widget ID is not a security concern
		$preview = isset( $_GET['siteleads-preview'] ) ? filter_var( sanitize_text_field( wp_unslash( $_GET['siteleads-preview'] ) ), FILTER_VALIDATE_BOOLEAN ) : false;

		if ( $preview ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we don't verify for nonce, as this is only used to detect if we're in preview mode, and the presence of a valid widget ID is not a security concern
			$widgetId = isset( $_GET['widgetId'] ) ? sanitize_text_field( wp_unslash( $_GET['widgetId'] ) ) : null;

			if ( $widgetId ) {
				return $widgetId;
			}

			return null;
		}

		return false;
	}

	/**
	 * Returns the widget ID if in live preview mode, null if preview mode but no widget ID, false if not in preview mode
	 *
	 * @return string|null|false
	 */
	public static function getLivePreviewedWidgetId() {

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we don't verify for nonce, as this is only used to detect if we're in live preview mode, and the presence of a valid widget ID is not a security concern
		$livePreview = isset( $_GET['siteleads-live-preview'] ) ? filter_var( sanitize_text_field( wp_unslash( $_GET['siteleads-live-preview'] ) ), FILTER_VALIDATE_BOOLEAN ) : false;

		if ( $livePreview ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- we don't verify for nonce, as this is only used to detect if we're in live preview mode, and the presence of a valid widget ID is not a security concern
			$widgetId = isset( $_GET['widgetId'] ) ? sanitize_text_field( wp_unslash( $_GET['widgetId'] ) ) : null;

			if ( $widgetId ) {
				return $widgetId;
			}

			return null;
		}

		return false;
	}

	public static function isInsideBoundingBox( $bbox, $coordinates ) {

		try {
			if ( $bbox && $coordinates && is_array( $bbox ) && is_array( $coordinates ) && isset( $coordinates['lat'] ) && isset( $coordinates['long'] ) ) {
				list( $min_latitude, $max_latitude, $min_longitude, $max_longitude ) = $bbox;
				$lat  = $coordinates['lat'];
				$long = $coordinates['long'];

				return $lat >= $min_latitude && $lat <= $max_latitude && $long >= $min_longitude && $long <= $max_longitude;
			}
		} catch ( \Exception $e ) {
			return false;
		}

		return false;
	}

	public static function isBlogPage() {
		global $wp_query;
		$blogPageID    = intval( get_option( 'page_for_posts' ) );
		$currentPageID = isset( $wp_query->query_vars['page_id'] ) ? $wp_query->query_vars['page_id'] : false;
		if ( isset( $wp_query->queried_object_id ) && $currentPageID == false ) {
			$currentPageID = $wp_query->queried_object_id;
		}

		if ( $currentPageID == $blogPageID ) {
			return $blogPageID;
		}

		return false;
	}

	public static function getShopPageId() {
		if ( ! function_exists( 'wc_get_page_id' ) ) {
			return null;
		}
		$shopPageID = wc_get_page_id( 'shop' );

		return $shopPageID;
	}


	public static function normalizeTimezone( $timezone ) {
		if ( $timezone === 'website' ) {
			return wp_timezone();
		}

			return new \DateTimeZone( $timezone );
	}

	private static function convertTimeToSeconds( $time ) {
		$parts = explode( ':', $time );
		if ( count( $parts ) === 2 ) {
			$hours   = intval( $parts[0] );
			$minutes = intval( $parts[1] );

			return $hours * 3600 + $minutes * 60;
		}

		return false;
	}

	/**
	 * Check if a given timestamp is within a date+time range in a specific timezone.
	 *
	 * @param string $timezone "label#offsetHours" OR "website"
	 * @param string|null $startDate "YYYY-MM-DD"
	 * @param string|null $endDate "YYYY-MM-DD"
	 * @param string|null $startTime "HH:MM"
	 * @param string|null $endTime "HH:MM"
	 * @param int|null $timestamp UTC timestamp (defaults to now)
	 *
	 * @return bool
	 */
	public static function isBetweenDatesWithTimezone(
		$timezone,
		$startDate,
		$startTime,
		$endDate,
		$endTime
	) {
		if ( empty( $startDate ) || empty( $startTime ) ) {
			return true;
		}

		$tz      = self::normalizeTimezone( $timezone );
		$current = new \DateTime( 'now', $tz );

		$startDateTime = new \DateTime( $startDate . ' ' . $startTime, $tz );
		$endDateTime   = new \DateTime( ( $endDate ?? $startDate ) . ' ' . ( $endTime ?? $startTime ), $tz );

		return $current >= $startDateTime && $current < $endDateTime;
	}

	/**
	 * Check if the current time (based on timezone) is within a start–end range.
	 *
	 * @param string $timezone "Europe\Bucharest" or "website"
	 * @param string $startTime e.g. "09:00"
	 * @param string $endTime e.g. "17:00"
	 *
	 * @return bool
	 */
	public static function isTimeWithinRange( $timezone, $startTime, $endTime ) {

		$tz = self::normalizeTimezone( $timezone );
		$dt = new \DateTime( 'now', $tz );

		// get number of secconds since midnight in $dt's timezone
		$currentSeconds = (int) $dt->format( 'H' ) * 3600 + (int) $dt->format( 'i' ) * 60 + (int) $dt->format( 's' );

		$startTimeSeconds = self::convertTimeToSeconds( $startTime );
		$endTimeSeconds   = self::convertTimeToSeconds( $endTime );

		if ( $startTimeSeconds === false || $endTimeSeconds === false ) {
			return true; // if time format is invalid, ignore time targeting
		}

		return $currentSeconds >= $startTimeSeconds && $currentSeconds < $endTimeSeconds;
	}

	/**
	 * Get the day of the week for a given UTC timestamp and timezone.
	 *
	 * @param string $timezone "Europe\Bucharest" OR "website"
	 *
	 * @return string  Day of week in lowercase, e.g. "monday"
	 */
	public static function getDayOfWeekFromTimezone( $timezone = 'website' ) {
		$dt = new \DateTime( 'now', self::normalizeTimezone( $timezone ) );

		return strtolower( $dt->format( 'l' ) );
	}



	private static function compose_site_url( $path, $args = array() ) {
		$root_url = untrailingslashit( SITELEADS_WEBSITE_URL );
		$path     = ltrim( $path, '/' );

		$default_args = array(
			'sl_key'               => get_option( 'siteleads_ai_api_key', '' ),
			'utm_theme'            => get_template(),
			'utm_childtheme'       => get_stylesheet(),
			'utm_install_source'   => Flags::get( 'start_source', 'other' ),
			'utm_activated_on'     => Flags::get( 'activation_time', '' ),
			'utm_pro_activated_on' => Flags::get( 'pro_activation_time', '' ),
		);

		$args = array_merge( $default_args, $args );

		$args = array_map( 'urlencode', $args );
		$args = array_filter( $args );

		return add_query_arg( $args, "{$root_url}/{$path}" );
	}

	public static function getWebsiteURL( $target = 'homepage' ) {

		$args = apply_filters(
			'siteleads_website_root_url_args',
			array()
		);

		switch ( $target ) {
			case 'upgrade':
				return self::compose_site_url( '/#pricing', $args );
			case 'documentation':
				return self::compose_site_url( '/docs', $args );
			case 'support':
			case 'contact':
				return self::compose_site_url( '/contact', $args );
				break;
			default:
				return self::compose_site_url( '/', $args );
		}
	}

	public static function convertStyleArrayToString( $arr ) {
		$vars = array();

		foreach ( $arr as $key => $value ) {
			if ( $value === null ) {
				continue;
			}
			$vars[] = "$key: $value";
		}

		return implode( ';', $vars );
	}

	public static function hex2rgb( $hex ) {
		$hex = str_replace( '#', '', $hex );

		if ( strlen( $hex ) == 3 ) {
			$r = hexdec( substr( $hex, 0, 1 ) . substr( $hex, 0, 1 ) );
			$g = hexdec( substr( $hex, 1, 1 ) . substr( $hex, 1, 1 ) );
			$b = hexdec( substr( $hex, 2, 1 ) . substr( $hex, 2, 1 ) );
		} else {
			$r = hexdec( substr( $hex, 0, 2 ) );
			$g = hexdec( substr( $hex, 2, 2 ) );
			$b = hexdec( substr( $hex, 4, 2 ) );
		}

		return array( $r, $g, $b );
	}

	public static function fontSettingsToStyle( $settings ) {
		if ( ! $settings || ! count( $settings ) ) {
			return '';
		}

		$raw_settings = array(
			'color'       => isset( $settings['color'] ) ? $settings['color'] : null,
			'font-family' => isset( $settings['family'] ) ? $settings['family'] : null,
			'font-weight' => isset( $settings['weight'] ) ? $settings['weight'] : null,
			'font-size'   => isset( $settings['size'] ) ? $settings['size'] . 'px' : null,
			'text-align'  => isset( $settings['align'] ) ? $settings['align'] : null,
		);

		$style = array_filter( $raw_settings );

		return Utils::convertStyleArrayToString( $style );
	}

	public static function buildGoogleFontsURL( $fonts ) {
		$href       = 'https://fonts.googleapis.com/css';
		$query_args = array(
			'display' => 'swap',
		);

		$family_query_parts = array();
		foreach ( $fonts as $family => $variants ) {

			if ( empty( $variants ) ) {
				continue;
			}

			$family_query_part = $family;
			if ( count( $variants ) > 0 ) {
				$family_query_part .= ':' . implode( ',', $variants );
			}
			$family_query_parts[] = $family_query_part;
		}

		$query_args['family'] = urlencode( implode( '|', $family_query_parts ) );

		return add_query_arg( $query_args, $href );
	}

	public static function base64EncodeJWT( $str ) {
		return str_replace( array( '+', '/', '=' ), array( '-', '_', '' ), base64_encode( $str ) );
	}

	public static function generateWidgetJWT() {
		$private_key            = Admin::getApiKey();
		$private_key_identifier = Admin::getApiKeyIdentifier();

		$header = json_encode(
			array(
				'alg' => 'HS256',
				'typ' => 'JWT',
			)
		);

		$time = time();

		$payload = json_encode(
			array(
				'site-url'           => site_url(),
				'api-key-identifier' => $private_key_identifier,
				'exp'                => $time + 300,
				'iat'                => $time,
			)
		);

		$base64UrlHeader  = Utils::base64EncodeJWT( $header );
		$base64UrlPayload = Utils::base64EncodeJWT( $payload );

		$signature          = hash_hmac( 'sha256', $base64UrlHeader . '.' . $base64UrlPayload, $private_key, true );
		$base64UrlSignature = Utils::base64EncodeJWT( $signature );

		return $base64UrlHeader . '.' . $base64UrlPayload . '.' . $base64UrlSignature;
	}
}