course)) {
$this->course = new CourseHelper();
}
return $this->course;
}
public function cpt(): CptHelper|null
{
if (is_null($this->cpt)) {
$this->cpt = new CptHelper();
}
return $this->cpt;
}
public function email(): EmailHelper|null
{
if (is_null($this->email)) {
$this->email = new EmailHelper();
}
return $this->email;
}
public function queryLogger(): QueryLogger|null
{
if (is_null($this->queryLogger)) {
$this->queryLogger = new QueryLogger();
}
return $this->queryLogger;
}
public function ai(): AiHelper|null
{
if (is_null($this->ai)) {
$this->ai = new AiHelper();
}
return $this->ai;
}
public function acadlix_modify_video_shortcode($content)
{
if (empty($content)) {
return $content; // If content is empty, return it as-is
}
if (has_shortcode($content, 'video')) {
// Use regex to find all [video] shortcodes in the content
$pattern = get_shortcode_regex(['video']);
$content = preg_replace_callback("/$pattern/", function ($matches) {
// Check if the matched shortcode is [video]
if ($matches[2] === 'video') {
// Parse shortcode attributes
$attrs = shortcode_parse_atts($matches[3]);
// Remove width and height if they exist
unset($attrs['width'], $attrs['height']);
// Rebuild the shortcode with the modified attributes
$attr_string = '';
if ($attrs) {
foreach ($attrs as $key => $value) {
$attr_string .= sprintf(' %s="%s"', $key, esc_attr($value));
}
}
// Return the modified shortcode
return sprintf('[%s%s]%s[/%s]', $matches[2], $attr_string, $matches[5], $matches[2]);
}
return $matches[0]; // Return original shortcode if not [video]
}, $content);
}
return $content;
}
public function renderShortCode($content)
{
// Prevent null warnings — always ensure it's a string
$content = (string) ($content ?? '');
$content = $this->acadlix_modify_video_shortcode($content);
$content = apply_filters('the_content', $content); // phpcs:ignore
return $content;
}
public function upload_base64_image_to_wordpress($content)
{
$pattern = '/]+src="data:image\/([^;]+);base64,([^"]+)"[^>]*>/i';
// Callback function to handle each match
$callback = function ($matches) {
require_once(ABSPATH . 'wp-admin/includes/file.php');
// Initialize the filesystem
global $wp_filesystem;
if (empty($wp_filesystem)) {
WP_Filesystem(); // This initializes the $wp_filesystem global
}
// Extract mime type and base64 data from matches
$mime_type = $matches[1];
$base64_data = $matches[2];
// Decode base64 data
$image_data = base64_decode($base64_data);
// Create a unique filename
$filename = uniqid() . '.' . $mime_type;
// Path to the uploads directory
$upload_dir = wp_upload_dir();
$file_path = $upload_dir['path'] . '/' . $filename;
// Save the image data to a file
if (!$wp_filesystem->put_contents($file_path, $image_data, FS_CHMOD_FILE)) {
/* translators: %s is the filepath */
return new WP_Error('failed', sprintf(__('Failed to write to file: %s', 'acadlix'), esc_html($file_path)), array('status' => 400));
} else {
// Prepare file data for WP attachment
$wp_file_type = wp_check_filetype($filename, null);
$attachment = array(
'guid' => $upload_dir['url'] . '/' . $filename,
'post_mime_type' => $wp_file_type['type'],
'post_title' => sanitize_file_name($filename),
'post_content' => '',
'post_status' => 'inherit'
);
// // Insert the attachment into the media library
$attach_id = wp_insert_attachment($attachment, $file_path);
// // Generate attachment metadata
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attach_id, $file_path);
wp_update_attachment_metadata($attach_id, $attach_data);
// Return the URL of the uploaded image
return "
";
}
};
// Perform the replacement
$new_content = preg_replace_callback($pattern, $callback, $content);
return $new_content;
}
public function acadlix_upload_file_to_wordpress(
$file,
$subdir = '/',
$allowed_extensions = [],
$max_size_mb = null,
$options = []
) {
require_once ABSPATH . 'wp-admin/includes/file.php';
// 1. Validate raw file payload
if (!is_array($file) || empty($file['tmp_name']) || empty($file['name'])) {
return new WP_Error('invalid_file', __('Invalid file upload data provided.', 'acadlix'));
}
if (isset($file['error']) && $file['error'] !== UPLOAD_ERR_OK) {
return new WP_Error('upload_error', __('File upload error encountered.', 'acadlix'), ['status' => 400]);
}
// 2. Extension validation
$original_filename = sanitize_file_name($file['name']);
$extension = strtolower(pathinfo($original_filename, PATHINFO_EXTENSION));
$clean_allowed_extensions = array_filter(array_map('strtolower', (array) $allowed_extensions));
if (!empty($clean_allowed_extensions) && !in_array($extension, $clean_allowed_extensions, true)) {
return new WP_Error(
'invalid_extension',
sprintf(
/* translators: %s is the file extension */
__('File extension ".%s" is not allowed.', 'acadlix'),
$extension
),
['status' => 400]
);
}
// 3. File size validation
if ($max_size_mb !== null && floatval($max_size_mb) > 0) {
$max_size_bytes = floatval($max_size_mb) * 1024 * 1024;
if (!empty($file['size']) && $file['size'] > $max_size_bytes) {
return new WP_Error(
'file_too_large',
/* translators: %1$s is the file name, %2$s is the maximum allowed size in MB */
sprintf(__('File "%1$s" exceeds the maximum allowed size of %2$s MB.', 'acadlix'), $original_filename, $max_size_mb),
['status' => 400]
);
}
}
// 4. Ensure target directory exists
$upload_dir = wp_upload_dir();
$custom_dir_path = $upload_dir['basedir'] . $subdir;
if (!file_exists($custom_dir_path)) {
if (!wp_mkdir_p($custom_dir_path)) {
return new WP_Error('mkdir_failed', __('Failed to create destination upload directory.', 'acadlix'), ['status' => 500]);
}
}
// 5. Generate unique secure filename
$secure_token = wp_generate_password(16, false, false);
$filename_wo_ext = pathinfo($original_filename, PATHINFO_FILENAME);
$unique_filename = "{$filename_wo_ext}_{$secure_token}_" . time() . ".{$extension}";
$file['name'] = $unique_filename;
// 6. Map MIME types for wp_handle_upload safety
$constructed_mimes = [];
if (!empty($clean_allowed_extensions)) {
$wp_mimes = wp_get_mime_types();
foreach ($wp_mimes as $ext_pattern => $mime) {
foreach (explode('|', $ext_pattern) as $ext) {
if (in_array(strtolower($ext), $clean_allowed_extensions, true)) {
$constructed_mimes[$ext_pattern] = $mime;
}
}
}
}
// 7. Dynamic Upload Directory Filter Scope
$upload_dir_callback = function ($dirs) use ($subdir) {
$dirs['subdir'] = $subdir;
$dirs['path'] = $dirs['basedir'] . $subdir;
$dirs['url'] = $dirs['baseurl'] . $subdir;
return $dirs;
};
add_filter('upload_dir', $upload_dir_callback);
$overrides = array_merge(
['test_form' => false],
$options['upload_overrides'] ?? [],
['mimes' => !empty($constructed_mimes) ? $constructed_mimes : null]
);
$upload = wp_handle_upload($file, $overrides);
// Clean up filter callback
remove_filter('upload_dir', $upload_dir_callback);
if (isset($upload['error'])) {
return new WP_Error('upload_error', $upload['error'], ['status' => 400]);
}
// 8. Construct Output File Payload
$new_file = [
'file_name' => $file['name'],
'file_size' => $file['size'],
'file_extension' => $extension,
'file_url' => $upload['url'],
'file_path' => $upload['file'],
'file_type' => $upload['type'],
];
// 9. Stateless Cryptographic Delete Token (Option 1)
if (!empty($options['generate_delete_token'])) {
$current_user_id = get_current_user_id();
$token_payload = [
'file_name' => $file['name'],
'user_id' => $current_user_id,
'created' => time(),
];
$token_json = wp_json_encode($token_payload);
$hmac_sig = wp_hash($token_json, 'nonce');
$new_file['delete_token'] = base64_encode($token_json . '|' . $hmac_sig);
}
return $new_file;
}
public function acadlix_delete_file_from_wordpress($file_path_or_url, $delete_token = null, $options = [])
{
$defaults = [
'verify_token' => true,
'enforce_user' => true,
'allowed_subdir' => '',
];
$options = wp_parse_args($options, $defaults);
// 1. Path Resolution First (Need real path to extract target filename)
$upload_dir = wp_upload_dir();
$base_dir = realpath($upload_dir['basedir']);
if (!$base_dir) {
return new WP_Error('upload_dir_error', __('Uploads directory cannot be resolved.', 'acadlix'), ['status' => 500]);
}
$file_path = $file_path_or_url;
// Convert URL to absolute path if needed
if (filter_var($file_path_or_url, FILTER_VALIDATE_URL)) {
if (str_starts_with($file_path_or_url, $upload_dir['baseurl'])) {
$relative_path = str_replace($upload_dir['baseurl'], '', $file_path_or_url);
$file_path = $upload_dir['basedir'] . $relative_path;
} else {
return new WP_Error(
'invalid_file_url',
__('The provided file URL does not belong to the WordPress uploads directory.', 'acadlix'),
['status' => 400]
);
}
}
// Resolve symlinks and relative path sequences ('..')
$real_file_path = realpath($file_path);
if (!$real_file_path || !file_exists($real_file_path)) {
return new WP_Error(
'file_not_found',
__('The target file does not exist on the server.', 'acadlix'),
['status' => 404]
);
}
// Security: Prevent Directory Traversal Attack
if (!str_starts_with($real_file_path, $base_dir)) {
return new WP_Error(
'directory_traversal',
__('Access denied: File path is outside the allowed uploads directory.', 'acadlix'),
['status' => 403]
);
}
// Enforce subdirectory restriction if option is set
if (!empty($options['allowed_subdir'])) {
$allowed_subdir_path = realpath($upload_dir['basedir'] . '/' . ltrim($options['allowed_subdir'], '/'));
if ($allowed_subdir_path && !str_starts_with($real_file_path, $allowed_subdir_path)) {
return new WP_Error(
'restricted_subdir',
__('Access denied: File resides outside the permitted subdirectory.', 'acadlix'),
['status' => 403]
);
}
}
// Prevent deleting core directory or index files
if (is_dir($real_file_path) || basename($real_file_path) === 'index.php') {
return new WP_Error('invalid_target', __('Cannot delete system directories or index files.', 'acadlix'), ['status' => 400]);
}
// 2. Token & Payload Verification
if ($options['verify_token']) {
if (empty($delete_token)) {
return new WP_Error(
'missing_token',
__('Delete token is required for this operation.', 'acadlix'),
['status' => 400]
);
}
$decoded = base64_decode($delete_token, true);
if ($decoded === false || !str_contains($decoded, '|')) {
return new WP_Error(
'invalid_token_format',
__('Invalid delete token structure.', 'acadlix'),
['status' => 400]
);
}
list($token_json, $token_hmac) = explode('|', $decoded, 2);
// Verify HMAC signature to prevent payload tampering
$expected_hmac = wp_hash($token_json, 'nonce');
if (!hash_equals($expected_hmac, $token_hmac)) {
return new WP_Error(
'token_tampered',
__('Delete token verification failed. Token signature is invalid.', 'acadlix'),
['status' => 403]
);
}
$token_data = json_decode($token_json, true);
if (!is_array($token_data) || empty($token_data['file_name'])) {
return new WP_Error(
'token_corrupt',
__('Delete token payload is corrupt or missing file name.', 'acadlix'),
['status' => 400]
);
}
// *** CRITICAL CHECK: Verify token's file_name matches the target file being deleted ***
$target_filename = basename($real_file_path);
if (!hash_equals($token_data['file_name'], $target_filename)) {
return new WP_Error(
'token_filename_mismatch',
__('Delete token does not match the specified file.', 'acadlix'),
['status' => 403]
);
}
// Validate user ownership if enabled
if ($options['enforce_user']) {
$current_user_id = get_current_user_id();
if (!empty($token_data['user_id']) && intval($token_data['user_id']) !== $current_user_id) {
// Allow site admins to bypass ownership check
if (!current_user_can('manage_options')) {
return new WP_Error(
'unauthorized_user',
__('You do not have permission to delete this file.', 'acadlix'),
['status' => 403]
);
}
}
}
}
// 3. Perform Deletion
if (!wp_delete_file($real_file_path)) {
return new WP_Error(
'delete_failed',
__('Failed to delete file from the filesystem.', 'acadlix'),
['status' => 500]
);
}
return true;
}
public function acadlix_get_system_languages()
{
if (!function_exists('wp_get_available_translations')) {
return ['en_US' => 'English'];
}
$translations = wp_get_available_translations();
$language_map = [];
foreach ($translations as $locale => $details) {
$language_map[$locale] = $details['native_name'];
}
$language_map['en_US'] ??= 'English';
return $language_map;
}
public function acadlix_currencies()
{
$currencies = array(
'AFN' => __('Afghan afghani', 'acadlix'),
'ALL' => __('Albanian lek', 'acadlix'),
'AED' => __('United Arab Emirates dirham', 'acadlix'),
'AOA' => __('Angolan kwanza', 'acadlix'),
'ARS' => __('Argentine peso', 'acadlix'),
'AMD' => __('Armenian dram', 'acadlix'),
'ANG' => __('Netherlands Antilles guilder', 'acadlix'),
'AWG' => __('Aruban florin', 'acadlix'),
'AUD' => __('Australian dollar', 'acadlix'),
'AZN' => __('Azerbaijani manat', 'acadlix'),
'BSD' => __('Bahamian dollar', 'acadlix'),
'BHD' => __('Bahraini dinar', 'acadlix'),
'BDT' => __('Bangladeshi taka', 'acadlix'),
'BBD' => __('Barbadian dollar', 'acadlix'),
'BYR' => __('Belarusian ruble', 'acadlix'),
'BZD' => __('Belizean dollar', 'acadlix'),
'BMD' => __('Bermudian dollar', 'acadlix'),
'BTN' => __('Bhutanese ngultrum', 'acadlix'),
'BOB' => __('Bolivian boliviano', 'acadlix'),
'BAM' => __('Bosnia and Herzegovina convertible mark', 'acadlix'),
'BWP' => __('Botswana pula', 'acadlix'),
'BRL' => __('Brazilian real', 'acadlix'),
'BND' => __('Brunei dollar', 'acadlix'),
'BGN' => __('Bulgarian lev', 'acadlix'),
'BIF' => __('Burundian franc', 'acadlix'),
'CAD' => __('Canadian dollar', 'acadlix'),
'CVE' => __('Cape Verdean escudo', 'acadlix'),
'CLP' => __('Chilean peso', 'acadlix'),
'CNY' => __('Chinese renminbi', 'acadlix'),
'COP' => __('Colombian peso', 'acadlix'),
'CDF' => __('Congolese franc', 'acadlix'),
'CHF' => __('Swiss franc', 'acadlix'),
'CRC' => __('Costa Rican colón', 'acadlix'),
'CUC' => __('Cuban peso', 'acadlix'),
'CZK' => __('Czech koruna', 'acadlix'),
'DKK' => __('Danish krone', 'acadlix'),
'DJF' => __('Djiboutian franc', 'acadlix'),
'DOP' => __('Dominican peso', 'acadlix'),
'DZD' => __('Algerian dinar', 'acadlix'),
'EGP' => __('Egyptian pound', 'acadlix'),
'ERN' => __('Eritrean nakfa', 'acadlix'),
'EUR' => __('Euro', 'acadlix'),
'ETB' => __('Ethiopian birr', 'acadlix'),
'FKP' => __('Falkland Islands pound', 'acadlix'),
'FJD' => __('Fijian dollar', 'acadlix'),
'GMD' => __('Gambian dalasi', 'acadlix'),
'GEL' => __('Georgian lari', 'acadlix'),
'GHS' => __('Ghanian cedi', 'acadlix'),
'GIP' => __('Gibraltar pound', 'acadlix'),
'GTQ' => __('Guatemalan quetzal', 'acadlix'),
'GBP' => __('British pound', 'acadlix'),
'GNF' => __('Guinean franc', 'acadlix'),
'GYD' => __('Guyanese dollar', 'acadlix'),
'HTG' => __('Haitian gourde', 'acadlix'),
'HNL' => __('Honduran lempira', 'acadlix'),
'HKD' => __('Hong Kong dollar', 'acadlix'),
'HRK' => __('Croatian kuna', 'acadlix'),
'HUF' => __('Hungarian forint', 'acadlix'),
'ISK' => __('Icelandic króna', 'acadlix'),
'INR' => __('Indian rupee', 'acadlix'),
'IDR' => __('Indonesian rupiah', 'acadlix'),
'IRR' => __('Iranian rial', 'acadlix'),
'IQD' => __('Iraqi dinar', 'acadlix'),
'ILS' => __('Israeli new sheqel', 'acadlix'),
'JMD' => __('Jamaican dollar', 'acadlix'),
'JPY' => __('Japanese yen ', 'acadlix'),
'JEP' => __('Jersey pound', 'acadlix'),
'JOD' => __('Jordanian dinar', 'acadlix'),
'KZT' => __('Kazakhstani tenge', 'acadlix'),
'KES' => __('Kenyan shilling', 'acadlix'),
'KPW' => __('North Korean won', 'acadlix'),
'KHR' => __('Cambodian riel', 'acadlix'),
'KWD' => __('Kuwaiti dinar', 'acadlix'),
'KGS' => __('Kyrgyzstani som', 'acadlix'),
'KYD' => __('Cayman Islands dollar', 'acadlix'),
'KMF' => __('Comorian franc', 'acadlix'),
'KRW' => __('South Korean won', 'acadlix'),
'LAK' => __('Lao kip', 'acadlix'),
'LVL' => __('Latvian lats', 'acadlix'),
'LBP' => __('Lebanese pound', 'acadlix'),
'LSL' => __('Lesotho loti', 'acadlix'),
'LRD' => __('Liberian dollar', 'acadlix'),
'LD' => __('Libyan dinar', 'acadlix'),
'LYD' => __('Libyan dinar', 'acadlix'),
'LKR' => __('Sri Lankan rupee', 'acadlix'),
'LTL' => __('Lithuanian litas', 'acadlix'),
'MOP' => __('Macanese pataca', 'acadlix'),
'MKD' => __('Macedonian denar', 'acadlix'),
'MGA' => __('Malagasy ariary', 'acadlix'),
'MWK' => __('Malawian kwacha', 'acadlix'),
'MYR' => __('Malaysian ringgit', 'acadlix'),
'MVR' => __('Maldivian rufiyaa', 'acadlix'),
'MRO' => __('Mauritanian ouguiya', 'acadlix'),
'MUR' => __('Mauritian rupee', 'acadlix'),
'MXN' => __('Mexican peso', 'acadlix'),
'MDL' => __('Moldovan leu', 'acadlix'),
'MNT' => __('Mongolian tugrik', 'acadlix'),
'MAD' => __('Moroccan dirham', 'acadlix'),
'MZN' => __('Mozambican metical', 'acadlix'),
'MMK' => __('Burmese kyat', 'acadlix'),
'NAD' => __('Namibian dollar', 'acadlix'),
'NPR' => __('Nepalese rupee', 'acadlix'),
'NIO' => __('Nicaraguan córdoba', 'acadlix'),
'NGN' => __('Nigerian naira', 'acadlix'),
'NOK' => __('Norwegian krone', 'acadlix'),
'NZD' => __('New Zealand dollar', 'acadlix'),
'OMR' => __('Omani rial', 'acadlix'),
'PKR' => __('Pakistani rupee', 'acadlix'),
'PAB' => __('Panamanian balboa', 'acadlix'),
'PGK' => __('Papua New Guinea kina', 'acadlix'),
'PYG' => __('Paraguayan guarani', 'acadlix'),
'PEN' => __('Peruvian nuevo sol', 'acadlix'),
'PHP' => __('Philippine peso', 'acadlix'),
'PLN' => __('Polish zloty', 'acadlix'),
'QAR' => __('Qatari riyal', 'acadlix'),
'RON' => __('Romanian leu', 'acadlix'),
'RUB' => __('Russian ruble', 'acadlix'),
'RSD' => __('Serbian dinar', 'acadlix'),
'RWF' => __('Rwandan franc', 'acadlix'),
'SVC' => __('Salvadoran colón', 'acadlix'),
'STD' => __('São Tomé and Príncipe dobra', 'acadlix'),
'SAR' => __('Saudi riyal', 'acadlix'),
'SCR' => __('Seychellois rupee', 'acadlix'),
'SLL' => __('Sierra Leonean leone', 'acadlix'),
'SGD' => __('Singapore dollar', 'acadlix'),
'SBD' => __('Solomon Islands dollar', 'acadlix'),
'SOS' => __('Somali shilling', 'acadlix'),
'SHP' => __('St. Helena pound', 'acadlix'),
'SDG' => __('Sudanese pound', 'acadlix'),
'SRD' => __('Surinamese dollar', 'acadlix'),
'SZL' => __('Swazi lilangeni', 'acadlix'),
'SEK' => __('Swedish krona', 'acadlix'),
'SYP' => __('Syrian pound', 'acadlix'),
'TWD' => __('New Taiwan dollar', 'acadlix'),
'TJS' => __('Tajikistani somoni', 'acadlix'),
'TZS' => __('Tanzanian shilling', 'acadlix'),
'THB' => __('Thai baht ', 'acadlix'),
'TOP' => __('Tongan pa’anga', 'acadlix'),
'TTD' => __('Trinidad and Tobago dollar', 'acadlix'),
'TND' => __('Tunisian dinar', 'acadlix'),
'TRY' => __('Turkish lira', 'acadlix'),
'TMT' => __('Turkmenistani manat', 'acadlix'),
'UGX' => __('Ugandan shilling', 'acadlix'),
'UAH' => __('Ukrainian hryvnia', 'acadlix'),
'USD' => __('US dollar', 'acadlix'),
'UYU' => __('Uruguayan peso', 'acadlix'),
'UZS' => __('Uzbekistani som', 'acadlix'),
'VUV' => __('Vanuatu vatu', 'acadlix'),
'VEF' => __('Venezuelan bolivar', 'acadlix'),
'VND' => __('Vietnamese dong', 'acadlix'),
'WST' => __('Samoan tālā', 'acadlix'),
'XCD' => __('East Caribbean dollar', 'acadlix'),
'XOF' => __('West African CFA franc', 'acadlix'),
'XAF' => __('Central African CFA franc', 'acadlix'),
'XPF' => __('CFP franc', 'acadlix'),
'YER' => __('Yemeni rial', 'acadlix'),
'ZAR' => __('South African rand', 'acadlix'),
'ZMK' => __('Zambian kwacha', 'acadlix'),
'ZWL' => __('Zimbabwean dollar', 'acadlix'),
);
asort($currencies);
return $currencies;
}
public function acadlix_currency_symbols()
{
$symbols = array(
'AED' => 'د.إ',
'AFN' => '؋',
'ALL' => 'L',
'AMD' => 'AMD',
'ANG' => 'ƒ',
'AOA' => 'Kz',
'ARS' => '$',
'AUD' => '$',
'AWG' => 'Afl.',
'AZN' => 'AZN',
'BAM' => 'KM',
'BBD' => '$',
'BDT' => '৳ ',
'BGN' => 'лв.',
'BHD' => '.د.ب',
'BIF' => 'Fr',
'BMD' => '$',
'BND' => '$',
'BOB' => 'Bs.',
'BRL' => 'R$',
'BSD' => '$',
'BTC' => '฿',
'BTN' => 'Nu.',
'BWP' => 'P',
'BYR' => 'Br',
'BYN' => 'Br',
'BZD' => '$',
'CAD' => '$',
'CDF' => 'Fr',
'CHF' => 'CHF',
'CLP' => '$',
'CNY' => '¥',
'COP' => '$',
'CRC' => '₡',
'CUC' => '$',
'CUP' => '$',
'CVE' => '$',
'CZK' => 'Kč',
'DJF' => 'Fr',
'DKK' => 'DKK',
'DOP' => 'RD$',
'DZD' => 'د.ج',
'EGP' => 'EGP',
'ERN' => 'Nfk',
'ETB' => 'Br',
'EUR' => '€',
'FJD' => '$',
'FKP' => '£',
'GBP' => '£',
'GEL' => '₾',
'GGP' => '£',
'GHS' => '₵',
'GIP' => '£',
'GMD' => 'D',
'GNF' => 'Fr',
'GTQ' => 'Q',
'GYD' => '$',
'HKD' => '$',
'HNL' => 'L',
'HRK' => 'kn',
'HTG' => 'G',
'HUF' => 'Ft',
'IDR' => 'Rp',
'ILS' => '₪',
'IMP' => '£',
'INR' => '₹',
'IQD' => 'ع.د',
'IRR' => '﷼',
'IRT' => 'تومان',
'ISK' => 'kr.',
'JEP' => '£',
'JMD' => '$',
'JOD' => 'د.ا',
'JPY' => '¥',
'KES' => 'KSh',
'KGS' => 'сом',
'KHR' => '៛',
'KMF' => 'Fr',
'KPW' => '₩',
'KRW' => '₩',
'KWD' => 'د.ك',
'KYD' => '$',
'KZT' => '₸',
'LAK' => '₭',
'LBP' => 'ل.ل',
'LKR' => 'රු',
'LRD' => '$',
'LSL' => 'L',
'LYD' => 'ل.د',
'MAD' => 'د.م.',
'MDL' => 'MDL',
'MGA' => 'Ar',
'MKD' => 'ден',
'MMK' => 'Ks',
'MNT' => '₮',
'MOP' => 'P',
'MRU' => 'UM',
'MUR' => '₨',
'MVR' => '.ރ',
'MWK' => 'MK',
'MXN' => '$',
'MYR' => 'RM',
'MZN' => 'MT',
'NAD' => 'N$',
'NGN' => '₦',
'NIO' => 'C$',
'NOK' => 'kr',
'NPR' => '₨',
'NZD' => '$',
'OMR' => 'ر.ع.',
'PAB' => 'B/.',
'PEN' => 'S/',
'PGK' => 'K',
'PHP' => '₱',
'PKR' => '₨',
'PLN' => 'zł',
'PRB' => 'р.',
'PYG' => '₲',
'QAR' => 'ر.ق',
'RMB' => '¥',
'RON' => 'lei',
'RSD' => 'рсд',
'RUB' => '₽',
'RWF' => 'Fr',
'SAR' => 'ر.س',
'SBD' => '$',
'SCR' => '₨',
'SDG' => 'ج.س.',
'SEK' => 'kr',
'SGD' => '$',
'SHP' => '£',
'SLL' => 'Le',
'SOS' => 'Sh',
'SRD' => '$',
'SSP' => '£',
'STN' => 'Db',
'SYP' => 'ل.س',
'SZL' => 'L',
'THB' => '฿',
'TJS' => 'ЅМ',
'TMT' => 'm',
'TND' => 'د.ت',
'TOP' => 'T$',
'TRY' => '₺',
'TTD' => '$',
'TWD' => 'NT$',
'TZS' => 'Sh',
'UAH' => '₴',
'UGX' => 'UGX',
'USD' => '$',
'UYU' => '$',
'UZS' => 'UZS',
'VEF' => 'Bs F',
'VES' => 'Bs.S',
'VND' => '₫',
'VUV' => 'Vt',
'WST' => 'T',
'XAF' => 'CFA',
'XCD' => '$',
'XOF' => 'CFA',
'XPF' => 'Fr',
'YER' => '﷼',
'ZAR' => 'R',
'ZMW' => 'ZK',
);
return $symbols;
}
public function acadlix_get_currency_with_symbols()
{
$currency_symbol = [];
$currencies = $this->acadlix_currencies();
$symbol = $this->acadlix_currency_symbols();
$i = 0;
foreach ($currencies as $key => $currency) {
$currency_symbol[$i]['short_name'] = $key;
$currency_symbol[$i]['name'] = $currency;
$currency_symbol[$i]['symbol'] = $symbol[$key] ?? '';
$i++;
}
return $currency_symbol;
}
public function acadlix_options()
{
$options = [
// Page Setup
'acadlix_dashboard_page_id' => null,
// 'acadlix_cart_page_id' => null,
'acadlix_checkout_page_id' => null,
'acadlix_thankyou_page_id' => null,
'acadlix_one_click_checkout' => 'no',
// Course Option
'acadlix_no_of_courses_per_page' => 12,
'acadlix_column_per_row' => 3,
'acadlix_disable_wishlist' => 'no',
'acadlix_enable_rating_and_reviews' => 'no',
'acadlix_require_admin_approval_for_reviews' => 'no',
'acadlix_review_pagination_count' => 10,
'acadlix_enable_course_filters' => 'no',
'acadlix_disable_student_enrolled' => 'no',
// Currency Option
'acadlix_currency' => 'USD',
'acadlix_currency_position' => 'Left ( $99.99 )',
'acadlix_thousand_separator' => ',',
'acadlix_decimal_separator' => '.',
'acadlix_number_of_decimals' => 2,
'acadlix_default_payment_gateway' => '',
// Admin Option
'acadlix_default_rows_per_page' => 20,
'acadlix_admin_auto_registration_to_courses' => 'no',
'acadlix_admin_can_assign_courses_to_student' => 'no',
'acadlix_admin_can_remove_student_from_course' => 'no',
'acadlix_enable_gutenberg_editor_for_lesson' => 'no',
// Front end Option
'acadlix_disable_admin_toolbar' => 'no',
'acadlix_enable_content_protection' => 'no',
// Student Dashboard Option
'acadlix_logout_redirect_url' => '',
'acadlix_enable_dashboard_fullwidth' => 'no',
'acadlix_enable_site_logo_in_header' => 'no',
'acadlix_custom_logo' => [],
'acadlix_show_dark_mode_toggle' => 'no',
'acadlix_show_font_resizer' => 'no',
'acadlix_enable_course_content_prefetch' => 'no',
// Checkout Option
'acadlix_enable_coupon_code' => 'no',
// Data management
'acadlix_delete_data_on_plugin_uninstall' => 'no',
// Notification option
'acadlix_notify_course_purchase_to_student' => 'no',
'acadlix_notify_course_purchase_to_admin' => 'no',
'acadlix_notify_course_completion_to_student' => 'no',
'acadlix_notify_course_completion_to_admin' => 'no',
'acadlix_notify_failed_transaction_to_student' => 'no',
'acadlix_notify_failed_transaction_to_admin' => 'no',
'acadlix_notify_offline_purchase_to_student' => 'no',
'acadlix_notify_offline_purchase_to_admin' => 'no',
// Permalink option
'acadlix_course_base' => 'courses',
'acadlix_course_category_base' => 'course-category',
'acadlix_course_tag_base' => 'course-tag',
// Student Dashboard option
'acadlix_enable_student_info' => 'yes',
'acadlix_enable_quick_performance' => 'yes',
'acadlix_enable_advertising_banner' => 'yes',
'acadlix_enable_continue_learning' => 'yes',
'acadlix_enable_course_category' => 'yes',
'acadlix_enable_recently_joined_courses' => 'yes',
'acadlix_enable_popular_courses' => 'yes',
'acadlix_enable_new_courses' => 'yes',
'acadlix_enable_subject_performance' => 'yes',
'acadlix_enable_recently_attempted_quizzes' => 'yes',
'acadlix_course_link_type_desktop' => 'public',
'acadlix_course_link_type_mobile' => 'public',
//Advertisement banner options
'acadlix_advertising_banners' => [],
// DB option
'acadlix_db_version' => '',
// Certificate option
'acadlix_certificate_authorised_name' => '',
'acadlix_certificate_authorised_company' => '',
'acadlix_certificate_show_instructor_name_on_certificate' => 'no',
'acadlix_certificate_show_course_completion_date_on_certificate' => 'no',
'acadlix_certificate_page_id' => null,
'acadlix_certificate_show_certificate_link_in_email' => 'no',
'acadlix_certificate_signature' => [],
'acadlix_certificate_template' => 'classic-landscape',
// Authentication option
'acadlix_default_auth_screen' => 'login',
'acadlix_registration_fields' => $this->acadlix_registration_fields(),
'acadlix_enable_fraud_protection' => 'no',
'acadlix_v3_site_key' => '',
// Caching option
'acadlix_student_dashboard_offline_cache' => 'no',
'acadlix_student_dashboard_cache_ttl_days' => 7,
'acadlix_student_dashboard_cache_buster' => 'acadlix-v1',
'acadlix_student_dashboard_cache_debug' => 'no',
];
// Filter options
$options = apply_filters('acadlix_options', $options);
return $options;
}
public function acadlix_advance_options()
{
$options = [
'acadlix_razorpay_active' => 'no',
'acadlix_razorpay_client_id' => '',
'acadlix_razorpay_secret_key' => '',
'acadlix_razorpay_webhook_secret' => '',
'acadlix_razorpay_webhook_url' => $this->acadlix_get_webhook_url('razorpay'),
'acadlix_paypal_active' => 'no',
'acadlix_paypal_client_id' => '',
'acadlix_paypal_secret_key' => '',
'acadlix_paypal_sandbox' => 'no',
'acadlix_paypal_webhook_id' => '',
'acadlix_paypal_webhook_url' => $this->acadlix_get_webhook_url('paypal'),
'acadlix_payu_active' => 'no',
'acadlix_payu_merchant_key' => '',
'acadlix_payu_salt' => '',
'acadlix_payu_sandbox' => 'no',
'acadlix_payu_webhook_url' => $this->acadlix_get_webhook_url('payu'),
'acadlix_offline_payment' => 'no',
'acadlix_stripe_active' => 'no',
'acadlix_stripe_public_key' => '',
'acadlix_stripe_secret_key' => '',
'acadlix_stripe_sandbox' => 'no',
'acadlix_stripe_webhook_signature_key' => '',
'acadlix_stripe_webhook_url' => $this->acadlix_get_webhook_url('stripe'),
'acadlix_v3_secret_key' => '',
'acadlix_offline_active' => 'no',
'acadlix_offline_instructions' => '',
'acadlix_offline_enable_file_upload' => 'no',
'acadlix_offline_max_upload_file_size' => 2,
'acadlix_offline_allowed_mime_types' => [],
'acadlix_knit_pay_active' => 'no',
'acadlix_knit_pay_title' => 'Online Payment',
'acadlix_knit_pay_configuration' => '',
'acadlix_knit_pay_description' => '',
'acadlix_openai_api_key' => null,
];
return $options;
}
public function acadlix_registration_fields()
{
return [
[
'id' => 'first_name',
'name' => 'First Name',
'label' => 'First Name',
'placeholder' => 'First Name',
'type' => 'text',
'meta_key' => 'first_name',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'settings' => [
"width" => "50%",
],
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'last_name',
'name' => 'Last Name',
'label' => 'Last Name',
'placeholder' => 'Last Name',
'type' => 'text',
'meta_key' => 'last_name',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'settings' => [
"width" => "50%",
],
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'username',
'name' => 'Username',
'label' => 'Username',
'placeholder' => 'Username',
'type' => 'text',
'meta_key' => 'user_login',
'is_meta' => false,
'default' => true,
'enabled' => true,
'required' => true,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'email',
'name' => 'Email',
'label' => 'Email',
'placeholder' => 'Email',
'type' => 'email',
'meta_key' => 'user_email',
'is_meta' => false,
'default' => true,
'enabled' => true,
'required' => true,
'value' => '',
'controls' => [
'can_disable' => false,
'can_require_toggle' => false,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'password',
'name' => 'Password',
'label' => 'Password',
'placeholder' => 'Password',
'type' => 'password',
'meta_key' => 'user_pass',
'is_meta' => false,
'default' => true,
'enabled' => true,
'required' => true,
'value' => '',
'controls' => [
'can_disable' => false,
'can_require_toggle' => false,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'confirm_password',
'name' => 'Confirm Password',
'label' => 'Confirm Password',
'placeholder' => 'Confirm Password',
'type' => 'password',
'meta_key' => 'user_pass',
'is_meta' => false,
'default' => true,
'enabled' => true,
'required' => true,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'phone_number',
'name' => 'Phone Number',
'label' => 'Phone Number',
'placeholder' => 'Phone Number',
'type' => 'tel',
'meta_key' => get_option("acadlix_phone_user_meta_key", "_acadlix_profile_phone_number"),
'is_meta' => true,
'default' => true,
'enabled' => get_option("acadlix_registration_options", [])['phone']['enabled'] ?? false,
'required' => get_option("acadlix_registration_options", [])['phone']['required'] ?? false,
'value' => '',
'settings' => [
'phonecode' => [
'enabled' => true,
'placeholder' => 'Code',
'default' => get_option("acadlix_registration_options", [])['phone']['default_phonecode'] ?? '',
'meta_key' => get_option("acadlix_phonecode_user_meta_key", "_acadlix_profile_phonecode"),
'value' => get_option("acadlix_registration_options", [])['phone']['default_phonecode'] ?? '',
],
'isocode' => [
'default' => get_option("acadlix_registration_options", [])['phone']['default_isocode'] ?? '',
'meta_key' => get_option("acadlix_isocode_user_meta_key", "_acadlix_profile_isocode"),
'value' => get_option("acadlix_registration_options", [])['phone']['default_isocode'] ?? '',
]
],
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'user_url',
'name' => 'Website',
'label' => 'Website',
'placeholder' => 'https://example.com',
'type' => 'url',
'meta_key' => 'user_url',
'is_meta' => false,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'address',
'name' => 'Address',
'label' => 'Address',
'placeholder' => 'Address',
'type' => 'text',
'meta_key' => '_acadlix_profile_address',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'country',
'name' => 'Country',
'label' => 'Country',
'placeholder' => 'Country',
'type' => 'country',
'meta_key' => '_acadlix_profile_country',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'city',
'name' => 'City',
'label' => 'City',
'placeholder' => 'City',
'type' => 'text',
'meta_key' => '_acadlix_profile_city',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'zip_code',
'name' => 'Zip Code',
'label' => 'Zip Code',
'placeholder' => 'Zip Code',
'type' => 'text',
'meta_key' => '_acadlix_profile_zip_code',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
[
'id' => 'bio',
'name' => 'Bio',
'label' => 'Bio',
'placeholder' => 'Bio',
'type' => 'textarea',
'meta_key' => 'description',
'is_meta' => true,
'default' => true,
'enabled' => false,
'required' => false,
'value' => '',
'controls' => [
'can_disable' => true,
'can_require_toggle' => true,
'can_delete' => false,
'can_edit_name' => false,
'can_edit_type' => false,
'can_edit_meta_key' => false,
'can_edit_label' => true,
'can_edit_placeholder' => true,
],
],
];
}
public function acadlix_get_webhook_url($payment_method = 'paypal')
{
// Ensure method is safe for URLs
$payment_method = sanitize_key($payment_method);
// Base REST route
$rest_route = sprintf(
'acadlix/v1/front-checkout/handle-webhook'
);
// Build REST URL
$url = add_query_arg(
array('payment_method' => $payment_method),
rest_url($rest_route)
);
return esc_url_raw($url);
}
public function acadlix_get_all_options($options = [])
{
if (empty($options)) {
$options = $this->acadlix_options();
}
$new_options = [];
if (count($options) > 0) {
foreach ($options as $key => $option) {
$new_options[$key] = $this->acadlix_get_option($key, $option);
}
}
return $new_options;
}
public function acadlix_delete_all_options()
{
$options = $this->acadlix_options();
$advance_options = $this->acadlix_advance_options();
$options = array_merge($options, $advance_options);
if (count($options) > 0) {
foreach ($options as $key => $option) {
$this->acadlix_delete_option($key);
}
}
}
public function acadlix_get_option($key = '', $default = false)
{
$options = $this->acadlix_options();
if (!empty($key)) {
return get_option($key, $options[$key] ?? $default);
}
return $default;
}
public function acadlix_update_option($key = '', $value = '')
{
if (!empty($key)) {
update_option($key, $value);
}
}
public function acadlix_delete_option($key = '')
{
if (!empty($key)) {
delete_option($key);
}
}
/**
* Formats a date string according to the WordPress date format setting.
*
* @param string $dateStr The date string to format.
*
* @return string The formatted date string.
*/
public function formatDate(string $dateStr): string
{
$dateFormat = $this->acadlix_get_option('date_format');
$dateObj = strtotime($dateStr);
$formattedDate = date($dateFormat, $dateObj); // phpcs:ignore
return $formattedDate;
}
public function acadlix_get_time_zone_string()
{
$timezone = $this->acadlix_get_option('timezone_string');
if ($timezone) {
return $timezone;
}
// get UTC offset, if it isn't set then return UTC
$utcOffset = $this->acadlix_get_option('gmt_offset', 0);
if ($utcOffset === 0) {
return 'UTC';
}
// Adjust UTC offset from hours to seconds
$utcOffset *= 3600;
// Attempt to guess the timezone string from the UTC offset
$timezone = timezone_name_from_abbr('', $utcOffset, 0);
if ($timezone) {
return $timezone;
}
return 'UTC';
}
public function acadlix_get_date_time_format()
{
return $this->acadlix_get_option('date_format') . ' ' . $this->acadlix_get_option('time_format');
}
public function acadlix_ddd($data)
{
foreach (func_get_args() as $arg) {
echo '
';
print_r($arg); // phpcs:ignore
echo '';
}
}
public function acadlix_dd($data)
{
foreach (func_get_args() as $arg) {
echo '';
print_r($arg); // phpcs:ignore
echo '';
}
die;
}
public function acadlix_max_execution_time()
{
return ini_get('max_execution_time') * 1000;
}
public function acadlix_get_email_template($template_name, $type = 'student', $vars = [])
{
$file_path = ACADLIX_TEMPLATE_PATH . 'email/' . $type . '/' . $template_name;
// if (file_exists($file_path)) {
// return file_get_contents($file_path);
// }
// return '';
if (!file_exists($file_path)) {
return '';
}
extract($vars, EXTR_SKIP);
// Start output buffering
ob_start();
// Execute the PHP template file
include $file_path;
// Get the final rendered HTML and clear the buffer
$content = ob_get_clean();
return $content;
}
public function acadlix_preload_scripts($script = '')
{
$manifest_path = ACADLIX_BUILD_PATH . acadlix()->versionPath . '/manifest.php';
if (file_exists($manifest_path)) {
$manifest = include $manifest_path;
// Preload dependencies
if (!empty($script) && is_array($manifest[$script]['imports'])) {
foreach ($manifest[$script]['imports'] as $name => $file) {
if (strpos($name, 'vendor-') === 0) {
echo '' . "\n";
}
}
}
}
}
public function acadlix_table_prefix($table_name)
{
global $wpdb;
return "{$wpdb->prefix}acadlix_{$table_name}";
}
public function acadlix_wp_prefix($table_name)
{
global $wpdb;
return "{$wpdb->prefix}{$table_name}";
}
public function acadlix_fk_prefix($table_name, $key)
{
global $wpdb;
// Base string: wp_tablename_key_fk
$full_name = "{$wpdb->prefix}{$table_name}_{$key}_fk";
// If it's over 30 characters, shorten it deterministically
if (strlen($full_name) > 30) {
// Use an md5 hash snippet of the table + key to keep it unique but short
$short_hash = substr(md5($table_name . $key), 0, 6);
return "{$wpdb->prefix}fk_{$short_hash}";
}
return $full_name;
}
public function acadlix_index_prefix($table_name, $key)
{
global $wpdb;
// Base string: wp_tablename_key_ix
$full_name = "{$wpdb->prefix}{$table_name}_{$key}_ix";
// If it's over 30 characters, shorten it deterministically
if (strlen($full_name) > 30) {
// Use an md5 hash snippet of the table + key to keep it unique but short
$short_hash = substr(md5($table_name . $key), 0, 6);
return "{$wpdb->prefix}ix_{$short_hash}";
}
return $full_name;
}
public function acadlix_old_fk_prefix($table_name, $key)
{
return "acadlix_{$table_name}_{$key}_foreign";
}
public function acadlix_old_index_prefix($table_name, $key)
{
return "acadlix_{$table_name}_{$key}_index";
}
public function acadlix_update_fk($table, $oldConstraint, $column, $referencedTable, $newConstraint)
{
global $wpdb;
$exists = $wpdb->get_var( // phpcs:ignore
$wpdb->prepare(
'SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = %s
AND TABLE_NAME = %s
AND CONSTRAINT_NAME = %s',
DB_NAME,
$table,
$oldConstraint
)
);
if ($exists) {
try {
Manager::schema()->table($table, function ($table) use ($oldConstraint) {
$table->dropForeign($oldConstraint);
});
} catch (\Exception $e) {
// Fail silently or log if needed
}
}
// Step 2: Check if new constraint already exists
$newExists = $wpdb->get_var( // phpcs:ignore
$wpdb->prepare(
'SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = %s
AND TABLE_NAME = %s
AND CONSTRAINT_NAME = %s',
DB_NAME,
$table,
$newConstraint
)
);
if (!$newExists) {
Manager::schema()->table($table, function ($table) use ($column, $referencedTable, $newConstraint) {
$table
->foreign($column, $newConstraint)
->references('id')
->on($referencedTable)
->onDelete('cascade');
});
}
}
public function acadlix_update_index($table, $oldIndex, $columns, $newIndex)
{
global $wpdb;
// Step 1: Drop old index if it exists
$oldExists = $wpdb->get_var( // phpcs:ignore
$wpdb->prepare(
'SELECT INDEX_NAME
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = %s
AND TABLE_NAME = %s
AND INDEX_NAME = %s',
DB_NAME,
$table,
$oldIndex
)
);
if ($oldExists) {
try {
Manager::schema()->table($table, function ($table) use ($oldIndex) {
$table->dropIndex($oldIndex); // Drop old index
});
} catch (\Throwable $e) {
// Optional: log or silently ignore
}
}
$newExists = $wpdb->get_var( // phpcs:ignore
$wpdb->prepare(
'SELECT INDEX_NAME
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = %s
AND TABLE_NAME = %s
AND INDEX_NAME = %s',
DB_NAME,
$table,
$newIndex
)
);
if (!$newExists) {
Manager::schema()->table($table, function ($table) use ($columns, $newIndex) {
$table->index($columns, $newIndex);
});
}
}
public function acadlix_get_all_addons(): array
{
return [
[
'name' => __('Knit Pay Integration', 'acadlix'),
'description' => __('Knit pay integration for seamless payment processing.', 'acadlix'),
'pro' => false,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_knit_pay_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_knit_pay_enabled',
'icon' => 'KnitPayIcon',
'icon_color' => '#efefef',
'disabled' => acadlix()->integrations()->knit_pay()->is_active() ? false : true,
],
[
'name' => __('Bunny.net Integration', 'acadlix'),
'description' => __('Bunny.net integration for efficient video content management.', 'acadlix'),
'pro' => false,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_bunny_net_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_bunny_net_enabled',
'icon' => 'BunnyNetIcon',
'icon_color' => '#efefef',
'disabled' => false,
],
[
'name' => __('Acadlix Bulk Question Upload', 'acadlix'),
'description' => __('Upload hundreds of questions in seconds via MS Word.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_bulk_question_upload_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_bulk_question_upload_enabled',
'icon' => 'FaCloudUploadAlt',
'icon_color' => 'red',
'disabled' => false,
],
[
'name' => __('Acadlix Assignments', 'acadlix'),
'description' => __('Create, assign, and evaluate student assignments seamlessly.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_assignments_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_assignments_enabled',
'icon' => 'MdAssignment',
'icon_color' => '#ffa65a',
'disabled' => false,
],
[
'name' => __('Acadlix Zoom Integration', 'acadlix'),
'description' => __('Schedule and manage live classes via Zoom from your dashboard.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_zoom_integration_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_zoom_integration_enabled',
'icon' => 'BiLogoZoom',
'icon_color' => '#2d8cff',
'disabled' => false,
],
[
'name' => __('Acadlix Advanced Report', 'acadlix'),
'description' => __('Generate detailed reports for your students and courses.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_advanced_report_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_advanced_report_enabled',
'icon' => 'HiDocumentReport',
'icon_color' => '#4A90E2',
'disabled' => false,
],
[
'name' => __('Acadlix Subscriptions', 'acadlix'),
'description' => __('Manage subscriptions for your students, courses and course bundles.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_subscriptions_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_subscriptions_enabled',
'icon' => 'MdSubscriptions',
'icon_color' => '#22C55E',
'disabled' => false,
],
[
'name' => __('Acadlix Data Exporter', 'acadlix'),
'description' => __('Export results, answer sheets in PDF or Excel.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_data_exporter_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_data_exporter_enabled',
'icon' => 'FaFileExport',
'icon_color' => '#3B82F6',
'disabled' => false,
],
[
'name' => __('Question Error Reporting', 'acadlix'),
'description' => __('This addon lets students flag and report incorrect or unclear questions, helping admins maintain accuracy and improve question quality.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_question_error_reporting_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_question_error_reporting_enabled',
'icon' => 'TbAlertTriangleFilled',
'icon_color' => '#FFAB00',
'disabled' => false,
],
[
'name' => __('Social Login', 'acadlix'),
'description' => __('Allow users to log in using their social media accounts.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_social_login_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_social_login_enabled',
'icon' => 'SocialLogin',
'icon_color' => '#3B82F6',
'disabled' => false,
],
[
'name' => __('Course Bundle', 'acadlix'),
'description' => __('Allow users to purchase multiple courses as a bundle.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => true,
'active' => $this->acadlix_get_option('acadlix_addon_course_bundle_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_course_bundle_enabled',
'icon' => 'FaLayerGroup',
'icon_color' => '#7C3AED',
'disabled' => false,
],
[
'name' => __('Google Meet', 'acadlix'),
'description' => __('Schedule and manage live classes via Google Meet from your dashboard.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => false,
'active' => $this->acadlix_get_option('acadlix_addon_google_meet_integration_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_google_meet_integration_enabled',
'icon' => 'GoogleMeet',
'icon_color' => '#e9ecea',
'disabled' => false,
],
[
'name' => __('Microsoft Teams', 'acadlix'),
'description' => __('Schedule and manage live classes via Microsoft Teams from your dashboard.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => false,
'active' => $this->acadlix_get_option('acadlix_addon_microsoft_teams_integration_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_microsoft_teams_integration_enabled',
'icon' => 'BsMicrosoftTeams',
'icon_color' => '#5553c1',
'disabled' => false,
],
[
'name' => __('Drip Content', 'acadlix'),
'description' => __('Release course lessons, sections, or quizzes automatically based on schedule or completion progress. Keep learners engaged with a structured learning path and prevent content overload.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => false,
'active' => $this->acadlix_get_option('acadlix_addon_drip_content_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_drip_content_enabled',
'icon' => 'MdAccountTree',
'icon_color' => '#13647a',
'disabled' => false,
],
[
'name' => __('PWA', 'acadlix'),
'description' => __('Progressive Web App (PWA) support for offline access and improved performance.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => false,
'active' => $this->acadlix_get_option('acadlix_addon_pwa_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_pwa_enabled',
'icon' => 'MdInstallMobile',
'icon_color' => '#3B0764',
'disabled' => false,
],
[
'name' => __('Youtube live streaming', 'acadlix'),
'description' => __('Add YouTube Live Streams directly to your Acadlix courses, allowing students to join live classes from within the course.', 'acadlix'),
'pro' => true,
'internal' => true,
'installed' => false,
'active' => $this->acadlix_get_option('acadlix_addon_youtube_live_streaming_enabled', false) == 'yes',
'url' => '',
'option_name' => 'acadlix_addon_youtube_live_streaming_enabled',
'icon' => 'FaYoutube',
'icon_color' => '#efefef',
'disabled' => false,
],
];
}
public function is_youtube_live_streaming_addon_active()
{
$value = get_option('acadlix_addon_youtube_live_streaming_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_pwa_addon_active()
{
$value = get_option('acadlix_addon_pwa_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_knit_pay_integration_addon_active()
{
$value = get_option('acadlix_addon_knit_pay_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->integrations()->knit_pay()->is_active()) {
return false;
}
return true;
}
public function is_bunny_net_integration_addon_active()
{
$value = get_option('acadlix_addon_bunny_net_enabled', false);
if ($value != 'yes') {
return false;
}
return true;
}
public function is_drip_content_addon_active()
{
$value = get_option('acadlix_addon_drip_content_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_microsoft_teams_integration_addon_active()
{
$value = get_option('acadlix_addon_microsoft_teams_integration_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_google_meet_integration_addon_active()
{
$value = get_option('acadlix_addon_google_meet_integration_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_social_login_addon_active()
{
$value = get_option('acadlix_addon_social_login_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_bulk_question_addon_active()
{
$value = get_option('acadlix_addon_bulk_question_upload_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_assignment_addon_active()
{
$value = get_option('acadlix_addon_assignments_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_zoom_integration_addon_active()
{
$value = get_option('acadlix_addon_zoom_integration_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_advanced_report_addon_active()
{
$value = get_option('acadlix_addon_advanced_report_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_subscriptions_addon_active()
{
$value = get_option('acadlix_addon_subscriptions_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_data_exporter_addon_active()
{
$value = get_option('acadlix_addon_data_exporter_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_question_error_reporting_addon_active()
{
$value = get_option('acadlix_addon_question_error_reporting_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function is_course_bundle_addon_active()
{
$value = get_option('acadlix_addon_course_bundle_enabled', false);
if ($value != 'yes') {
return false;
}
if (!acadlix()->pro) {
return false;
} else {
if (!acadlix()->license()->isActive) {
return false;
}
}
return true;
}
public function acadlix_delete_addon_options()
{
$options = $this->acadlix_get_all_addons();
foreach ($options as $option) {
$this->acadlix_delete_option($option['option_name']);
}
}
public function acadlix_extra_options()
{
return [
'acadlix_theme_settings',
'acadlix_free_course_enrollment_update',
'acadlix_wizard',
'acadlix_deactivated_plugin_notice',
'acadlix_activation_pending',
'acadlix_phone_user_meta_key',
'acadlix_phonecode_user_meta_key',
'acadlix_flush_rewrite',
'acadlix_registration_options'
];
}
public function acadlix_delete_extra_options()
{
$options = $this->acadlix_extra_options();
foreach ($options as $option) {
$this->acadlix_delete_option($option);
}
}
private function acadlix_allowed_html()
{
return [
'main' => ['id' => true, 'class' => true, 'role' => true, 'style' => true],
'section' => ['id' => true, 'class' => true, 'style' => true],
'header' => ['id' => true, 'class' => true, 'style' => true],
'footer' => ['id' => true, 'class' => true, 'style' => true],
'nav' => ['id' => true, 'class' => true, 'role' => true, 'style' => true, 'aria-*' => true],
'div' => ['id' => true, 'class' => true, 'style' => true, 'role' => true, 'data-*' => true, 'aria-*' => true],
'span' => ['id' => true, 'class' => true, 'style' => true, 'data-*' => true, 'aria-*' => true],
'p' => ['id' => true, 'class' => true, 'style' => true],
'a' => [
'href' => true,
'class' => true,
'target' => true,
'rel' => true,
'title' => true,
'id' => true,
'style' => true,
'aria-*' => true,
'data-*' => true,
],
'ul' => ['id' => true, 'class' => true, 'style' => true, 'data-*' => true],
'ol' => ['id' => true, 'class' => true, 'style' => true, 'data-*' => true],
'li' => ['id' => true, 'class' => true, 'style' => true, 'data-*' => true],
'strong' => [],
'em' => [],
'br' => [],
'small' => ['class' => true, 'style' => true],
'h1' => ['id' => true, 'class' => true, 'style' => true],
'h2' => ['id' => true, 'class' => true, 'style' => true],
'h3' => ['id' => true, 'class' => true, 'style' => true],
'h4' => ['id' => true, 'class' => true, 'style' => true],
'h5' => ['id' => true, 'class' => true, 'style' => true],
'h6' => ['id' => true, 'class' => true, 'style' => true],
'img' => [
'src' => true,
'alt' => true,
'class' => true,
'width' => true,
'height' => true,
'loading' => true,
'srcset' => true,
'sizes' => true,
'style' => true,
'data-*' => true,
],
'i' => ['class' => true, 'aria-hidden' => true, 'style' => true],
'svg' => ['class' => true, 'viewBox' => true, 'xmlns' => true, 'width' => true, 'height' => true, 'aria-hidden' => true, 'focusable' => true, 'role' => true],
'path' => ['d' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true],
'button' => ['type' => true, 'class' => true, 'id' => true, 'data-*' => true, 'aria-*' => true, 'style' => true],
'input' => ['type' => true, 'name' => true, 'value' => true, 'placeholder' => true, 'class' => true, 'id' => true, 'checked' => true, 'disabled' => true, 'aria-*' => true, 'data-*' => true, 'style' => true],
'label' => ['for' => true, 'class' => true, 'id' => true, 'style' => true],
'select' => ['name' => true, 'class' => true, 'id' => true, 'style' => true],
'option' => ['value' => true, 'selected' => true, 'label' => true],
'textarea' => ['name' => true, 'class' => true, 'id' => true, 'rows' => true, 'cols' => true, 'placeholder' => true, 'style' => true],
'form' => ['action' => true, 'method' => true, 'class' => true, 'id' => true, 'style' => true],
'time' => ['datetime' => true, 'class' => true],
'figure' => ['class' => true],
'figcaption' => ['class' => true],
'style' => true,
];
}
public function acadlix_render_component($node)
{
// Allowed HTML for dynamic rendering
$allowed_html = $this->acadlix_allowed_html();
// Allow plain string or callable directly
if (is_string($node)) {
echo wp_kses($node, $allowed_html);
return;
}
if (is_callable($node)) {
$output = call_user_func($node);
echo wp_kses($output, $allowed_html);
return;
}
if (!is_array($node)) {
return;
}
// Skip if not visible
if (isset($node['visible']) && $node['visible'] === false) {
return;
}
$tag = !empty($node['component']) ? strtolower($node['component']) : 'div';
// Special case: "php" pseudo-component
if ($tag === 'php' && isset($node['value'])) {
$output = is_callable($node['value'])
? call_user_func($node['value'])
: $node['value'];
echo wp_kses($output, $allowed_html);
return;
}
/**
* ----------------------------------------
* Build attributes safely
* ----------------------------------------
*/
$attributes = '';
if (!empty($node['props']) && is_array($node['props'])) {
foreach ($node['props'] as $key => $value) {
if (is_bool($value)) {
if ($value) {
$attributes .= ' ' . esc_attr($key);
}
continue;
}
if ($value !== null) {
// Special handling for style
if ($key === 'style') {
$safe_style = safecss_filter_attr($value);
if (!empty($safe_style)) {
$attributes .= sprintf(
' %s="%s"',
esc_attr($key),
esc_attr($safe_style)
);
}
continue;
}
$attributes .= sprintf(
' %s="%s"',
esc_attr($key),
esc_attr($value)
);
}
}
}
// Self-closing tags
$self_closing = in_array(
$tag,
['input', 'img', 'br', 'hr', 'meta', 'link'],
true
);
if ($self_closing) {
printf(
'<%1$s%2$s />',
esc_html($tag),
$attributes // phpcs:ignore
);
return;
}
// Output opening tag
printf(
'<%1$s %2$s>',
esc_html($tag),
$attributes // phpcs:ignore
);
if (array_key_exists('value', $node) && $node['value'] !== null) {
echo wp_kses($node['value'], $allowed_html);
}
/**
* ----------------------------------------
* Children
* ----------------------------------------
*/
if (!empty($node['children']) && is_array($node['children'])) {
foreach ($node['children'] as $child) {
$this->acadlix_render_component($child);
}
}
/**
* ----------------------------------------
* Closing tag
* ----------------------------------------
*/
printf(
'%s>',
esc_html($tag)
);
}
public function acadlix_render_tree($tree)
{
// $tree = apply_filters('acadlix_render_tree', $tree);
if (is_array($tree)) {
foreach ($tree as $node) {
$this->acadlix_render_component($node);
}
} elseif (is_string($tree) || is_callable($tree)) {
$this->acadlix_render_component($tree);
}
}
public function acadlix_add_to_date($date = '', $format = 'Y-m-d H:i:s', $number = 1, $type = 'day')
{
// Convert given date to timestamp (WP timezone aware)
if (empty($date)) {
$date = current_time('timestamp');
}
$timestamp = is_numeric($date) ? (int) $date : strtotime($date);
if (!$timestamp) {
return false;
}
switch (strtolower($type)) {
case 'day':
case 'days':
$timestamp += $number * DAY_IN_SECONDS;
break;
case 'week':
case 'weeks':
$timestamp += $number * WEEK_IN_SECONDS;
break;
case 'month':
case 'months':
$timestamp = strtotime("+{$number} month", $timestamp);
break;
case 'year':
case 'years':
$timestamp = strtotime("+{$number} year", $timestamp);
break;
default:
return false;
}
$tz_string = $this->acadlix_get_option('timezone_string') ?: 'UTC';
$timezone = new \DateTimeZone($tz_string);
return wp_date($format, $timestamp, $timezone);
}
public function acadlix_convert_to_unit_price($amount = 0)
{
// Convert null/empty to string
if ($amount === null) {
$amount = '0';
}
// Get decimal places (default 2)
$decimal_places = $this->acadlix_get_option('acadlix_number_of_decimals', 2);
$multiplier = pow(10, $decimal_places);
// Get thousand separator (default empty string)
$thousand_separator = $this->acadlix_get_option('acadlix_thousand_separator', '');
// Remove thousand separator from string if present
if ($thousand_separator !== '') {
$amount = str_replace($thousand_separator, '', strval($amount));
}
// Convert to number and multiply
$amount = floatval($amount);
return round($amount * $multiplier);
}
public function acadlix_format_price_for_storage($price)
{
$decimal_places = $this->acadlix_get_option('acadlix_number_of_decimals', 2);
return round((float) $price, (int) $decimal_places);
}
public function acadlix_format_price_for_display($price)
{
$decimal_places = $this->acadlix_get_option('acadlix_number_of_decimals', 2);
$decimal_separator = $this->acadlix_get_option('acadlix_decimal_separator', '.');
$thousand_separator = $this->acadlix_get_option('acadlix_thousand_separator', '');
return number_format((float) $price, (int) $decimal_places, $decimal_separator, $thousand_separator);
}
public function acadlix_get_price_with_currency(float|string $price, $currency = '')
{
$price = (float) preg_replace('/[^\d.-]/', '', (string) $price);
if (empty($currency)) {
$currency = $this->acadlix_get_option('acadlix_currency');
}
$currency_position_option = $this->acadlix_get_option('acadlix_currency_position');
$currency_symbols = $this->acadlix_currency_symbols();
if (!array_key_exists($currency, $currency_symbols)) {
return new \WP_Error('currency_symbol_not_found', __('Currency symbol not found for the selected currency.', 'acadlix'));
}
$currency_symbol = $currency_symbols[$currency];
$price = $this->acadlix_format_price_for_display($price);
return match ($currency_position_option) {
'Left ( $99.99 )' => "$currency_symbol$price",
'Right ( 99.99$ )' => "$price$currency_symbol",
'Left with space ( $ 99.99 )' => "$currency_symbol $price",
'Right with space ( 99.99 $ )' => "$price $currency_symbol",
default => "$currency_symbol$price",
};
}
public function acadlix_is_json($string)
{
if (!is_string($string)) {
return false;
}
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
public function acadlix_format_e164($phone, $country_code)
{
if (empty($phone) || empty($country_code)) {
return '';
}
// If already E.164 → trust but verify
if (strpos($phone, '+') === 0) {
return preg_replace('/[^\+\d]/', '', $phone);
}
$phone = preg_replace('/\D/', '', $phone);
$country_code = preg_replace('/\D/', '', $country_code);
// Remove leading zero
if (substr($phone, 0, 1) === '0') {
$phone = substr($phone, 1);
}
return '+' . $country_code . $phone;
}
public function acadlix_css_color($value, $default)
{
if (!is_string($value)) {
return $default;
}
$value = trim($value);
// Hex colors (#fff, #ffffff)
if (sanitize_hex_color($value)) {
return $value;
}
// hsl(0–360, 0–100%, 0–100%)
if (
preg_match(
'/^hsl\(\s*(?:[0-9]|[1-9][0-9]|[12][0-9]{2}|3[0-5][0-9]|360)\s*,\s*(?:100|[0-9]{1,2})%\s*,\s*(?:100|[0-9]{1,2})%\s*\)$/',
$value
)
) {
return $value;
}
// rgb(0–255, 0–255, 0–255)
if (
preg_match(
'/^rgb\(\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*,\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*,\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*\)$/',
$value
)
) {
return $value;
}
// rgba(0–255, 0–255, 0–255, 0–1)
if (
preg_match(
'/^rgba\(\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*,\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*,\s*(?:25[0-5]|2[0-4]\d|1?\d{1,2})\s*,\s*(?:0|1|0?\.\d+)\s*\)$/',
$value
)
) {
return $value;
}
return $default;
}
public function acadlix_css_size($value, $default)
{
if (preg_match('/^\d+(\.\d+)?(px|rem|em|%)$/', (string) $value)) {
return $value;
}
return $default;
}
public function acadlix_css_number($value, $default)
{
return is_numeric($value) ? (string) $value : (string) $default;
}
public function acadlix_css_line_height($value, $default)
{
if (is_numeric($value) || preg_match('/^\d+(\.\d+)?$/', (string) $value)) {
return (string) $value;
}
return $default;
}
public function acadlix_generate_certificate_id()
{
do {
$id = 'CRT-' . wp_date('Y') . '-' . strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
} while (
acadlix()->model()->userActivityMeta()->where('reference_id', $id)->exists()
);
return $id;
}
public function acadlix_httpsify($url)
{
if (strpos($url, 'localhost') === false) {
return str_replace('http://', 'https://', $url);
} else {
return $url;
}
}
public function acadlix_get_wp_rest_nonce()
{
return wp_create_nonce('wp_rest');
}
public function acadlix_get_api_url()
{
return esc_url_raw(rest_url('acadlix/v1'));
}
public function acadlix_sync_user_billing_data($user_id, array $billing_info)
{
if (!$user_id || empty($billing_info)) {
return new WP_Error('invalid_data', __('User ID and billing information are required.', 'acadlix'));
}
// 1. Define required keys and sanitize input
$required_fields = array('first_name', 'email');
foreach ($required_fields as $field) {
if (empty($billing_info[$field])) {
/* translators: %s is the field name */
return new WP_Error('missing_field', sprintf(__('The %s field is required in billing info.', 'acadlix'), $field));
}
}
$user = get_userdata($user_id);
if (!$user) {
return new WP_Error('user_not_found', __('User does not exist.', 'acadlix'));
}
// 2. Sync standard WordPress User Fields (if currently empty)
$user_updates = array('ID' => $user_id);
if (empty($user->first_name) && !empty($billing_info['first_name'])) {
$user_updates['first_name'] = sanitize_text_field($billing_info['first_name']);
}
if (empty($user->last_name) && !empty($billing_info['last_name'])) {
$user_updates['last_name'] = sanitize_text_field($billing_info['last_name']);
}
// Update core user record only if needed
if (count($user_updates) > 1) {
wp_update_user($user_updates);
}
// 3. Sync User Meta Fields (only update if meta does not exist or is empty)
$meta_mappings = array(
'_acadlix_profile_phonecode' => 'phonecode',
'_acadlix_profile_isocode' => 'isocode',
'_acadlix_profile_phone_number' => 'phone_number',
'_acadlix_profile_address' => 'address',
'_acadlix_profile_country' => 'country',
'_acadlix_profile_city' => 'city',
'_acadlix_profile_zip_code' => 'zip_code',
);
foreach ($meta_mappings as $meta_key => $input_key) {
if (!empty($billing_info[$input_key])) {
$existing_val = get_user_meta($user_id, $meta_key, true);
if (empty($existing_val)) {
update_user_meta($user_id, $meta_key, sanitize_text_field($billing_info[$input_key]));
}
}
}
return true;
}
public static function instance()
{
if (is_null(self::$_instance)) {
self::$_instance = new self();
}
return self::$_instance;
}
}
}