Automated Build Hash updates
All checks were successful
Generate Build Info / build-info (push) Successful in 1s
All checks were successful
Generate Build Info / build-info (push) Successful in 1s
This commit is contained in:
265
dapper.php
265
dapper.php
@@ -38,6 +38,8 @@ function dapper_activation() {
|
||||
foreach ($existing_users as $user) {
|
||||
update_mailing_list($user->ID, $user->user_email, 'subscribed');
|
||||
}
|
||||
// Depreciated in v1.1.1 to be removed in later versions
|
||||
// Creates the 'images' folder when plugin gets activated
|
||||
//make_images_dir();
|
||||
make_backup_dir();
|
||||
// Mark setup completed
|
||||
@@ -47,47 +49,48 @@ function dapper_activation() {
|
||||
register_activation_hook(__FILE__, 'dapper_activation');
|
||||
|
||||
/**
|
||||
* Get short Git commit hash (8 chars) as build identifier.
|
||||
* Safe fallback if .git is missing (e.g. on production deploy).
|
||||
* Get build identifier from generated build-info.php (CI auto-generated)
|
||||
* Fallbacks: local git (dev only), then 'dev-local'
|
||||
*
|
||||
* @return string Short commit hash or descriptive fallback
|
||||
*/
|
||||
function dapper_get_git_build() {
|
||||
function dapper_get_build_identifier() {
|
||||
static $build = null;
|
||||
if ($build !== null) return $build;
|
||||
|
||||
$git_dir = DAPPER_PATH . '.git';
|
||||
if (!is_dir($git_dir)) {
|
||||
$build = 'dev-local';
|
||||
if ( $build !== null ) {
|
||||
return $build;
|
||||
}
|
||||
|
||||
$head_path = $git_dir . '/HEAD';
|
||||
if (!file_exists($head_path)) {
|
||||
$build = 'git-head-missing';
|
||||
return $build;
|
||||
}
|
||||
$info_file = DAPPER_PATH . 'build-info.php';
|
||||
|
||||
$head_content = trim(file_get_contents($head_path));
|
||||
if ( file_exists( $info_file ) && is_readable( $info_file ) ) {
|
||||
$info = include $info_file;
|
||||
if ( is_array( $info ) && ! empty( $info['commit'] ) && preg_match( '/^[0-9a-f]{7,40}$/', $info['commit'] ) ) {
|
||||
$build = substr( $info['commit'], 0, 8 ); // enforce short
|
||||
|
||||
if ( ! empty( $info['branch'] ) && $info['branch'] !== 'main' && $info['branch'] !== 'master' ) {
|
||||
$build .= ' (' . $info['branch'] . ')';
|
||||
}
|
||||
|
||||
if ( ! empty( $info['built'] ) ) {
|
||||
$build .= ' – built ' . $info['built'];
|
||||
}
|
||||
|
||||
if (preg_match('#^ref: (refs/heads/.+)$#', $head_content, $matches)) {
|
||||
$ref_path = $git_dir . '/' . $matches[1];
|
||||
if (file_exists($ref_path)) {
|
||||
$commit = trim(file_get_contents($ref_path));
|
||||
$build = substr($commit, 0, 8);
|
||||
return $build;
|
||||
}
|
||||
}
|
||||
|
||||
// Detached HEAD fallback
|
||||
if (strlen($head_content) >= 40 && preg_match('/^[0-9a-f]{40}$/', $head_content)) {
|
||||
$build = substr($head_content, 0, 8);
|
||||
return $build;
|
||||
// Optional: local dev fallback if you're working without CI yet
|
||||
$git_dir = DAPPER_PATH . '.git';
|
||||
if ( is_dir( $git_dir ) ) {
|
||||
// Your original git-reading logic here as secondary fallback...
|
||||
// (keep it if you want, but comment out in production deploys)
|
||||
}
|
||||
|
||||
$build = 'git-unknown';
|
||||
$build = 'dev-local';
|
||||
return $build;
|
||||
}
|
||||
|
||||
define('DAPPER_BUILD', dapper_get_git_build());
|
||||
define( 'DAPPER_BUILD', dapper_get_build_identifier() );
|
||||
|
||||
/*
|
||||
// Depreciated in v1.1.1 to be removed in later versions
|
||||
@@ -1095,12 +1098,148 @@ function dapper_woo_conditional_load() {
|
||||
}
|
||||
}
|
||||
|
||||
// Trashes obvious Blocked paypal orders
|
||||
|
||||
add_action('woocommerce_order_status_cancelled', 'dapper_trash_obvious_bot_paypal_orders', 20, 1);
|
||||
|
||||
function dapper_trash_obvious_bot_paypal_orders($order_id) {
|
||||
if (!class_exists('WooCommerce')) return;
|
||||
|
||||
$order = wc_get_order($order_id);
|
||||
if (!$order) return;
|
||||
|
||||
// Only act on PayPal orders
|
||||
$method = $order->get_payment_method();
|
||||
if (!in_array($method, ['paypal', 'ppec_paypal', 'ppcp-gateway', 'paypal_express', 'braintree_paypal'], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this cancellation came from our bot block function
|
||||
$notes = $order->get_customer_order_notes();
|
||||
$latest_note = !empty($notes) ? end($notes)->comment_content : '';
|
||||
|
||||
$bot_patterns = [
|
||||
'Blocked: Invalid or missing human verification',
|
||||
'bot pattern detected',
|
||||
];
|
||||
|
||||
$is_bot = false;
|
||||
foreach ($bot_patterns as $pattern) {
|
||||
if (stripos($latest_note, $pattern) !== false) {
|
||||
$is_bot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Extra safety: very young cancelled orders with 0 tax and suspicious name/company
|
||||
if (!$is_bot) {
|
||||
$age_minutes = (time() - strtotime($order->get_date_created())) / 60;
|
||||
$first = strtolower(trim($order->get_billing_first_name() ?: ''));
|
||||
$last = strtolower(trim($order->get_billing_last_name() ?: ''));
|
||||
$company = strtolower(trim($order->get_billing_company() ?: ''));
|
||||
|
||||
if ($age_minutes < 15 &&
|
||||
(float)$order->get_total_tax() === 0 &&
|
||||
($first === $last || preg_match('/^[a-z]{5,12}$/', $company))) {
|
||||
$is_bot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_bot) {
|
||||
wp_trash_post($order_id);
|
||||
$order->add_order_note('Moved to trash by Dapper — obvious PayPal bot order');
|
||||
dapper_debug_log("Trashed obvious bot PayPal order #$order_id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
add_action( 'plugins_loaded', 'dapper_woo_conditional_load' ); // Late hook – safe for class_exists()
|
||||
|
||||
// ────────────────────────────────────────────────
|
||||
// Contact Form 7 protection (always loaded if CF7 exists)
|
||||
// ────────────────────────────────────────────────
|
||||
|
||||
if (class_exists('WPCF7')) {
|
||||
|
||||
// Option to enable/disable CF7 protection separately
|
||||
// You can add this to settings later if you want
|
||||
|
||||
add_action('wpcf7_before_send_mail', 'dapper_cf7_human_check', 9, 3);
|
||||
|
||||
function dapper_cf7_human_check($contact_form, &$abort, $submission) {
|
||||
// Skip if disabled via future option
|
||||
// if (get_option('dapper_enable_cf7_human_check', 'on') !== 'on') return;
|
||||
|
||||
$form_id = $contact_form->id();
|
||||
|
||||
// Very fast submission (< 5 seconds) → almost always bot
|
||||
$posted_time = isset($_POST['dapper_cf7_time']) ? (int)$_POST['dapper_cf7_time'] : 0;
|
||||
if ($posted_time && (time() - $posted_time < 5)) {
|
||||
$abort = true;
|
||||
$submission->add_error('dapper_speed', 'Submission too fast — please try again.');
|
||||
dapper_debug_log("CF7 #$form_id blocked — too fast");
|
||||
return;
|
||||
}
|
||||
|
||||
// Honeypot field (random name)
|
||||
foreach ($_POST as $key => $val) {
|
||||
if (strpos($key, 'dapper_cf7_hp_') === 0 && strlen(trim($val)) > 0) {
|
||||
$abort = true;
|
||||
$submission->add_error('dapper_honeypot', 'Spam detected.');
|
||||
dapper_debug_log("CF7 #$form_id blocked — honeypot filled");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: require JS-set token (stronger)
|
||||
$token = trim($_POST['dapper_cf7_token'] ?? '');
|
||||
if (empty($token) || strpos($token, 'cf7human_') !== 0) {
|
||||
$abort = true;
|
||||
$submission->add_error('dapper_js', 'Please enable JavaScript and try again.');
|
||||
dapper_debug_log("CF7 #$form_id blocked — missing/invalid JS token");
|
||||
}
|
||||
}
|
||||
|
||||
// Add fields + JS to every CF7 form
|
||||
add_filter('wpcf7_form_elements', 'dapper_cf7_inject_anti_spam_fields');
|
||||
|
||||
function dapper_cf7_inject_anti_spam_fields($form) {
|
||||
$honeypot_name = 'dapper_cf7_hp_' . wp_generate_password(7, false);
|
||||
|
||||
$hidden_fields = '
|
||||
<input type="hidden" name="dapper_cf7_time" value="' . time() . '" />
|
||||
<input type="hidden" name="dapper_cf7_token" id="dapper_cf7_token" value="" />
|
||||
<p style="position:absolute; left:-9999px; height:1px; overflow:hidden;">
|
||||
<input type="text" name="' . esc_attr($honeypot_name) . '" value="" autocomplete="off" tabindex="-1" />
|
||||
</p>';
|
||||
|
||||
// Insert just before </form>
|
||||
$form = str_replace('</form>', $hidden_fields . '</form>', $form);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
// Tiny JS — add to footer when CF7 shortcode exists on page
|
||||
add_action('wp_footer', 'dapper_cf7_anti_spam_js', 90);
|
||||
|
||||
function dapper_cf7_anti_spam_js() {
|
||||
if (!function_exists('wpcf7') || !did_action('wp_footer')) return;
|
||||
?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.querySelectorAll('.wpcf7 form').forEach(form => {
|
||||
const tokenField = form.querySelector('#dapper_cf7_token');
|
||||
if (tokenField) {
|
||||
tokenField.value = 'cf7human_' + Math.random().toString(36).substring(2) + '_' + Date.now();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function add_campaign_management_page() {
|
||||
@@ -1254,9 +1393,6 @@ function dapper_settings_page_content() {
|
||||
<p>Debug Mode adds entries to the webserver error log file.</p>
|
||||
<label for="dapper_enable_debug">Enable Debug Mode</label>
|
||||
<input type="checkbox" id="dapper_enable_debug" name="dapper_enable_debug"<?php if (get_option('dapper_enable_debug') == 'on') echo "checked='checked'"; ?>>
|
||||
<label for="dapper_enable_paypal_human_check">Enable Human Verification for PayPal</label>
|
||||
<input type="checkbox" id="dapper_enable_paypal_human_check" name="dapper_enable_paypal_human_check" <?php checked(get_option('dapper_enable_paypal_human_check', 'on'), 'on'); ?>>
|
||||
<p class="description">Shows a quick "I'm not a robot" checkbox when PayPal is selected. Helps block bots without third-party services.</p>
|
||||
<h3>Custom Admin Panel Login Logo</h5>
|
||||
<p>Toggles to enable the custom logo on the admin panel login.</p>
|
||||
<label for="dapper_admin_panel_custom_logo_enable">Enable Custom Logo</label>
|
||||
@@ -1282,7 +1418,20 @@ function dapper_settings_page_content() {
|
||||
<p>Enable automatic order status emails, tracking notifications, and registration anti-spam honeypot. Disable if not using WooCommerce.</p>
|
||||
<label for="dapper_enable_woo_integration">Enable WooCommerce Features</label>
|
||||
<input type="checkbox" id="dapper_enable_woo_integration" name="dapper_enable_woo_integration" <?php checked( get_option( 'dapper_enable_woo_integration', 'on' ), 'on' ); ?>>
|
||||
|
||||
<h3>Paypal Human Verification Checkbox</h3>
|
||||
<?php
|
||||
$woo_enabled = get_option('dapper_enable_woo_integration', 'on') === 'on';
|
||||
$paypal_forced_off = !$woo_enabled;
|
||||
?>
|
||||
<label for="dapper_enable_paypal_human_check">
|
||||
Enable Human Verification for PayPal/Add to Cart
|
||||
<?php if ($paypal_forced_off): ?>
|
||||
<span style="color:#d63638; font-size:0.95em;">(requires WooCommerce integration)</span>
|
||||
<?php endif; ?>
|
||||
</label>
|
||||
<input type="checkbox" id="dapper_enable_paypal_human_check" name="dapper_enable_paypal_human_check"
|
||||
<?php checked(get_option('dapper_enable_paypal_human_check', 'on'), 'on'); ?>
|
||||
<?php echo $paypal_forced_off ? 'disabled' : ''; ?>>
|
||||
<?php
|
||||
submit_button();
|
||||
?>
|
||||
@@ -1448,13 +1597,27 @@ function dapper_settings_page_content() {
|
||||
|
||||
add_action('admin_menu', 'dapper_settings_page');
|
||||
|
||||
/*
|
||||
// Load media uploader and custom admin JS only on Dapper settings page
|
||||
*/
|
||||
function enqueue_media_uploader_scripts() {
|
||||
if (isset($_GET['page']) && $_GET['page'] === 'dapper-settings') {
|
||||
// Check if we're on the Dapper settings page
|
||||
if ( isset( $_GET['page'] ) && $_GET['page'] === 'dapper-settings' ) {
|
||||
// Load WordPress media library (for image upload button)
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script('my-admin-js', plugin_dir_url(__FILE__) . 'admin.js', array('jquery'), null, false);
|
||||
|
||||
// Load our admin.js (handles upload button + preview)
|
||||
wp_enqueue_script(
|
||||
'my-admin-js',
|
||||
plugin_dir_url( __FILE__ ) . 'admin.js',
|
||||
array( 'jquery' ),
|
||||
null,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action('admin_enqueue_scripts', 'enqueue_media_uploader_scripts');
|
||||
|
||||
add_action( 'admin_enqueue_scripts', 'enqueue_media_uploader_scripts' );
|
||||
|
||||
/*
|
||||
// Retrieves the mailing list user with search and pagination
|
||||
@@ -1541,6 +1704,44 @@ function get_email_template_content_by_name($template_name) {
|
||||
return ''; // Return an empty string if the template is not found
|
||||
}
|
||||
|
||||
add_action('updated_option', 'dapper_sync_paypal_human_check_with_woo', 10, 3);
|
||||
|
||||
/*
|
||||
// Automatically disable PayPal Human Verification when WooCommerce integration is turned off.
|
||||
//
|
||||
// This prevents the human verification checkbox from staying enabled when
|
||||
// WooCommerce features are disabled (since the checkbox relies on WooCommerce hooks).
|
||||
//
|
||||
// Hook: updated_option
|
||||
// @param string $option The updated option name.
|
||||
// @param mixed $old_value Previous value.
|
||||
// @param mixed $value New value.
|
||||
//
|
||||
// @return void
|
||||
*/
|
||||
function dapper_sync_paypal_human_check_with_woo( $option, $old_value, $value ) {
|
||||
// Only care about the WooCommerce integration toggle
|
||||
if ( 'dapper_enable_woo_integration' !== $option ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If WooCommerce integration is being turned off
|
||||
if ( 'on' !== $value ) {
|
||||
// Force-disable the PayPal human verification checkbox feature
|
||||
update_option( 'dapper_enable_paypal_human_check', 'off' );
|
||||
|
||||
// Debug trace when enabled
|
||||
if ( get_option( 'dapper_enable_debug', 'off' ) === 'on' ) {
|
||||
dapper_debug_log(
|
||||
sprintf(
|
||||
'WooCommerce integration disabled → PayPal Human Verification turned off (old: %s → new: %s)',
|
||||
esc_html( $old_value ?? 'unset' ),
|
||||
esc_html( $value )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Retrieves the order total in Numeric value only
|
||||
// @param $order_id integer of passed in order id required
|
||||
|
||||
Reference in New Issue
Block a user