From 70a9adac4cc6c0fef1f6a6841eec7029cc3ede94 Mon Sep 17 00:00:00 2001
From: Cooper Dalrymple
Date: Tue, 22 Jul 2025 15:53:23 -0500
Subject: [PATCH] Initial commit of 0.2.0
---
.gitignore | 6 ++
.vscode/tasks.json | 25 +++++
Makefile | 22 +++++
README.md | 3 -
assets/js/obfuscation.js | 65 ++++++++++++
composer.json | 48 +++++++++
gulpfile.js | 53 ++++++++++
inc/filter.php | 73 ++++++++++++++
inc/global.php | 154 +++++++++++++++++++++++++++++
inc/settings.php | 208 +++++++++++++++++++++++++++++++++++++++
ogre-obfuscation.php | 56 +++++++++++
package.json | 25 +++++
readme.txt | 40 ++++++++
13 files changed, 775 insertions(+), 3 deletions(-)
create mode 100644 .gitignore
create mode 100644 .vscode/tasks.json
create mode 100644 Makefile
delete mode 100644 README.md
create mode 100644 assets/js/obfuscation.js
create mode 100644 composer.json
create mode 100644 gulpfile.js
create mode 100644 inc/filter.php
create mode 100644 inc/global.php
create mode 100644 inc/settings.php
create mode 100644 ogre-obfuscation.php
create mode 100644 package.json
create mode 100644 readme.txt
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..81781bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+vendor
+lib
+composer.lock
+package-lock.json
+node_modules
+*.zip
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..f22fd45
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,25 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Package",
+ "type": "shell",
+ "command": "gulp package",
+ "group": "build",
+ "presentation": {
+ "reveal": "silent",
+ "panel": "shared",
+ }
+ },
+ {
+ "label": "Install",
+ "type": "shell",
+ "command": "make install",
+ "group": "build",
+ "presentation": {
+ "reveal": "silent",
+ "panel": "shared",
+ }
+ }
+ ]
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9fc3f54
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+all: install
+
+reinstall: clean install
+
+clean: clean-composer clean-npm
+
+clean-composer:
+ rm -rf vendor/* || true
+ rm -rf lib/* || true
+ rm composer.lock || true
+ composer clearcache
+
+clean-npm:
+ rm -rf node_modules/* || true
+ rm package-lock.json || true
+
+install:
+ composer install
+ npm install
+
+package: install
+ gulp package
diff --git a/README.md b/README.md
deleted file mode 100644
index 5810b59..0000000
--- a/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# ogre-obfuscation
-
-Want to remove email addresses from your site when viewed by a robot using client-side encryption techniques? Then this is the plugin for you. Obscuring your email address to pesky bots can help reduce spam while still providing your clients with a direct way to message you and your business.
\ No newline at end of file
diff --git a/assets/js/obfuscation.js b/assets/js/obfuscation.js
new file mode 100644
index 0000000..5e981e0
--- /dev/null
+++ b/assets/js/obfuscation.js
@@ -0,0 +1,65 @@
+/**
+ * @package ogre-obfuscation
+ * @author cleverogre
+ * @version 0.2.0
+ * @since 0.1.0
+ */
+
+(() => {
+
+ const reObfuscateElement = (element) => {
+ element.outerHTML = unEncodeEmailChars(reObfuscateCore(element.getAttribute('data-content')));
+ };
+
+ const reObfuscate = (str, id) => {
+ const el = document.getElementById(id);
+ const newEl = document.createTextNode('');
+ newEl.innerHTML = unEncodeEmailChars(reObfuscateCore(str));
+ el.parentNode.replaceChild(newEl, el);
+ };
+
+ const reObfuscateCore = (str) => {
+ let out = '';
+ for (let i = 0; i < str.length; i++) {
+ out = str.charAt(i) + out;
+ }
+ return out;
+ };
+
+ const unEncodeEmailChars = (str) => {
+ let i = 0;
+ let num = '';
+ let out = '';
+
+ while (i < str.length) {
+ if (str.charAt (i) == '&') {
+ num = '';
+ i += 2; /// skip
+
+ while (str.charAt(i) != ';' && i < str.length) {
+ num += str.charAt(i);
+ i++;
+ }
+
+ if (str.charAt(i) == ";") {
+ i++;
+ }
+
+ out += String.fromCharCode(parseInt(num));
+ } else {
+ out += str.charAt(i);
+ i++;
+ }
+ }
+
+ return out;
+ };
+
+ document.addEventListener('DOMContentLoaded', () => {
+ const elements = document.getElementsByClassName('obfuscate');
+ for (let i = elements.length - 1; i >= 0; i--) {
+ reObfuscateElement(elements[i]);
+ }
+ });
+
+})();
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..5cafa5d
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,48 @@
+{
+ "$schema": "https://getcomposer.org/schema.json",
+ "name": "cleverogre/ogre-obfuscation",
+ "version": "0.2.0",
+ "title": "Ogre Obfuscation",
+ "description": "Ogre Obfuscation is a plugin developed by CleverOgre in Pensacola, Florida.",
+ "author": "CleverOgre",
+ "license": "GPL-3.0+",
+ "keywords": [
+ "WordPress",
+ "Plugin",
+ "Obfuscation"
+ ],
+ "homepage": "https://cleverogre.com",
+ "repositories": {
+ "cleverogre/plugin-framework": {
+ "type": "vcs",
+ "url": "git@git.cleverogre.com:cleverogre/plugin-framework.git"
+ },
+ "wp-package-updater": {
+ "type": "package",
+ "package": {
+ "name": "froger-me/wp-package-updater",
+ "version": "1.4.0",
+ "source": {
+ "url": "https://github.com/froger-me/wp-package-updater.git",
+ "type": "git",
+ "reference": "master"
+ }
+ }
+ }
+ },
+ "require": {
+ "cleverogre/plugin-framework": "dev-main",
+ "simplehtmldom/simplehtmldom": "dev-master"
+ },
+ "replace": {
+ "wpengine/advanced-custom-fields-pro": "*"
+ },
+ "scripts": {
+ "post-update-cmd": [
+ "php vendor/magicoli/wp-package-updater-lib/install.php"
+ ]
+ },
+ "config": {
+ "optimize-autoloader": true
+ }
+}
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..a4b8bff
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,53 @@
+const gulp = require('gulp'),
+ clean = require('gulp-clean'),
+ zip = require('gulp-zip').default;
+
+// Clean Tasks
+
+gulp.task('clean-package', () => {
+ return gulp.src('ogre-obfuscation.zip', {
+ read: false,
+ allowEmpty: true,
+ }).pipe(clean());
+});
+
+gulp.task(
+ 'clean',
+ gulp.series(
+ 'clean-package'
+ )
+);
+
+// Package Tasks
+
+gulp.task('package-compress', () => {
+ return gulp.src([
+ 'assets/**/*',
+ 'inc/**/*',
+ 'lib/**/*',
+ 'vendor/**/*',
+ 'LICENSE',
+ 'ogre-obfuscation.php',
+ 'readme.txt'
+ ], { base: './' })
+ .pipe(zip('ogre-obfuscation.zip'))
+ .pipe(gulp.dest('./'));
+});
+
+gulp.task(
+ 'package',
+ gulp.series(
+ 'clean',
+ 'package-compress'
+ )
+);
+
+// Default Tasks
+
+gulp.task(
+ 'default',
+ gulp.series(
+ 'clean',
+ 'package'
+ )
+);
diff --git a/inc/filter.php b/inc/filter.php
new file mode 100644
index 0000000..20b1b22
--- /dev/null
+++ b/inc/filter.php
@@ -0,0 +1,73 @@
+id_base;
+
+ if (is_callable($callback)) {
+ ob_start();
+ call_user_func_array($callback, $params);
+ $widget_output = ob_get_clean();
+
+ echo apply_filters('widget_output', $widget_output, $widget_id_base, $widget_id);
+ }
+ }
+}
+
+Filter::instance();
diff --git a/inc/global.php b/inc/global.php
new file mode 100644
index 0000000..07d6ae1
--- /dev/null
+++ b/inc/global.php
@@ -0,0 +1,154 @@
+email_enabled != '1') return false;
+
+ return true;
+}
+
+function obfuscate($str) {
+ static $obfuscate_id = 0;
+
+ $out = '';
+ for ($i = 0; $i < strlen($str); $i++) {
+ $out .= '' . ord(substr($str, $i, 1)) . ';';
+ }
+
+ $out2 = '';
+ for ($i = 0; $i < strlen($out); $i++) {
+ $out2 = substr($out, $i, 1) . $out2;
+ }
+
+ $output = "";
+ $obfuscate_id++;
+ return $output;
+}
+
+function _obfuscate_element($element) {
+ return obfuscate($element->outertext);
+}
+
+function find_emails($str) {
+ preg_match_all('/[a-zA-Z0-9_\-\+\.]+@[a-zA-Z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?/i', $str, $matches, PREG_OFFSET_CAPTURE);
+ if (!is_array($matches) || empty($matches)) return false;
+
+ $return = array();
+ foreach ($matches[0] as $match) {
+ $return[] = array(
+ 'text' => $match[0],
+ 'pos' => $match[1],
+ 'len' => strlen($match[0]),
+ );
+ }
+
+ return $return;
+}
+
+function filter($content) {
+ // Parse content
+ $html = new HtmlDocument();
+ $html->load('' . $content . '
');
+
+ //_filter_html($html->find('div[id=dom_root]', 0));
+
+ $html->find('div[id=dom_root]', 0)->outertext = _filter_html($html->find('div[id=dom_root]', 0));
+ $html->find('div[id=dom_root]', 0)->outertext = $html->find('div[id=dom_root]', 0)->innertext; // remove root div wrapper
+
+ $content = $html->save();
+
+ // Clean up memory
+ $html->clear();
+ unset($html);
+
+ return $content;
+}
+
+function _filter_html($element) {
+ if ($element == null) return false;
+
+ switch ($element->tag) {
+ case 'a':
+ if (_test_anchor($element)) {
+ return _obfuscate_element($element);
+ } else {
+ return $element->outertext;
+ }
+ default:
+ $node = _get_text_node($element);
+ $node['text'] = _filter_text($node['text']);
+
+ foreach ($node['tags'] as &$tag) {
+ $tag['content'] = _filter_html($tag['element']);
+ }
+
+ $element->outertext = _set_text_node($node);
+ return $element->outertext;
+ }
+}
+
+function _filter_text($text) {
+ $emails = find_emails($text);
+ if (!$emails) return $text;
+
+ for ($i = count($emails) - 1; $i >= 0; $i--) {
+ $text = substr($text, 0, $emails[$i]['pos']) . obfuscate($emails[$i]['text']) . substr($text, $emails[$i]['pos'] + $emails[$i]['len']);
+ }
+
+ return $text;
+}
+
+function _get_text_node($element) {
+ //$tag_pattern = '/<\s*[a-zA-Z]+[^>]*>.*?<\s*\/\s*[a-zA-Z]+>/is'
+
+ $text = $element->outertext;
+ $return = array(
+ 'raw' => $text,
+ 'text' => '',
+ 'tags' => array(),
+ );
+
+ $i = 0;
+ foreach ($element->children() as $child) {
+ $return['tags'][$i] = array(
+ 'element' => $child,
+ 'index' => $i,
+ 'key' => "[[{$i}]]",
+ 'content' => $child->outertext,
+ 'pos' => strpos($text, $child->outertext),
+ 'len' => strlen($child->outertext),
+ );
+ $text = str_replace($child->outertext, "[[{$i}]]", $text);
+ $i++;
+ }
+
+ $return['text'] = $text;
+ return $return;
+}
+
+function _set_text_node($node) {
+ $text = $node['text'];
+ foreach ($node['tags'] as $tag) {
+ $text = str_replace($tag['key'], $tag['content'], $text);
+ }
+ return $text;
+}
+
+function _test_anchor($element) {
+ if ($element->tag != 'a') return false;
+ return preg_match('/[a-zA-Z0-9_\-\+\.]+@[a-zA-Z0-9\-]+\.([a-z]{2,4})(?:\.[a-z]{2})?<\/a>/i', $element->outertext) == 1;
+}
diff --git a/inc/settings.php b/inc/settings.php
new file mode 100644
index 0000000..e47e7dd
--- /dev/null
+++ b/inc/settings.php
@@ -0,0 +1,208 @@
+ $value) {
+ unset($this->$key);
+ }
+ }
+
+ public function __set($key, $value) {
+ if (property_exists($this, $key)) {
+ switch ($key) {
+ /* // Read-only properties
+ case 'readonly':
+ break;
+ */
+
+ default:
+ $options = get_option('obfuscation_options');
+ $options['obfuscation_settings_' . $key] = $value;
+ update_option('obfuscation_options', $options);
+ break;
+ }
+ }
+ return $this;
+ }
+
+ public function __get($key) {
+ if (property_exists($this, $key)) {
+ switch ($key) {
+ // Constant properties
+ case 'defaults':
+ return array(
+ 'email_enabled' => '1',
+ );
+ break;
+
+ default:
+ $options = get_option('obfuscation_options');
+
+ if (isset($options['obfuscation_settings_' . $key]) && !empty($options['obfuscation_settings_' . $key])) {
+ return $options['obfuscation_settings_' . $key];
+ } else if (isset($this->defaults[$key])) {
+ return $this->defaults[$key];
+ } else {
+ return false;
+ }
+
+ break;
+ }
+ }
+ return null;
+ }
+}
+
+final class SettingsPage {
+ use Singleton;
+
+ protected function __construct() {
+ add_action('admin_init', array($this, 'init'));
+ add_action('admin_menu', array($this, 'menu'));
+ }
+
+ public function menu() {
+ if (!current_user_can('manage_options')) return;
+
+ $page = add_options_page(__('Ogre Obfuscation Settings', 'obfuscation'), __('Ogre Obfuscation', 'obfuscation'), 'manage_options', 'obfuscation_settings', array($this, 'page'));
+ add_action('load-' . $page, array($this, 'load'));
+ }
+
+ public function load() {
+ if (!current_user_can('manage_options')) return;
+
+ add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
+ }
+
+ public function admin_enqueue_scripts() {
+ //wp_enqueue_script('obfuscation-admin', Settings::instance()->dir . 'assets/js/obfuscation.admin.js', array('jquery'), Settings::instance()->version, true);
+ }
+
+ public function init() {
+ register_setting('obfuscation', 'obfuscation_options'); //, array($this, 'validate_options'));
+
+ // Message Settings
+
+ add_settings_section('obfuscation_section_obfuscation', __('Obfuscation', 'obfuscation'), array($this, 'section'), 'obfuscation');
+
+ add_settings_field('obfuscation_email_enabled', __('Email Obfuscation Enabled', 'obfuscation'), array($this, 'field_checkbox'), 'obfuscation', 'obfuscation_section_obfuscation', array(
+ 'id' => 'obfuscation_settings_email_enabled',
+ 'default' => Settings::instance()->defaults['email_enabled'],
+ ));
+ }
+
+ public function page() {
+ if (!current_user_can('manage_options')) return;
+
+ // Check if the user has submitted the settings
+ if (isset($_GET['settings-updated'])) {
+ // Show update message
+ //add_settings_error('obfuscation_messages', 'obfuscation_message', __('Settings Saved', 'obfuscation'), 'updated');
+ }
+
+ // Show error/update messages
+ settings_errors('obfuscation_messages');
+?>
+
+
+
+
+Description
+ }
+
+ // Fields
+
+ public function field_text($args) {
+ $value = '';
+ $options = get_option('obfuscation_options');
+ if (isset($args['default'])) $value = $args['default'];
+ if (isset($args['id']) && !empty($args['id']) && isset($options[$args['id']]) && !empty($options[$args['id']])) $value = $options[$args['id']];
+
+ $type = 'text';
+ if (isset($args['type']) && !empty($args['type'])) {
+ $type = $args['type'];
+ }
+
+ echo '';
+
+ if (isset($args['description']) && !empty($args['description'])) {
+ echo '';
+ _e($args['description'], 'obfuscation');
+ echo '
';
+ }
+ }
+
+ public function field_checkbox($args) {
+ $value = false;
+ $options = get_option('obfuscation_options');
+ if (isset($args['default'])) $value = $args['default'];
+ if (isset($args['id']) && !empty($args['id']) && isset($options[$args['id']]) && !empty($options[$args['id']])) $value = $options[$args['id']];
+
+ echo '';
+
+ if (isset($args['description']) && !empty($args['description'])) {
+ echo '';
+ esc_html_e($args['description'], 'obfuscation');
+ echo '
';
+ }
+ }
+
+ public function validate_options($fields) {
+ $valid_fields = array();
+
+ foreach ($fields as $id => $field) {
+ if (!isset($input[$id])) continue;
+ }
+
+ return apply_filters('validate_options', $valid_fields, $fields);
+ }
+
+}
+
+SettingsPage::instance();
diff --git a/ogre-obfuscation.php b/ogre-obfuscation.php
new file mode 100644
index 0000000..3086b79
--- /dev/null
+++ b/ogre-obfuscation.php
@@ -0,0 +1,56 @@
+add_files([
+ 'inc/settings.php',
+ 'inc/global.php', // Global Functions
+ 'inc/filter.php', // Content and navigation filters
+ ]);
+ }
+
+ protected function enable():void {
+ parent::enable();
+ add_filter('plugin_action_links_' . self::get_basename(), [$this, 'action_links']);
+ }
+
+ protected function disable():void {
+ parent::disable();
+ remove_filter('plugin_action_links_' . self::get_basename(), [$this, 'action_links']);
+ }
+
+ public function action_links($links) {
+ if (current_user_can('manage_options')) {
+ array_unshift($links, sprintf(
+ '%s',
+ esc_url(admin_url('options-general.php?page=obfuscation_settings')),
+ esc_html(self::__('Settings'))
+ ));
+ }
+ return $links;
+ }
+
+}
+
+Obfuscation::instance();
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..c6933c2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,25 @@
+{
+ "$schema": "https://getcomposer.org/schema.json",
+ "name": "cleverogre/ogre-obfuscation",
+ "version": "0.2.0",
+ "title": "Ogre Obfuscation",
+ "description": "Ogre Obfuscation is a plugin developed by CleverOgre in Pensacola, Florida.",
+ "author": "CleverOgre",
+ "license": "GPL-3.0+",
+ "keywords": [
+ "WordPress",
+ "Plugin",
+ "Obfuscation"
+ ],
+ "homepage": "https://cleverogre.com",
+ "engines": {
+ "node": ">=21.1.0",
+ "npm": ">=10.2.3"
+ },
+ "devDependencies": {
+ "gulp": "^5.0.0",
+ "gulp-clean": "^0.4.0",
+ "gulp-cli": "^2.3.0",
+ "gulp-zip": "^6.1.0"
+ }
+}
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..f464803
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,40 @@
+=== Ogre Obfuscation ===
+Contributors: ogrecooper, cleverogre
+Tested up to: 6.8
+Requires at least: 5.0
+Requires PHP: 8.0
+Version: 0.2.0
+License: GPLv3 or later
+License URI: https://www.gnu.org/licenses/gpl-3.0.html
+Copyright: CleverOgre
+Donate link: https://cleverogre.com/
+Tags: wordpress, plugin, obfuscation, email, phone
+
+Ogre Obfuscation is a plugin developed by CleverOgre in Pensacola, Florida.
+
+== Description ==
+
+TODO
+
+== Installation ==
+
+The required libraries can be installed using the following command:
+composer install
+
+You can install the optional development tools using the following command:
+npm install
+
+== FAQ ==
+
+= What is this plugin? =
+
+If you don't know what plugin you have downloaded, please contact [CleverOgre](team@cleverogre.com) for more information. This plugin is only developed for a small, private audience.
+
+== Changelog ==
+
+= 0.2.0 - 2025-07-22 =
+* DEV: Implemented `cleverogre/plugin-framework`
+* DEV: Code rewritten for PHP 8.0+
+
+= 0.1.0 =
+* Initial release