Changeset 3633858
- Timestamp:
- 08/04/2026 07:08:25 AM (3 months ago)
- Location:
- wap-qa-auditor-for-elementor
- Files:
-
- 22 added
- 12 edited
-
tags/1.1.0 (added)
-
tags/1.1.0/assets (added)
-
tags/1.1.0/assets/admin.css (added)
-
tags/1.1.0/assets/admin.js (added)
-
tags/1.1.0/composer.json (added)
-
tags/1.1.0/languages (added)
-
tags/1.1.0/readme.txt (added)
-
tags/1.1.0/src (added)
-
tags/1.1.0/src/Admin (added)
-
tags/1.1.0/src/Admin/Dashboard.php (added)
-
tags/1.1.0/src/Admin/Results.php (added)
-
tags/1.1.0/src/Audit (added)
-
tags/1.1.0/src/Audit/Auditor.php (added)
-
tags/1.1.0/src/Audit/ElementorScanner.php (added)
-
tags/1.1.0/src/Core (added)
-
tags/1.1.0/src/Core/Activator.php (added)
-
tags/1.1.0/src/Core/Plugin.php (added)
-
tags/1.1.0/src/Services (added)
-
tags/1.1.0/src/Services/ExportService.php (added)
-
tags/1.1.0/src/Services/ScanStorage.php (added)
-
tags/1.1.0/uninstall.php (added)
-
tags/1.1.0/wap-qa-auditor-for-elementor.php (added)
-
trunk/assets/admin.css (modified) (2 diffs)
-
trunk/assets/admin.js (modified) (4 diffs)
-
trunk/readme.txt (modified) (5 diffs)
-
trunk/src/Admin/Dashboard.php (modified) (1 diff)
-
trunk/src/Admin/Results.php (modified) (7 diffs)
-
trunk/src/Audit/Auditor.php (modified) (3 diffs)
-
trunk/src/Audit/ElementorScanner.php (modified) (2 diffs)
-
trunk/src/Core/Plugin.php (modified) (2 diffs)
-
trunk/src/Services/ExportService.php (modified) (1 diff)
-
trunk/src/Services/ScanStorage.php (modified) (3 diffs)
-
trunk/uninstall.php (modified) (1 diff)
-
trunk/wap-qa-auditor-for-elementor.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
wap-qa-auditor-for-elementor/trunk/assets/admin.css
r3630418 r3633858 266 266 } 267 267 268 .wapqa-tooltip-note { 269 font-size: 11.5px; 270 line-height: 1.4; 271 color: var(--wapqa-text-muted); 272 font-style: italic; 273 } 274 268 275 .wapqa-tooltip-divider { 269 276 height: 1px; … … 824 831 } 825 832 826 .wapqa-actions-cell a { 833 .wapqa-actions-cell a, 834 .wapqa-actions-cell .wapqa-btn-ignore-toggle { 827 835 margin-right: 12px; 828 836 font-weight: 600; 829 837 white-space: nowrap; 838 } 839 840 .wapqa-btn-ignore-toggle { 841 background: none; 842 border: none; 843 padding: 0; 844 font-size: inherit; 845 font-family: inherit; 846 /* Matches wp-admin's default link color, since "View Page" and "Edit 847 with Elementor" are plain <a> tags styled entirely by wp-admin core. */ 848 color: #2271b1; 849 text-decoration: underline; 850 cursor: pointer; 851 } 852 853 .wapqa-btn-ignore-toggle:hover:not(:disabled) { 854 color: #135e96; 855 } 856 857 .wapqa-btn-ignore-toggle:disabled { 858 opacity: 0.5; 859 cursor: wait; 860 } 861 862 .wapqa-row-ignored { 863 opacity: 0.55; 864 } 865 866 .wapqa-badge-ignored { 867 background: var(--wapqa-none-bg); 868 color: var(--wapqa-text-muted); 830 869 } 831 870 -
wap-qa-auditor-for-elementor/trunk/assets/admin.js
r3630418 r3633858 29 29 initSelectedScanModal(); 30 30 initResultsFilters(); 31 initIgnoreToggles(); 31 32 } 32 33 … … 385 386 386 387 /** 387 * Loads a plugin page via AJAX and swaps it into the current document. 388 * Loads a plugin page via AJAX and swaps it into the current document, 389 * updating the browser URL and history. 388 390 * 389 391 * @param {string} page "dashboard" or "results". 390 392 */ 391 393 function navigateTo( page ) { 394 loadPageFragment( page, true ); 395 } 396 397 /** 398 * Fetches a plugin page fragment via AJAX and swaps it into the current 399 * document. Shared by link navigation (which updates browser history) 400 * and in-place refreshes like the ignore toggle (which should not add a 401 * new history entry, since the URL hasn't changed). 402 * 403 * @param {string} page "dashboard" or "results". 404 * @param {boolean} updateHistory Whether to push a new history entry and 405 * update the admin menu highlight. 406 * @return {Promise<void>} 407 */ 408 function loadPageFragment( page, updateHistory ) { 392 409 var root = document.getElementById( 'wapqa-page-root' ); 393 410 394 411 if ( ! root ) { 395 412 window.location.href = page === 'dashboard' ? config.dashboardUrl : config.resultsUrl; 396 return; 397 } 413 return Promise.resolve(); 414 } 415 416 // An in-place refresh (updateHistory false, e.g. after ignoring an 417 // issue) re-renders the same page the user is already looking at, so 418 // preserve which results groups they had expanded — the server only 419 // auto-opens critical groups by default, which would otherwise 420 // collapse any warning group the user had opened manually. 421 var openGroupKeys = updateHistory ? null : getOpenGroupKeys( root ); 398 422 399 423 root.style.opacity = '0.45'; 400 424 root.style.pointerEvents = 'none'; 401 425 402 ajaxPost( 'wapqa_load_page', { target: page } )426 return ajaxPost( 'wapqa_load_page', { target: page } ) 403 427 .then( function ( response ) { 404 428 if ( ! response || ! response.success ) { … … 416 440 } 417 441 442 if ( openGroupKeys ) { 443 restoreOpenGroups( newRoot, openGroupKeys ); 444 } 445 418 446 root.replaceWith( newRoot ); 419 document.title = response.data.title; 420 window.history.pushState( { wapqaPage: page }, '', response.data.url ); 421 updateAdminMenuHighlight( page ); 422 window.scrollTo( 0, 0 ); 447 448 if ( updateHistory ) { 449 document.title = response.data.title; 450 window.history.pushState( { wapqaPage: page }, '', response.data.url ); 451 updateAdminMenuHighlight( page ); 452 window.scrollTo( 0, 0 ); 453 } 454 423 455 initPage(); 424 456 } ) … … 429 461 430 462 /** 463 * Collects the issue-type keys of every currently expanded results 464 * group. 465 * 466 * @param {HTMLElement} root Current #wapqa-page-root element. 467 * @return {string[]} 468 */ 469 function getOpenGroupKeys( root ) { 470 var openKeys = []; 471 472 Array.prototype.forEach.call( root.querySelectorAll( 'details.wapqa-group[open]' ), function ( group ) { 473 var key = group.getAttribute( 'data-issue-type' ); 474 475 if ( key ) { 476 openKeys.push( key ); 477 } 478 } ); 479 480 return openKeys; 481 } 482 483 /** 484 * Re-opens groups matching the given issue-type keys in a freshly 485 * fetched fragment, before it replaces the current DOM. 486 * 487 * @param {HTMLElement} newRoot Freshly fetched #wapqa-page-root element. 488 * @param {string[]} openKeys Issue-type keys that were open before the refresh. 489 */ 490 function restoreOpenGroups( newRoot, openKeys ) { 491 Array.prototype.forEach.call( newRoot.querySelectorAll( 'details.wapqa-group' ), function ( group ) { 492 var key = group.getAttribute( 'data-issue-type' ); 493 494 if ( key && openKeys.indexOf( key ) !== -1 ) { 495 group.open = true; 496 } 497 } ); 498 } 499 500 /** 501 * Wires up the "Ignore" / "Unignore" buttons on the results page. On 502 * success, refreshes the results fragment in place (no new history 503 * entry) so the row's state, group counts and dashboard totals stay 504 * consistent without a full page reload. 505 */ 506 function initIgnoreToggles() { 507 var buttons = document.querySelectorAll( '.wapqa-btn-ignore-toggle' ); 508 509 Array.prototype.forEach.call( buttons, function ( button ) { 510 button.addEventListener( 'click', function () { 511 var issueKey = button.getAttribute( 'data-issue-key' ); 512 513 if ( ! issueKey || button.disabled ) { 514 return; 515 } 516 517 button.disabled = true; 518 519 ajaxPost( 'wapqa_toggle_ignore', { issue_key: issueKey } ) 520 .then( function ( response ) { 521 if ( ! response || ! response.success ) { 522 button.disabled = false; 523 return; 524 } 525 526 loadPageFragment( 'results', false ); 527 } ) 528 .catch( function () { 529 button.disabled = false; 530 } ); 531 } ); 532 } ); 533 } 534 535 /** 431 536 * Approximates wp-admin's "current page" highlight on the sidebar menu 432 537 * for the two plugin pages after an AJAX navigation. -
wap-qa-auditor-for-elementor/trunk/readme.txt
r3630418 r3633858 5 5 Tested up to: 7.0 6 6 Requires PHP: 8.1 7 Stable tag: 1. 0.07 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 42 42 Every issue is grouped by type into clean, expandable sections, with critical issues shown first. Search, filter by page, severity or content type, jump straight to the affected page or open it in Elementor, and export the full report to CSV in one click. 43 43 44 Found a false positive, or an issue you've decided not to fix? Mark it as "Ignored" — it stays visible for reference but is excluded from the site score, issue totals and CSV export. Ignored issues stay ignored across future scans of the same content, and can be unignored at any time. 45 44 46 **Privacy & data:** 45 47 46 This plugin does not collect, transmit or track any data. It stores one scan summary using the WordPress Options API and the current scan's issue list in a transient. Each new scan replaces the previous one— no scan history is kept, and no custom database tables are created.48 This plugin does not collect, transmit or track any data. It stores one scan summary and one small list of ignored-issue references using the WordPress Options API, plus the current scan's issue list in a transient. Each new scan replaces the previous results — no scan history is kept, and no custom database tables are created. 47 49 48 50 == Installation == … … 71 73 No. Only the most recent scan summary and results are stored. Running a new scan replaces the previous one. 72 74 75 = Can I exclude an issue I don't want to fix? = 76 77 Yes. Click "Ignore" next to any issue on the Results page. It remains visible for your reference but is no longer counted toward the site score, issue totals or CSV export, and stays ignored across future scans until you click "Unignore". 78 73 79 = What happens to plugin data when I uninstall it? = 74 80 75 All plugin data (the scan summary option and the scan results transient) is permanently removed when the plugin is uninstalled.81 All plugin data (the scan summary option, the ignored-issues option and the scan results transient) is permanently removed when the plugin is uninstalled. 76 82 77 83 == Screenshots == … … 83 89 == Changelog == 84 90 91 = 1.1.0 = 92 * Added the ability to mark individual issues as "Ignored" from the Results page. Ignored issues stay visible but are excluded from the site score, issue totals and CSV export, and persist across future scans until unignored. 93 85 94 = 1.0.0 = 86 95 * Initial release. … … 88 97 == Upgrade Notice == 89 98 90 = 1. 0.0 =91 Initial release.99 = 1.1.0 = 100 Adds the ability to ignore individual issues so they no longer affect your site score. -
wap-qa-auditor-for-elementor/trunk/src/Admin/Dashboard.php
r3630418 r3633858 145 145 <span class="wapqa-tooltip-row"><span class="wapqa-dot wapqa-dot-needs-improvement"></span><span>50–74</span><span class="wapqa-tooltip-value"><?php esc_html_e( 'Needs Improvement', 'wap-qa-auditor-for-elementor' ); ?></span></span> 146 146 <span class="wapqa-tooltip-row"><span class="wapqa-dot wapqa-dot-poor"></span><span>0–49</span><span class="wapqa-tooltip-value"><?php esc_html_e( 'Poor', 'wap-qa-auditor-for-elementor' ); ?></span></span> 147 <span class="wapqa-tooltip-divider"></span> 148 <span class="wapqa-tooltip-note"><?php esc_html_e( 'Issues you mark as "Ignored" on the Results page are not counted toward the score.', 'wap-qa-auditor-for-elementor' ); ?></span> 147 149 </span> 148 150 </span> -
wap-qa-auditor-for-elementor/trunk/src/Admin/Results.php
r3630418 r3633858 8 8 namespace WAPQA\Admin; 9 9 10 use WAPQA\Audit\Auditor; 10 11 use WAPQA\Services\ScanStorage; 11 12 … … 20 21 21 22 /** 23 * Auditor service. 24 * 25 * @var Auditor 26 */ 27 private Auditor $auditor; 28 29 /** 22 30 * Scan storage service. 23 31 * … … 29 37 * Constructor. 30 38 * 39 * @param Auditor $auditor Auditor service. 31 40 * @param ScanStorage $scan_storage Scan storage service. 32 41 */ 33 public function __construct( ScanStorage $scan_storage ) { 42 public function __construct( Auditor $auditor, ScanStorage $scan_storage ) { 43 $this->auditor = $auditor; 34 44 $this->scan_storage = $scan_storage; 35 45 } … … 190 200 ); 191 201 192 echo '<details class="wapqa-group wapqa-group-' . esc_attr( $severity ) . '" ' . ( 'critical' === $severity ? ' open' : '' ) . '>';202 echo '<details class="wapqa-group wapqa-group-' . esc_attr( $severity ) . '" data-issue-type="' . esc_attr( $issue_type ) . '"' . ( 'critical' === $severity ? ' open' : '' ) . '>'; 193 203 echo '<summary class="wapqa-group-header">'; 194 204 echo '<svg class="wapqa-group-chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="m9 6 6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; … … 213 223 ); 214 224 215 echo '<tr class="wapqa-row" data-severity="' . esc_attr( $severity ) . '" data-type="' . esc_attr( $row['content_type'] ) . '" data-title="' . esc_attr( $row['content_title'] ) . '" data-search="' . esc_attr( $search_haystack ) . '">'; 216 echo '<td class="wapqa-cell-title">' . esc_html( $row['content_title'] ) . '</td>'; 225 $issue_key = $row['issue_key'] ?? ''; 226 $is_ignored = ! empty( $row['ignored'] ); 227 $row_classes = 'wapqa-row' . ( $is_ignored ? ' wapqa-row-ignored' : '' ); 228 229 echo '<tr class="' . esc_attr( $row_classes ) . '" data-severity="' . esc_attr( $severity ) . '" data-type="' . esc_attr( $row['content_type'] ) . '" data-title="' . esc_attr( $row['content_title'] ) . '" data-search="' . esc_attr( $search_haystack ) . '">'; 230 echo '<td class="wapqa-cell-title">' . esc_html( $row['content_title'] ); 231 232 if ( $is_ignored ) { 233 echo ' <span class="wapqa-badge wapqa-badge-ignored">' . esc_html__( 'Ignored', 'wap-qa-auditor-for-elementor' ) . '</span>'; 234 } 235 236 echo '</td>'; 217 237 echo '<td>' . esc_html( $row['content_type'] ) . '</td>'; 218 238 echo '<td>' . esc_html( $row['widget'] ) . '</td>'; … … 220 240 echo '<td class="wapqa-actions-cell">'; 221 241 242 if ( '' !== $issue_key ) { 243 echo '<button type="button" class="wapqa-btn-ignore-toggle" data-issue-key="' . esc_attr( $issue_key ) . '">' . 244 ( $is_ignored 245 ? esc_html__( 'Unignore', 'wap-qa-auditor-for-elementor' ) 246 : esc_html__( 'Ignore', 'wap-qa-auditor-for-elementor' ) 247 ) . '</button>'; 248 } 249 222 250 if ( ! empty( $row['url'] ) ) { 223 251 echo '<a href="' . esc_url( $row['url'] ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'View Page', 'wap-qa-auditor-for-elementor' ) . '</a> '; … … 238 266 echo '</details>'; 239 267 } 268 269 /** 270 * Handles the AJAX request that toggles whether a single issue is 271 * ignored. Ignored issues stay visible in the results but are excluded 272 * from the site score and issue totals. 273 * 274 * @return void 275 */ 276 public function handle_toggle_ignore(): void { 277 check_ajax_referer( 'wapqa_admin_nonce', 'nonce' ); 278 279 if ( ! current_user_can( 'manage_options' ) ) { 280 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'wap-qa-auditor-for-elementor' ) ), 403 ); 281 } 282 283 $issue_key = isset( $_POST['issue_key'] ) ? sanitize_text_field( wp_unslash( $_POST['issue_key'] ) ) : ''; 284 285 if ( ! preg_match( '/^[a-f0-9]{12}$/', $issue_key ) ) { 286 wp_send_json_error( array( 'message' => __( 'Invalid issue.', 'wap-qa-auditor-for-elementor' ) ), 400 ); 287 } 288 289 $summary = $this->auditor->toggle_ignore( $issue_key ); 290 291 wp_send_json_success( array( 'summary' => $summary ) ); 292 } 240 293 } -
wap-qa-auditor-for-elementor/trunk/src/Audit/Auditor.php
r3630418 r3633858 86 86 $post_ids = array_values( array_unique( array_filter( $post_ids ) ) ); 87 87 88 $issues = array(); 89 $critical_count = 0; 90 $warning_count = 0; 91 $total_scanned = 0; 88 $issues = array(); 89 $total_scanned = 0; 92 90 93 91 foreach ( $post_ids as $post_id ) { … … 100 98 ++$total_scanned; 101 99 102 $post_issues = $this->scanner->scan_post( $post ); 103 104 foreach ( $post_issues as $issue ) { 105 if ( 'critical' === $issue['severity'] ) { 106 ++$critical_count; 107 } else { 108 ++$warning_count; 109 } 110 } 111 112 $issues = array_merge( $issues, $post_issues ); 113 } 100 $issues = array_merge( $issues, $this->scanner->scan_post( $post ) ); 101 } 102 103 $issues = $this->apply_ignored_state( $issues ); 104 $counts = $this->count_active_issues( $issues ); 114 105 115 106 $summary = array( 116 107 'last_scan_date' => current_time( 'mysql' ), 117 'last_scan_score' => self::calculate_score( $c ritical_count, $warning_count),108 'last_scan_score' => self::calculate_score( $counts['critical'], $counts['warning'] ), 118 109 'last_scan_mode' => $mode, 119 110 'total_scanned' => $total_scanned, 120 'total_issues' => $c ritical_count + $warning_count,121 'critical_issues' => $c ritical_count,122 'warning_issues' => $ warning_count,111 'total_issues' => $counts['critical'] + $counts['warning'], 112 'critical_issues' => $counts['critical'], 113 'warning_issues' => $counts['warning'], 123 114 ); 124 115 … … 129 120 'summary' => $summary, 130 121 'results' => $issues, 122 ); 123 } 124 125 /** 126 * Toggles whether a specific issue is ignored and recalculates the 127 * stored summary and results accordingly, without a full rescan. 128 * 129 * @param string $issue_key Issue key to toggle. 130 * @return array<string, int|string> Updated summary. 131 */ 132 public function toggle_ignore( string $issue_key ): array { 133 $this->scan_storage->toggle_ignored_issue( $issue_key ); 134 135 $issues = $this->apply_ignored_state( $this->scan_storage->get_results() ); 136 $counts = $this->count_active_issues( $issues ); 137 138 $summary = $this->scan_storage->get_summary(); 139 $summary['last_scan_score'] = self::calculate_score( $counts['critical'], $counts['warning'] ); 140 $summary['total_issues'] = $counts['critical'] + $counts['warning']; 141 $summary['critical_issues'] = $counts['critical']; 142 $summary['warning_issues'] = $counts['warning']; 143 144 $this->scan_storage->save_summary( $summary ); 145 $this->scan_storage->save_results( $issues ); 146 147 return $summary; 148 } 149 150 /** 151 * Marks each issue with whether the user has chosen to ignore it. 152 * 153 * @param array<int, array<string, mixed>> $issues Issues to annotate. 154 * @return array<int, array<string, mixed>> 155 */ 156 private function apply_ignored_state( array $issues ): array { 157 $ignored_keys = $this->scan_storage->get_ignored_issue_keys(); 158 159 foreach ( $issues as &$issue ) { 160 $issue['ignored'] = in_array( $issue['issue_key'] ?? '', $ignored_keys, true ); 161 } 162 unset( $issue ); 163 164 return $issues; 165 } 166 167 /** 168 * Counts critical and warning issues, excluding ignored ones. Ignored 169 * issues are not counted toward the site score or the issue totals. 170 * 171 * @param array<int, array<string, mixed>> $issues Issues with an 172 * 'ignored' flag already applied. 173 * @return array{critical: int, warning: int} 174 */ 175 private function count_active_issues( array $issues ): array { 176 $critical_count = 0; 177 $warning_count = 0; 178 179 foreach ( $issues as $issue ) { 180 if ( ! empty( $issue['ignored'] ) ) { 181 continue; 182 } 183 184 if ( 'critical' === $issue['severity'] ) { 185 ++$critical_count; 186 } else { 187 ++$warning_count; 188 } 189 } 190 191 return array( 192 'critical' => $critical_count, 193 'warning' => $warning_count, 131 194 ); 132 195 } -
wap-qa-auditor-for-elementor/trunk/src/Audit/ElementorScanner.php
r3630418 r3633858 658 658 * Builds a single issue row in the shared results format. 659 659 * 660 * Includes a deterministic `issue_key` derived from the post, issue type, 661 * widget and description, so a user's decision to ignore a specific issue 662 * survives future scans as long as the underlying content is unchanged. 663 * 660 664 * @param array<string, mixed> $context Content context. 661 665 * @param string $issue_type Short issue type label. … … 667 671 */ 668 672 private function build_issue( array $context, string $issue_type, string $severity, string $widget, string $description, string $url_override = '' ): array { 673 $issue_key = substr( md5( $context['post_id'] . '|' . $issue_type . '|' . $widget . '|' . $description ), 0, 12 ); 674 669 675 return array( 676 'issue_key' => $issue_key, 670 677 'post_id' => $context['post_id'], 671 678 'content_title' => $context['title'], -
wap-qa-auditor-for-elementor/trunk/src/Core/Plugin.php
r3630418 r3633858 89 89 90 90 $this->dashboard = new Dashboard( $auditor, $scan_storage ); 91 $this->results = new Results( $ scan_storage );91 $this->results = new Results( $auditor, $scan_storage ); 92 92 93 93 add_action( 'admin_menu', array( $this, 'register_admin_menu' ) ); … … 97 97 add_action( 'wp_ajax_wapqa_search_content', array( $this->dashboard, 'handle_content_search' ) ); 98 98 add_action( 'wp_ajax_wapqa_load_page', array( $this, 'handle_load_page' ) ); 99 add_action( 'wp_ajax_wapqa_toggle_ignore', array( $this->results, 'handle_toggle_ignore' ) ); 99 100 } 100 101 -
wap-qa-auditor-for-elementor/trunk/src/Services/ExportService.php
r3630418 r3633858 65 65 66 66 foreach ( $results as $row ) { 67 if ( ! empty( $row['ignored'] ) ) { 68 continue; 69 } 70 67 71 $csv .= $this->csv_row( 68 72 array( -
wap-qa-auditor-for-elementor/trunk/src/Services/ScanStorage.php
r3630418 r3633858 39 39 */ 40 40 public const TRANSIENT_EXPIRATION = DAY_IN_SECONDS; 41 42 /** 43 * Option name storing the list of issue keys the user has ignored. 44 * 45 * A small bounded list of short hashes, not a history table: it persists 46 * a user decision (ignore this specific issue) across future scans of 47 * the same content. 48 * 49 * @var string 50 */ 51 public const OPTION_IGNORED = 'wapqa_ignored_issues'; 41 52 42 53 /** … … 97 108 98 109 /** 110 * Retrieves the list of issue keys the user has chosen to ignore. 111 * 112 * @return string[] 113 */ 114 public function get_ignored_issue_keys(): array { 115 $ignored = get_option( self::OPTION_IGNORED, array() ); 116 117 return is_array( $ignored ) ? array_values( array_map( 'strval', $ignored ) ) : array(); 118 } 119 120 /** 121 * Toggles whether a specific issue is ignored. 122 * 123 * @param string $issue_key Issue key to toggle. 124 * @return bool True if the issue is now ignored, false if now active. 125 */ 126 public function toggle_ignored_issue( string $issue_key ): bool { 127 $ignored = $this->get_ignored_issue_keys(); 128 $index = array_search( $issue_key, $ignored, true ); 129 130 if ( false !== $index ) { 131 array_splice( $ignored, $index, 1 ); 132 $is_now_ignored = false; 133 } else { 134 $ignored[] = $issue_key; 135 $is_now_ignored = true; 136 } 137 138 update_option( self::OPTION_IGNORED, array_values( $ignored ), false ); 139 140 return $is_now_ignored; 141 } 142 143 /** 99 144 * Removes all stored scan data. 100 145 * … … 103 148 public function clear(): void { 104 149 delete_option( self::OPTION_SUMMARY ); 150 delete_option( self::OPTION_IGNORED ); 105 151 delete_transient( self::TRANSIENT_RESULTS ); 106 152 } -
wap-qa-auditor-for-elementor/trunk/uninstall.php
r3630418 r3633858 14 14 15 15 delete_option( 'wapqa_scan_summary' ); 16 delete_option( 'wapqa_ignored_issues' ); 16 17 delete_transient( 'wapqa_scan_results' ); -
wap-qa-auditor-for-elementor/trunk/wap-qa-auditor-for-elementor.php
r3630418 r3633858 3 3 * Plugin Name: WAP QA Auditor for Elementor 4 4 * Description: A lightweight manual QA auditing tool for Elementor websites. Scan pages and templates for image weight, heading structure, global style usage, container nesting and broken links before launch. 5 * Version: 1. 0.05 * Version: 1.1.0 6 6 * Requires at least: 6.5 7 7 * Requires PHP: 8.1 … … 22 22 } 23 23 24 define( 'WAPQA_VERSION', '1. 0.0' );24 define( 'WAPQA_VERSION', '1.1.0' ); 25 25 define( 'WAPQA_MIN_PHP_VERSION', '8.1' ); 26 26 define( 'WAPQA_MIN_WP_VERSION', '6.5' );
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)