| 1 |
<?php |
| 2 |
/** |
| 3 |
* Contact Forms - Theme Helper |
| 4 |
* |
| 5 |
* A developer tool to detect CSS conflicts between themes and Contact Forms styling. |
| 6 |
* Scans loaded stylesheets for rules that may override Contact Forms classes. |
| 7 |
* |
| 8 |
* @package ContactForms |
| 9 |
* @since 2.0.0-beta.6 |
| 10 |
*/ |
| 11 |
|
| 12 |
if (!defined('ABSPATH')) { |
| 13 |
exit; |
| 14 |
} |
| 15 |
|
| 16 |
/** |
| 17 |
* Get list of Contact Forms CSS classes to monitor for conflicts |
| 18 |
*/ |
| 19 |
function accua_forms_get_monitored_css_classes() { |
| 20 |
return array( |
| 21 |
// Error handling |
| 22 |
'pfbc-error', |
| 23 |
'pfbc-error-heading', |
| 24 |
'pfbc-error-list', |
| 25 |
'pfbc-inline-error', |
| 26 |
'pfbc-error-message', |
| 27 |
|
| 28 |
// Field structure |
| 29 |
'pfbc-fieldwrap', |
| 30 |
'pfbc-invalid', |
| 31 |
'pfbc-element-has-error', |
| 32 |
'pfbc-element', |
| 33 |
'pfbc-elementbottom', |
| 34 |
'pfbc-label', |
| 35 |
'pfbc-help', |
| 36 |
'pfbc-description', |
| 37 |
'pfbc-required', |
| 38 |
|
| 39 |
// Form inputs |
| 40 |
'pfbc-textbox', |
| 41 |
'pfbc-textarea', |
| 42 |
'pfbc-select', |
| 43 |
'pfbc-checkbox', |
| 44 |
'pfbc-buttons', |
| 45 |
|
| 46 |
// Color picker |
| 47 |
'pfbc-color-wrapper', |
| 48 |
'pfbc-color-input', |
| 49 |
'pfbc-color-hex', |
| 50 |
|
| 51 |
// Inline labels |
| 52 |
'pfbc-inline-label-wrapper', |
| 53 |
'pfbc-floating-label', |
| 54 |
|
| 55 |
// Form layouts |
| 56 |
'accua-form', |
| 57 |
'accua-form-view-standard', |
| 58 |
'accua-form-view-sidebyside', |
| 59 |
'accua-form-view-inlinelabel', |
| 60 |
|
| 61 |
// Misc |
| 62 |
'accua_form_api_throbbler', |
| 63 |
'accua_forms_recaptcha2_container', |
| 64 |
); |
| 65 |
} |
| 66 |
|
| 67 |
/** |
| 68 |
* Render Theme Helper admin page |
| 69 |
*/ |
| 70 |
function accua_forms_theme_helper_page() { |
| 71 |
if (!current_user_can('manage_options')) { |
| 72 |
wp_die( esc_html__('You do not have sufficient permissions to access this page.', 'contact-forms') ); |
| 73 |
} |
| 74 |
?> |
| 75 |
<div id="accua_forms_theme_helper_page" class="accua_forms_admin_page wrap"> |
| 76 |
<h1><?php esc_html_e('Contact Forms - Theme Helper', 'contact-forms'); ?></h1> |
| 77 |
<?php accua_forms_theme_helper_content(); ?> |
| 78 |
|
| 79 |
<span id="accua-forms-version"><?php |
| 80 |
$plugin_data = get_plugin_data( ACCUA_FORMS_FILE ); |
| 81 |
echo esc_html( $plugin_data['Name'] ); ?> - <a href="https://www.cimatti.it/en/wordpress-plugins/contact-forms/"><?php |
| 82 |
esc_html_e('Version', 'contact-forms'); echo ' ' . esc_html( $plugin_data['Version'] ); ?></a></span> |
| 83 |
</div> |
| 84 |
<?php |
| 85 |
} |
| 86 |
|
| 87 |
/** |
| 88 |
* Render Theme Helper content (shared between standalone page and settings tab) |
| 89 |
*/ |
| 90 |
function accua_forms_theme_helper_content() { |
| 91 |
?> |
| 92 |
|
| 93 |
<p class="description"> |
| 94 |
<?php esc_html_e('This tool scans CSS stylesheets to detect rules that may conflict with Contact Forms styling. It helps identify theme customizations that could cause layout issues with form error messages, field spacing, and other elements.', 'contact-forms'); ?> |
| 95 |
</p> |
| 96 |
|
| 97 |
<div class="postbox" id="theme-helper-controls" style="margin: 20px 0;"> |
| 98 |
<div class="postbox-header"><h2><?php esc_html_e('Scan Options', 'contact-forms'); ?></h2></div> |
| 99 |
<div class="inside"> |
| 100 |
|
| 101 |
<div style="margin-bottom: 15px;"> |
| 102 |
<button type="button" id="scan-current-page" class="button button-primary"> |
| 103 |
<?php esc_html_e('Scan Admin Stylesheets', 'contact-forms'); ?> |
| 104 |
</button> |
| 105 |
<span class="description" style="margin-left: 10px;"> |
| 106 |
<?php esc_html_e('Instant scan of all CSS loaded on this page', 'contact-forms'); ?> |
| 107 |
</span> |
| 108 |
</div> |
| 109 |
|
| 110 |
<div style="margin-bottom: 15px;"> |
| 111 |
<label for="scan-url" style="display: block; margin-bottom: 5px; font-weight: 600;"> |
| 112 |
<?php esc_html_e('Scan Frontend URL:', 'contact-forms'); ?> |
| 113 |
</label> |
| 114 |
<input type="url" id="scan-url" style="width: 400px; max-width: 100%;" |
| 115 |
placeholder="<?php echo esc_attr(home_url('/your-form-page/')); ?>" /> |
| 116 |
<button type="button" id="scan-url-button" class="button"> |
| 117 |
<?php esc_html_e('Scan URL', 'contact-forms'); ?> |
| 118 |
</button> |
| 119 |
<p class="description"> |
| 120 |
<?php esc_html_e('Enter a URL of a page containing a Contact Form to scan its CSS for conflicts.', 'contact-forms'); ?> |
| 121 |
</p> |
| 122 |
</div> |
| 123 |
</div></div> |
| 124 |
|
| 125 |
<div id="theme-helper-status" style="display: none; margin: 20px 0; padding: 15px; border-left: 4px solid #0073aa; background: #f0f6fc;"> |
| 126 |
<span id="status-message"><?php esc_html_e('Ready to scan', 'contact-forms'); ?></span> |
| 127 |
</div> |
| 128 |
|
| 129 |
<div id="theme-helper-results" style="display: none; margin: 20px 0;"> |
| 130 |
<div class="postbox"> |
| 131 |
<div class="postbox-header"><h2><?php esc_html_e('Scan Results', 'contact-forms'); ?></h2></div> |
| 132 |
<div class="inside"> |
| 133 |
<div id="results-summary" style="margin-bottom: 20px; padding: 15px; background: #f6f7f7; border: 1px solid #e0e0e0;"> |
| 134 |
<!-- Summary will be inserted here --> |
| 135 |
</div> |
| 136 |
|
| 137 |
<table class="wp-list-table widefat fixed striped" id="conflicts-table"> |
| 138 |
<thead> |
| 139 |
<tr> |
| 140 |
<th scope="col" style="width: 25%;"><?php esc_html_e('Stylesheet', 'contact-forms'); ?></th> |
| 141 |
<th scope="col" style="width: 10%;"><?php esc_html_e('Line', 'contact-forms'); ?></th> |
| 142 |
<th scope="col" style="width: 25%;"><?php esc_html_e('Selector', 'contact-forms'); ?></th> |
| 143 |
<th scope="col" style="width: 40%;"><?php esc_html_e('Conflicting Properties', 'contact-forms'); ?></th> |
| 144 |
</tr> |
| 145 |
</thead> |
| 146 |
<tbody id="conflicts-tbody"> |
| 147 |
<!-- Results will be inserted here --> |
| 148 |
</tbody> |
| 149 |
</table> |
| 150 |
</div></div> |
| 151 |
</div> |
| 152 |
|
| 153 |
<div class="postbox" id="theme-helper-info" style="margin-top: 20px;"> |
| 154 |
<div class="postbox-header"><h2><?php esc_html_e('How to Fix Conflicts', 'contact-forms'); ?></h2></div> |
| 155 |
<div class="inside"> |
| 156 |
<ol> |
| 157 |
<li> |
| 158 |
<strong><?php esc_html_e('Identify the source:', 'contact-forms'); ?></strong> |
| 159 |
<?php esc_html_e('The "Stylesheet" column shows which CSS file contains the conflicting rule.', 'contact-forms'); ?> |
| 160 |
</li> |
| 161 |
<li> |
| 162 |
<strong><?php esc_html_e('Find the exact line:', 'contact-forms'); ?></strong> |
| 163 |
<?php esc_html_e('The "Line" column shows approximately where in the file the rule is located.', 'contact-forms'); ?> |
| 164 |
</li> |
| 165 |
<li> |
| 166 |
<strong><?php esc_html_e('Modify or remove the rule:', 'contact-forms'); ?></strong> |
| 167 |
<?php esc_html_e('Edit your theme\'s CSS file (usually style.css or a custom CSS file) to remove or adjust the conflicting properties.', 'contact-forms'); ?> |
| 168 |
</li> |
| 169 |
<li> |
| 170 |
<strong><?php esc_html_e('Use child theme:', 'contact-forms'); ?></strong> |
| 171 |
<?php esc_html_e('If the conflict is in a parent theme, create a child theme and override the conflicting styles there.', 'contact-forms'); ?> |
| 172 |
</li> |
| 173 |
</ol> |
| 174 |
|
| 175 |
<h4><?php esc_html_e('Common Conflicts', 'contact-forms'); ?></h4> |
| 176 |
<ul> |
| 177 |
<li><code>.pfbc-invalid</code>, <code>.pfbc-fieldwrap</code> - <?php esc_html_e('Error message spacing', 'contact-forms'); ?></li> |
| 178 |
<li><code>.pfbc-element</code> - <?php esc_html_e('Field element layout', 'contact-forms'); ?></li> |
| 179 |
<li><code>.pfbc-inline-error</code> - <?php esc_html_e('Inline error positioning', 'contact-forms'); ?></li> |
| 180 |
<li><code>.accua-form-view-*</code> - <?php esc_html_e('Form layout styles', 'contact-forms'); ?></li> |
| 181 |
</ul> |
| 182 |
</div></div> |
| 183 |
<?php |
| 184 |
} |
| 185 |
|
| 186 |
/** |
| 187 |
* AJAX handler for fetching CSS from a URL |
| 188 |
*/ |
| 189 |
add_action('wp_ajax_accua_forms_fetch_url_css', 'accua_forms_fetch_url_css'); |
| 190 |
function accua_forms_fetch_url_css() { |
| 191 |
check_ajax_referer('accua_theme_helper', 'nonce'); |
| 192 |
|
| 193 |
if (!current_user_can('manage_options')) { |
| 194 |
wp_send_json_error(array('message' => __('Permission denied', 'contact-forms'))); |
| 195 |
} |
| 196 |
|
| 197 |
$url = isset($_POST['url']) ? esc_url_raw(wp_unslash($_POST['url'])) : ''; |
| 198 |
|
| 199 |
if (empty($url)) { |
| 200 |
wp_send_json_error(array('message' => __('No URL provided', 'contact-forms'))); |
| 201 |
} |
| 202 |
|
| 203 |
// Fetch the HTML page |
| 204 |
$response = wp_remote_get($url, array( |
| 205 |
'timeout' => 30, |
| 206 |
'sslverify' => false, |
| 207 |
)); |
| 208 |
|
| 209 |
if (is_wp_error($response)) { |
| 210 |
wp_send_json_error(array('message' => $response->get_error_message())); |
| 211 |
} |
| 212 |
|
| 213 |
$html = wp_remote_retrieve_body($response); |
| 214 |
|
| 215 |
if (empty($html)) { |
| 216 |
wp_send_json_error(array('message' => __('Empty response from URL', 'contact-forms'))); |
| 217 |
} |
| 218 |
|
| 219 |
// Parse HTML to find stylesheet URLs |
| 220 |
$stylesheets = array(); |
| 221 |
|
| 222 |
// Match <link rel="stylesheet" href="..."> |
| 223 |
preg_match_all('/<link[^>]+rel=["\']stylesheet["\'][^>]+href=["\']([^"\']+)["\'][^>]*>/i', $html, $matches); |
| 224 |
if (!empty($matches[1])) { |
| 225 |
$stylesheets = array_merge($stylesheets, $matches[1]); |
| 226 |
} |
| 227 |
|
| 228 |
// Also match href before rel |
| 229 |
preg_match_all('/<link[^>]+href=["\']([^"\']+)["\'][^>]+rel=["\']stylesheet["\'][^>]*>/i', $html, $matches); |
| 230 |
if (!empty($matches[1])) { |
| 231 |
$stylesheets = array_merge($stylesheets, $matches[1]); |
| 232 |
} |
| 233 |
|
| 234 |
$stylesheets = array_unique($stylesheets); |
| 235 |
|
| 236 |
// Fetch each stylesheet and combine content |
| 237 |
$css_data = array(); |
| 238 |
$base_url = trailingslashit(dirname($url)); |
| 239 |
$site_url = trailingslashit(wp_parse_url($url, PHP_URL_SCHEME) . '://' . wp_parse_url($url, PHP_URL_HOST)); |
| 240 |
|
| 241 |
foreach ($stylesheets as $stylesheet_url) { |
| 242 |
// Handle relative URLs |
| 243 |
if (strpos($stylesheet_url, '//') === 0) { |
| 244 |
$stylesheet_url = wp_parse_url($url, PHP_URL_SCHEME) . ':' . $stylesheet_url; |
| 245 |
} elseif (strpos($stylesheet_url, '/') === 0) { |
| 246 |
$stylesheet_url = rtrim($site_url, '/') . $stylesheet_url; |
| 247 |
} elseif (strpos($stylesheet_url, 'http') !== 0) { |
| 248 |
$stylesheet_url = $base_url . $stylesheet_url; |
| 249 |
} |
| 250 |
|
| 251 |
// Skip external CDN stylesheets (Google Fonts, etc.) |
| 252 |
if (strpos($stylesheet_url, 'fonts.googleapis.com') !== false || |
| 253 |
strpos($stylesheet_url, 'cdnjs.cloudflare.com') !== false || |
| 254 |
strpos($stylesheet_url, 'cdn.jsdelivr.net') !== false) { |
| 255 |
continue; |
| 256 |
} |
| 257 |
|
| 258 |
$css_response = wp_remote_get($stylesheet_url, array( |
| 259 |
'timeout' => 15, |
| 260 |
'sslverify' => false, |
| 261 |
)); |
| 262 |
|
| 263 |
if (!is_wp_error($css_response)) { |
| 264 |
$css_content = wp_remote_retrieve_body($css_response); |
| 265 |
if (!empty($css_content)) { |
| 266 |
$css_data[] = array( |
| 267 |
'url' => $stylesheet_url, |
| 268 |
'content' => $css_content, |
| 269 |
); |
| 270 |
} |
| 271 |
} |
| 272 |
} |
| 273 |
|
| 274 |
// Also look for inline styles |
| 275 |
preg_match_all('/<style[^>]*>(.*?)<\/style>/is', $html, $inline_matches); |
| 276 |
if (!empty($inline_matches[1])) { |
| 277 |
foreach ($inline_matches[1] as $index => $inline_css) { |
| 278 |
if (!empty(trim($inline_css))) { |
| 279 |
$css_data[] = array( |
| 280 |
'url' => $url . ' (inline style #' . ($index + 1) . ')', |
| 281 |
'content' => $inline_css, |
| 282 |
); |
| 283 |
} |
| 284 |
} |
| 285 |
} |
| 286 |
|
| 287 |
// Detect Contact Forms version from remote site |
| 288 |
$remote_cf_version = null; |
| 289 |
|
| 290 |
// Check for Contact Forms CSS version in URL params (e.g., frontend.css?ver=69) |
| 291 |
foreach ($stylesheets as $stylesheet_url) { |
| 292 |
// Match both old and new CSS paths |
| 293 |
if (strpos($stylesheet_url, 'contact-forms/assets/css/frontend.css') !== false || |
| 294 |
strpos($stylesheet_url, 'contact-forms/assets/css/accua-form-api.css') !== false || |
| 295 |
strpos($stylesheet_url, 'contact-forms/accua-form-api.css') !== false) { // Old path |
| 296 |
// Extract version from query string |
| 297 |
$parsed = wp_parse_url($stylesheet_url); |
| 298 |
if (!empty($parsed['query'])) { |
| 299 |
parse_str($parsed['query'], $query_params); |
| 300 |
if (!empty($query_params['ver'])) { |
| 301 |
$remote_cf_version = $query_params['ver']; |
| 302 |
break; |
| 303 |
} |
| 304 |
} |
| 305 |
} |
| 306 |
} |
| 307 |
|
| 308 |
// Also try to find version in HTML comments or meta tags |
| 309 |
if (!$remote_cf_version) { |
| 310 |
// Look for Contact Forms generator meta or comment |
| 311 |
if (preg_match('/Contact Forms[^0-9]*([0-9]+\.[0-9]+\.[0-9]+[^"\s]*)/i', $html, $version_match)) { |
| 312 |
$remote_cf_version = $version_match[1]; |
| 313 |
} |
| 314 |
} |
| 315 |
|
| 316 |
// Get current plugin version |
| 317 |
$plugin_data = get_plugin_data(ACCUA_FORMS_FILE); |
| 318 |
$current_version = $plugin_data['Version']; |
| 319 |
|
| 320 |
wp_send_json_success(array( |
| 321 |
'stylesheets' => $css_data, |
| 322 |
'count' => count($css_data), |
| 323 |
'remoteContactFormsVersion' => $remote_cf_version, |
| 324 |
'remoteCssVersion' => $remote_cf_version, // CSS version number |
| 325 |
'currentVersion' => $current_version, |
| 326 |
'currentCssVersion' => ACCUA_FORMS_CSS_VERSION, |
| 327 |
)); |
| 328 |
} |
| 329 |
|