Automated Build Hash updates
All checks were successful
Generate Build Info / build-info (push) Successful in 1s

This commit is contained in:
carpentryplus25
2026-03-05 10:30:37 -05:00
parent 7de33d80f8
commit 9b408542af
2 changed files with 259 additions and 53 deletions

View File

@@ -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