)/i';
const ASCII_VALUES_BLACKLIST = ['32', '34', '39', '60', '62', '63', '92', '94', '96', '127'];
private static ?self $instance = null;
private static array $cryptXOptions = [];
private static int $imageCounter = 0;
private const FONT_EXTENSION = 'ttf';
private const PAYPAL_DONATION_URL = 'https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4026696';
private const MAILTO_PATTERN = '/\s*(.*?)\s*<\/a>/i';
private const EMAIL_PATTERN = "/([_a-zA-Z0-9-+]+(\.[_a-zA-Z0-9-+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,}))/i";
private CryptXSettingsTabs $settingsTabs;
private Config $config;
private function __construct()
{
$this->settingsTabs = new CryptXSettingsTabs($this);
$this->config = new Config(get_option('cryptX', []));
self::$cryptXOptions = $this->loadCryptXOptionsWithDefaults();
}
/**
* Retrieves the singleton instance of the class.
*
* @return self The singleton instance of the class.
*/
public static function get_instance(): self
{
$needs_initialization = !(self::$instance instanceof self);
if ($needs_initialization) {
self::$instance = new self();
}
return self::$instance;
}
/**
* @return Config
*/
public function getConfig(): Config
{
return $this->config;
}
/**
* Initializes the CryptX plugin by setting up version checks, applying filters, registering core hooks, initializing meta boxes (if enabled), and adding additional hooks.
*
* @return void
*/
public function startCryptX(): void
{
$this->checkAndUpdateVersion();
$this->addUniversalWidgetFilters(); // Add this line
$this->initializePluginFilters();
$this->registerCoreHooks();
$this->initializeMetaBoxIfEnabled();
$this->registerAdditionalHooks();
}
/**
* Checks the current version of the application against the stored version and updates settings if the application version is newer.
*
* @return void
*/
private function checkAndUpdateVersion(): void
{
$currentVersion = self::$cryptXOptions['version'] ?? null;
if ($currentVersion && version_compare(CRYPTX_VERSION, $currentVersion) > 0) {
$this->updateCryptXSettings();
}
}
/**
* Initializes and applies plugin filters based on the defined configuration options.
*
* @return void
*/
public function initializePluginFilters(): void
{
if (empty($this->config)) {
return;
}
$activeFilters = $this->config->getActiveFilters();
foreach ($activeFilters as $filter) {
if ($filter === 'widget_text') {
$this->addWidgetFilters();
} else {
// Add autolink filters for non-widget filters if autolink is enabled
if ($this->config->isAutolinkEnabled()) {
$this->addAutoLinkFilters($filter, 11);
}
$this->addOtherFilters($filter);
}
}
}
/**
* Registers core hooks for the plugin's functionality.
*
* @return void
*/
private function registerCoreHooks(): void
{
add_action('activate_' . CRYPTX_BASENAME, [$this, 'installCryptX']);
add_action('wp_enqueue_scripts', [$this, 'loadJavascriptFiles']);
}
/**
* Initializes the meta box functionality if enabled in the configuration.
*
* This method checks whether the meta box feature is enabled in the cryptX options.
* If enabled, it adds the necessary actions for administering the meta box and managing the posts' exclusion list.
*
* @return void
*/
private function initializeMetaBoxIfEnabled(): void
{
if (!isset(self::$cryptXOptions['metaBox']) || !self::$cryptXOptions['metaBox']) {
return;
}
add_action('admin_menu', [$this, 'metaBox']);
add_action('wp_insert_post', [$this, 'addPostIdToExcludedList']);
add_action('wp_update_post', [$this, 'addPostIdToExcludedList']);
}
/**
* Registers additional WordPress hooks and shortcodes.
*
* @return void
*/
private function registerAdditionalHooks(): void
{
add_filter('plugin_row_meta', [$this, 'add_plugin_action_links'], 10, 2);
add_filter('init', [$this, 'cryptXtinyUrl']);
add_shortcode('cryptx', [$this, 'cryptXShortcode']);
}
/**
* Retrieves the default options for CryptX configuration.
*
* @return array The default CryptX options, including version and font settings.
*/
public function getCryptXOptionsDefaults(): array
{
return array_merge(
$this->config->getAll(),
[
'version' => CRYPTX_VERSION,
'c2i_font' => $this->getDefaultFont()
]
);
}
/**
* Retrieves the default font from the available fonts directory.
*
* @return string|null Returns the name of the default font found, or null if no fonts are available.
*/
private function getDefaultFont(): ?string
{
$availableFonts = $this->getFilesInDirectory(
CRYPTX_DIR_PATH . 'fonts',
[self::FONT_EXTENSION]
);
return $availableFonts[0] ?? null;
}
/**
* Loads the cryptX options with default values.
*
* @return array The cryptX options array with default values.
*/
public function loadCryptXOptionsWithDefaults(): array
{
$defaultValues = $this->getCryptXOptionsDefaults();
$currentOptions = get_option('cryptX');
return wp_parse_args($currentOptions, $defaultValues);
}
/**
* Saves the cryptX options by updating the 'cryptX' option with the saved options merged with the default options.
*
* @param array $saveOptions The options to be saved.
*
* @return void
*/
public function saveCryptXOptions(array $saveOptions): void
{
update_option('cryptX', wp_parse_args($saveOptions, $this->loadCryptXOptionsWithDefaults()));
}
/**
* Decodes attributes from their encoded state and returns the decoded array.
*
* @param array $attributes The array of attributes, potentially encoded.
* @return array The array of decoded attributes with the 'encoded' key removed if present.
*/
private function decodeAttributes(array $attributes): array
{
if (($attributes['encoded'] ?? '') !== 'true') {
return $attributes;
}
$decodedAttributes = array_map(
fn($value) => $this->decodeString($value),
$attributes
);
unset($decodedAttributes['encoded']);
return $decodedAttributes;
}
/**
* Processes the provided shortcode attributes and content, encrypts content, and optionally creates links for email addresses.
*
* @param array $atts Attributes passed to the shortcode. Defaults to an empty array.
* @param string $content The content enclosed within the shortcode. Defaults to an empty string.
* @param string $tag The name of the shortcode tag. Defaults to an empty string.
* @return string The processed and encrypted content, optionally including links for email addresses.
*/
public function cryptXShortcode(array $atts = [], string $content = '', string $tag = ''): string
{
// Decode attributes if needed
$attributes = $this->decodeAttributes($atts);
// Update options if attributes provided
if (!empty($attributes)) {
self::$cryptXOptions = shortcode_atts(
$this->loadCryptXOptionsWithDefaults(),
array_change_key_case($attributes, CASE_LOWER),
$tag
);
}
// Process content (inline the encryptAndLinkContent logic)
if (self::$cryptXOptions['autolink'] ?? false) {
$content = $this->addLinkToEmailAddresses($content, true);
}
$content = $this->findEmailAddressesInContent($content, true);
$processedContent = $this->replaceEmailInContent($content, true);
// Reset options to defaults
self::$cryptXOptions = $this->loadCryptXOptionsWithDefaults();
return $processedContent;
}
/**
* Encrypts and links content.
*
* @param string $content The content to be encrypted and linked.
*
* @return string The encrypted and linked content.
*/
private function encryptAndLinkContent(string $content, bool $shortcode = false): string
{
$content = $this->findEmailAddressesInContent($content, $shortcode);
return $this->replaceEmailInContent($content, $shortcode);
}
/**
* Retrieves the ID of the current post.
*
* @return int The current post ID if available, or -1 if no post object is present.
*/
private function getCurrentPostId(): int
{
global $post;
return (is_object($post)) ? $post->ID : -1;
}
/**
* Generates and returns a tiny URL image.
*
* @return void
*/
public function cryptXtinyUrl(): void
{
$url = $_SERVER['REQUEST_URI'];
$params = explode('/', $url);
if (count($params) > 1) {
$tiny_url = $params[count($params) - 2];
if ($tiny_url == md5(get_bloginfo('url'))) {
$font = CRYPTX_DIR_PATH . 'fonts/' . self::$cryptXOptions['c2i_font'];
$msg = $params[count($params) - 1];
$size = self::$cryptXOptions['c2i_fontSize'];
$pad = 1;
$transparent = 1;
$rgb = str_replace("#", "", self::$cryptXOptions['c2i_fontRGB']);
$red = hexdec(substr($rgb, 0, 2));
$grn = hexdec(substr($rgb, 2, 2));
$blu = hexdec(substr($rgb, 4, 2));
$bg_red = 255 - $red;
$bg_grn = 255 - $grn;
$bg_blu = 255 - $blu;
$width = 0;
$height = 0;
$offset_x = 0;
$offset_y = 0;
$bounds = array();
$image = "";
$bounds = ImageTTFBBox($size, 0, $font, "W");
$font_height = abs($bounds[7] - $bounds[1]);
$bounds = ImageTTFBBox($size, 0, $font, $msg);
$width = abs($bounds[4] - $bounds[6]);
$height = abs($bounds[7] - $bounds[1]);
$offset_y = $font_height + abs(($height - $font_height) / 2) - 1;
$offset_x = 0;
$image = imagecreatetruecolor($width + ($pad * 2), $height + ($pad * 2));
imagesavealpha($image, true);
$foreground = ImageColorAllocate($image, $red, $grn, $blu);
$background = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagefill($image, 0, 0, $background);
ImageTTFText($image, $size, 0, round($offset_x + $pad, 0), round($offset_y + $pad, 0), $foreground, $font, $msg);
Header("Content-type: image/png");
imagePNG($image);
die;
}
}
}
/**
* Adds common filters to a given filter name.
*
* This function adds the common filter 'autolink' to the provided $filterName.
*
* @param string $filterName The name of the filter to add common filters to.
*
* @return void
*/
private function addAutoLinkFilters(string $filterName, $prio = 5): void
{
add_filter($filterName, [$this, 'addLinkToEmailAddresses'], $prio);
}
/**
* Adds additional filters to a given filter name.
*
* This function adds two additional filters, 'encryptx' and 'replaceEmailInContent',
* to the specified filter name. The 'encryptx' filter is added with a priority of 12,
* and the 'replaceEmailInContent' filter is added with a priority of 13.
*
* @param string $filterName The name of the filter to add the additional filters to.
*
* @return void
*/
private function addOtherFilters(string $filterName): void
{
// Check if this is a widget filter
$widgetFilters = $this->config->getWidgetFilters();
$isWidgetFilter = in_array($filterName, $widgetFilters);
if ($isWidgetFilter) {
// Use higher priority for widget filters (after autolink at priority 10)
add_filter($filterName, [$this, 'findEmailAddressesInContent'], 15);
add_filter($filterName, [$this, 'replaceEmailInContent'], 16);
} else {
// Standard priorities for other filters
add_filter($filterName, [$this, 'findEmailAddressesInContent'], 12);
add_filter($filterName, [$this, 'replaceEmailInContent'], 13);
}
}
/**
* Adds and applies widget filters from the configuration.
*
* @return void
*/
private function addWidgetFilters(): void
{
$widgetFilters = $this->config->getWidgetFilters();
foreach ($widgetFilters as $widgetFilter) {
$this->addAutoLinkFilters($widgetFilter, 11);
$this->addOtherFilters($widgetFilter);
}
}
/**
* Checks if a given ID is excluded based on the 'excludedIDs' variable.
*
* @param int $ID The ID to check if excluded.
*
* @return bool Returns true if the ID is excluded, false otherwise.
*/
private function isIdExcluded(int $ID): bool
{
$excludedIds = explode(",", self::$cryptXOptions['excludedIDs']);
return in_array($ID, $excludedIds);
}
/**
* Replaces email addresses in content with link texts.
*
* @param string|null $content The content to replace the email addresses in.
* @param bool $isShortcode Flag indicating whether the method is called from a shortcode.
*
* @return string|null The content with replaced email addresses.
*/
public function replaceEmailInContent(?string $content, bool $isShortcode = false): ?string
{
global $post;
if (self::$cryptXOptions['disable_rss'] && $this->isRssFeed()) return $content;
// Check if current filter is a widget filter
$widgetFilters = $this->config->getWidgetFilters();
$isWidgetContext = in_array(current_filter(), $widgetFilters);
$postId = (is_object($post)) ? $post->ID : -1;
// For widgets, always process; for other content, check exclusion rules
if (($isWidgetContext || !$this->isIdExcluded($postId) || $isShortcode) && !empty($content)) {
$content = $this->replaceEmailWithLinkText($content);
}
return $content;
}
/**
* Replace email addresses in a given content with link text.
*
* @param string $content The content to search for email addresses.
*
* @return string The content with email addresses replaced with link text.
*/
private function replaceEmailWithLinkText(string $content): string
{
$emailPattern = "/([_a-zA-Z0-9-+]+(\.[_a-zA-Z0-9-+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,}))/i";
return preg_replace_callback($emailPattern, [$this, 'encodeEmailToLinkText'], $content);
}
/**
* Encode email address to link text.
*
* @param array $Match The matched email address.
*
* @return string The encoded link text.
*/
private function encodeEmailToLinkText(array $Match): string
{
if ($this->inWhiteList($Match)) {
return $Match[1];
}
switch (self::$cryptXOptions['opt_linktext']) {
case 1:
$text = $this->getLinkText();
break;
case 2:
$text = $this->getLinkImage();
break;
case 3:
$img_url = wp_get_attachment_url(self::$cryptXOptions['alt_uploadedimage']);
$text = $this->getUploadedImage($img_url);
self::$imageCounter++;
break;
case 4:
$text = antispambot($Match[1]);
break;
case 5:
$text = $this->getImageFromText($Match);
self::$imageCounter++;
break;
default:
$text = $this->getDefaultLinkText($Match);
}
return $text;
}
/**
* Check if the given match is in the whitelist.
*
* @param array $Match The match to check against the whitelist.
*
* @return bool True if the match is in the whitelist, false otherwise.
*/
private function inWhiteList(array $Match): bool
{
$whiteList = array_filter(array_map('trim', explode(",", self::$cryptXOptions['whiteList'])));
$tmp = explode(".", $Match[0]);
return in_array(end($tmp), $whiteList);
}
/**
* Get the link text from cryptXOptions
*
* @return string The link text
*/
private function getLinkText(): string
{
return self::$cryptXOptions['alt_linktext'];
}
/**
* Generate an HTML image tag with the link image URL as the source
*
* @return string The HTML image tag
*/
private function getLinkImage(): string
{
return "";
}
/**
* Get the HTML tag for an uploaded image.
*
* @param string $img_url The URL of the image.
*
* @return string The HTML tag for the image.
*/
private function getUploadedImage(string $img_url): string
{
return "
";
}
/**
* Converts a matched image URL into an HTML image element with cryptX classes and attributes.
*
* @param array $Match The matched image URL and other related data.
*
* @return string Returns the HTML image element.
*/
private function getImageFromText(array $Match): string
{
return "
";
}
/**
* Replaces specific characters with values from cryptX options in a given string.
*
* @param array $Match The array containing matches from a regular expression search.
* Array format: `[0 => string, 1 => string, ...]`.
* The first element is ignored, and the second element is used as input string.
*
* @return string The string with replaced characters or the original array if no matches were found.
* If the input string is an array, the function returns an array with replaced characters
* for each element.
*/
private function getDefaultLinkText(array $Match): string
{
$text = str_replace("@", self::$cryptXOptions['at'], $Match[1]);
return str_replace(".", self::$cryptXOptions['dot'], $text);
}
/**
* List all files in a directory that match the given filter.
*
* @param string $path The path of the directory to list files from.
* @param array $filter The file extensions to filter by.
* If it's a string, it will be converted to an array of a single element.
*
* @return array An array of file names that match the filter.
*/
public function getFilesInDirectory(string $path, array $filter): array
{
$directoryHandle = opendir($path);
$directoryContent = array();
while ($file = readdir($directoryHandle)) {
$fileExtension = substr(strtolower($file), -3);
if (in_array($fileExtension, $filter)) {
$directoryContent[] = $file;
}
}
return $directoryContent;
}
/**
* Finds and processes email addresses within the given content.
*
* This method scans the provided content for email addresses and encrypts them based on the configuration.
* It checks for RSS feed settings and excluded post IDs to determine whether encryption should be applied.
*
* @param string|null $content The content to search for email addresses. If null, the method returns null.
* @param bool $shortcode Specifies whether the method is invoked via a shortcode.
* @return string|null The processed content with email addresses encrypted, or null if the input content is null.
*/
public function findEmailAddressesInContent(?string $content, bool $shortcode = false): ?string
{
global $post;
if (self::$cryptXOptions['disable_rss'] && $this->isRssFeed()) return $content;
if ($content === null) {
return null;
}
// Check if current filter is a widget filter
$widgetFilters = $this->config->getWidgetFilters();
$isWidgetContext = in_array(current_filter(), $widgetFilters);
$postId = (is_object($post)) ? $post->ID : -1;
$isIdExcluded = $this->isIdExcluded($postId);
$mailtoRegex = '/]*href=(["\'])mailto:([^"\']+)\1[^>]*>(.*?)<\/a>/is';
// For widgets, always process since there's no specific post context
// For other content, check exclusion rules
if ($isWidgetContext || !$isIdExcluded || $shortcode) {
// $content = preg_replace_callback($mailtoRegex, [$this, 'encryptEmailAddressNew'], $content);
$content = preg_replace_callback($mailtoRegex, [$this, 'encryptEmailAddressSecure'], $content);
}
return $content;
}
/**
* Encrypts email addresses in search results.
*
* @param array $searchResults The search results containing email addresses.
*
* @return string The search results with encrypted email addresses.
*/
private function encryptEmailAddress(array $searchResults): string
{
$originalValue = $searchResults[0];
if (strpos($searchResults[self::INDEX_TO_CHECK], '@') === self::NOT_FOUND) {
return $originalValue;
}
$mailReference = self::MAIL_IDENTIFIER . $searchResults[self::INDEX_TO_CHECK];
if (str_starts_with($searchResults[self::INDEX_TO_CHECK], self::SUBJECT_IDENTIFIER)) {
return $originalValue;
}
$return = $originalValue;
// Apply JavaScript handler if enabled
if (!empty(self::$cryptXOptions['java'])) {
$javaHandler = "javascript:DeCryptX('" . $this->generateHashFromString($searchResults[self::INDEX_TO_CHECK]) . "')";
$return = str_replace(self::MAIL_IDENTIFIER . $searchResults[self::INDEX_TO_CHECK], $javaHandler, $originalValue);
} else {
// Only apply antispambot if JavaScript is not enabled
$return = str_replace($mailReference, antispambot($mailReference), $return);
}
// Add CSS attributes if specified
if (!empty(self::$cryptXOptions['css_id'])) {
$return = preg_replace(self::PATTERN, '$1" id="' . self::$cryptXOptions['css_id'] . '">', $return);
}
if (!empty(self::$cryptXOptions['css_class'])) {
$return = preg_replace(self::PATTERN, '$1" class="' . self::$cryptXOptions['css_class'] . '">', $return);
}
return $return;
}
/**
* Encrypts an email address within the provided search results and generates a secure or obfuscated link.
* If secure encryption is enabled, the function uses secure encryption. Otherwise, it falls back to legacy methods
* or antispambot obfuscation if JavaScript is not enabled. Additional CSS attributes can be added if specified.
*
* @param array $searchResults The array containing match results:
* - Index 0: The full match value (original string),
* - Index 2: The email address to encrypt,
* - Index 3: The link text for the email link.
* @return string Returns the modified string where the email address is encrypted or obfuscated based on the configuration.
*/
private function encryptEmailAddressNew(array $searchResults): string
{
$originalValue = $searchResults[0]; // Full match
$emailAddress = $searchResults[2]; // Email address (now at index 2)
$linkText = $searchResults[3]; // Link text (now at index 3)
if (strpos($emailAddress, '@') === self::NOT_FOUND) {
return $originalValue;
}
if (str_starts_with($emailAddress, self::SUBJECT_IDENTIFIER)) {
return $originalValue;
}
$return = $originalValue;
// Apply JavaScript handler if enabled
if (!empty(self::$cryptXOptions['java'])) {
// Check if secure encryption is enabled and working
if ($this->config->isSecureEncryptionEnabled()) {
try {
// Use secure encryption - encrypt the full mailto URL
$password = $this->config->getEncryptionPassword();
$mailtoUrl = 'mailto:' . $emailAddress;
$encryptedEmail = SecureEncryption::encrypt($mailtoUrl, $password);
$javaHandler = "javascript:secureDecryptAndNavigate('" .
$this->escapeJavaScript($encryptedEmail) . "', '" .
$this->escapeJavaScript($password) . "')";
} catch (\Exception $e) {
// Fallback to legacy encryption if secure encryption fails
error_log('CryptX Secure Encryption failed: ' . $e->getMessage());
$encryptedEmail = $this->generateHashFromString($emailAddress);
$javaHandler = "javascript:DeCryptX('" . $this->escapeJavaScript($encryptedEmail) . "')";
}
} else {
// Use legacy encryption
$encryptedEmail = $this->generateHashFromString($emailAddress);
$javaHandler = "javascript:DeCryptX('" . $this->escapeJavaScript($encryptedEmail) . "')";
}
$return = str_replace('mailto:' . $emailAddress, $javaHandler, $originalValue);
} else {
// Fallback to antispambot if JavaScript is not enabled
$return = str_replace('mailto:' . $emailAddress,
antispambot('mailto:' . $emailAddress), $return);
}
// Add CSS attributes if specified
if (!empty(self::$cryptXOptions['css_id'])) {
$return = preg_replace('/(]*)(>)/i',
'$1 id="' . self::$cryptXOptions['css_id'] . '"$2', $return);
}
if (!empty(self::$cryptXOptions['css_class'])) {
$return = preg_replace('/(]*)(>)/i',
'$1 class="' . self::$cryptXOptions['css_class'] . '"$2', $return);
}
return $return;
}
/**
* Generate a hash string for the given input string.
*
* @param string $inputString The input string to generate a hash for.
*
* @return string The generated hash string.
*/
private function generateHashFromString(string $inputString): string
{
$inputString = str_replace("&", "&", $inputString);
$crypt = '';
for ($i = 0; $i < strlen($inputString); $i++) {
do {
$salt = mt_rand(0, 3);
$asciiValue = ord(substr($inputString, $i)) + $salt;
if (8364 <= $asciiValue) {
$asciiValue = 128;
}
} while (in_array($asciiValue, self::ASCII_VALUES_BLACKLIST));
$crypt .= $salt . chr($asciiValue);
}
return $crypt;
}
/**
* add link to email addresses
*/
/**
* Auto-link emails in the given content.
*
* @param string $content The content to process.
* @param bool $shortcode Whether the function is called from a shortcode or not.
*
* @return string The content with emails auto-linked.
*/
public function addLinkToEmailAddresses(string $content, bool $shortcode = false): string
{
global $post;
// Check if current filter is a widget filter
$widgetFilters = $this->config->getWidgetFilters();
$isWidgetContext = in_array(current_filter(), $widgetFilters);
$postID = is_object($post) ? $post->ID : -1;
// For widgets, always process; for other content, check exclusion rules
if (!$isWidgetContext && $this->isIdExcluded($postID) && !$shortcode) {
return $content;
}
$emailPattern = "[_a-zA-Z0-9-+]+(\\.[_a-zA-Z0-9-+]+)*@[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(\\.[a-zA-Z]{2,})";
$linkPattern = "\\2";
$src = [
"/([\\s])($emailPattern)/si",
"/(>)($emailPattern)(<)/si",
"/(\\()($emailPattern)(\\))/si",
"/(>)($emailPattern)([\\s])/si",
"/([\\s])($emailPattern)(<)/si",
"/^($emailPattern)/si",
"/(]*>)]*>/",
"/(<\\/A>)<\\/A>/i"
];
$tar = [
"\\1$linkPattern",
"\\1$linkPattern\\6",
"\\1$linkPattern\\6",
"\\1$linkPattern\\6",
"\\1$linkPattern\\6",
"\\0",
"\\1",
"\\1"
];
return preg_replace($src, $tar, $content);
}
/**
* Installs the CryptX plugin by updating its options and loading default values.
*/
public function installCryptX(): void
{
global $wpdb;
self::$cryptXOptions['admin_notices_deprecated'] = true;
if (self::$cryptXOptions['excludedIDs'] == "") {
$tmp = array();
$excludes = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'cryptxoff' AND meta_value = 'true'");
if (count($excludes) > 0) {
foreach ($excludes as $exclude) {
$tmp[] = $exclude->post_id;
}
sort($tmp);
self::$cryptXOptions['excludedIDs'] = implode(",", $tmp);
update_option('cryptX', self::$cryptXOptions);
self::$cryptXOptions = $this->loadCryptXOptionsWithDefaults(); // reread Options
$wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key = 'cryptxoff'");
}
}
if (empty(self::$cryptXOptions['c2i_font'])) {
self::$cryptXOptions['c2i_font'] = CRYPTX_DIR_PATH . 'fonts/' . $firstFont[0];
}
if (empty(self::$cryptXOptions['c2i_fontSize'])) {
self::$cryptXOptions['c2i_fontSize'] = 10;
}
if (empty(self::$cryptXOptions['c2i_fontRGB'])) {
self::$cryptXOptions['c2i_fontRGB'] = '000000';
}
update_option('cryptX', self::$cryptXOptions);
self::$cryptXOptions = $this->loadCryptXOptionsWithDefaults(); // reread Options
}
private function addHooksHelper($function_name, $hook_name): void
{
if (function_exists($function_name)) {
call_user_func($function_name, 'cryptx', 'CryptX', [$this, 'metaCheckbox'], $hook_name);
} else {
add_action("dbx_{$hook_name}_sidebar", [$this, 'metaOptionFieldset']);
}
}
public function metaBox(): void
{
$this->addHooksHelper('add_meta_box', 'post');
$this->addHooksHelper('add_meta_box', 'page');
}
/**
* Displays a checkbox to disable CryptX for the current post or page.
*
* This function outputs HTML code for a checkbox that allows the user to disable CryptX
* functionality for the current post or page. If the current post or page ID is excluded
**/
public function metaCheckbox(): void
{
global $post;
?>