diff --git a/README.md b/README.md index 8d519dc4..8f05413e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ **Requires at least:** 6.6 \ **Tested up to:** 6.8 \ **Requires PHP:** 7.4 \ -**Stable tag:** 3.5.0 \ +**Stable tag:** 3.5.2 \ **License:** GPLv2 or later Ally: Make your site more inclusive by scanning for accessibility violations, fixing them easily, and adding a usability widget and accessibility statement. @@ -213,6 +213,22 @@ These are smart suggestions generated by Ally to help you resolve issues more ef ## Changelog +### 3.5.2 - 2025-07-28 + +* Tweak: Improved performance by enqueuing Assistant only when logged in +* Fix: Admin post columns offset warning + + +### 3.5.1 - 2025-07-23 + +* Tweak: Admin panel UI updates +* Tweak: Assistant UI updates +* Tweak: Ensure case sensitive attributes in remediations +* Fix: Assistant accessibility issues +* Fix: Custom Icon issue in edge cases +* Fix: Critical Error on plugin conflict + + ### 3.5.0 - 2025-07-08 * New: Introducing URL Scanner – find 180+ issues instantly (WCAG 2.1 AA) diff --git a/assets/images/refresh-scan.svg b/assets/images/refresh-scan.svg index ee59a279..6d52289e 100644 --- a/assets/images/refresh-scan.svg +++ b/assets/images/refresh-scan.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/modules/remediation/actions/attribute.php b/modules/remediation/actions/attribute.php index 7ed3c23c..86385761 100644 --- a/modules/remediation/actions/attribute.php +++ b/modules/remediation/actions/attribute.php @@ -27,11 +27,11 @@ public function run() : ?DOMDocument { //Disable duplicates attr for image $exclusions = [ - 'alt' => ['role', 'title'], - 'role' => ['alt', 'title'] + 'alt' => [ 'role', 'title' ], + 'role' => [ 'alt', 'title' ], ]; - if ( isset( $exclusions[$this->data['attribute_name']] ) ) { - foreach ( $exclusions[$this->data['attribute_name']] as $attr_to_remove ) { + if ( isset( $exclusions[ $this->data['attribute_name'] ] ) ) { + foreach ( $exclusions[ $this->data['attribute_name'] ] as $attr_to_remove ) { $element_node->removeAttribute( $attr_to_remove ); } } diff --git a/modules/remediation/actions/replace.php b/modules/remediation/actions/replace.php index f9f32105..d785e313 100644 --- a/modules/remediation/actions/replace.php +++ b/modules/remediation/actions/replace.php @@ -24,19 +24,19 @@ public function run() : ?DOMDocument { $outer_html = $this->dom->saveHTML( $element_node ); - if ( strpos( $outer_html, $this->data['find'] ) === false ) { + if ( stripos( $outer_html, $this->data['find'] ) === false ) { return $this->dom; } - $updated_html = str_replace( $this->data['find'], $this->data['replace'], $outer_html ); + $updated_html = str_ireplace( $this->data['find'], $this->data['replace'], $outer_html ); if ( $updated_html === $outer_html ) { return $this->dom; } - $tmp_dom = new DOMDocument('1.0', 'UTF-8'); + $tmp_dom = new DOMDocument( '1.0', 'UTF-8' ); $tmp_dom->loadHTML( - mb_convert_encoding($updated_html, 'HTML-ENTITIES', 'UTF-8'), + mb_convert_encoding( $updated_html, 'HTML-ENTITIES', 'UTF-8' ), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD | LIBXML_NOERROR | LIBXML_NOWARNING ); diff --git a/modules/remediation/assets/js/actions/replace.js b/modules/remediation/assets/js/actions/replace.js index 255925bd..d51f33c5 100644 --- a/modules/remediation/assets/js/actions/replace.js +++ b/modules/remediation/assets/js/actions/replace.js @@ -1,6 +1,18 @@ import { RemediationBase } from './base'; export class ReplaceRemediation extends RemediationBase { + replaceIgnoreCase(outerHtml, find, replace, lowerOuterHTML, lowerFind) { + const index = lowerOuterHTML.indexOf(lowerFind); + if (index === -1) { + return outerHtml; + } + return ( + outerHtml.substring(0, index) + + replace + + outerHtml.substring(index + find.length) + ); + } + run() { const { xpath, find, replace } = this.data; const el = this.getElementByXPath(xpath); @@ -11,13 +23,24 @@ export class ReplaceRemediation extends RemediationBase { if (typeof find !== 'string' || typeof replace !== 'string') { return false; } - if (!outerHTML.includes(find)) { + const lowerOuterHTML = outerHTML.toLowerCase(); + const lowerFind = find.toLowerCase(); + if (!lowerOuterHTML.includes(lowerFind)) { return false; } - const updatedHTML = outerHTML.replace(find, replace); + + const updatedHTML = this.replaceIgnoreCase( + outerHTML, + find, + replace, + lowerOuterHTML, + lowerFind, + ); + if (updatedHTML === outerHTML) { return false; } + // Create a temporary container to parse the HTML string const tmp = document.createElement('div'); tmp.innerHTML = updatedHTML; diff --git a/modules/remediation/classes/utils.php b/modules/remediation/classes/utils.php index f09e3648..18d864ba 100644 --- a/modules/remediation/classes/utils.php +++ b/modules/remediation/classes/utils.php @@ -71,7 +71,7 @@ public static function get_current_object_type() : string { if ( $wp_query->is_singular() ) { if ( $wp_query->is_single() ) { - return $wp_query->query_vars['post_type'] ?? 'unknown'; + return get_post_type($wp_query->get_queried_object_id()) ?? 'unknown'; } elseif ( $wp_query->is_page() ) { return 'page'; } elseif ( $wp_query->is_attachment() ) { diff --git a/modules/scanner/assets/js/app.js b/modules/scanner/assets/js/app.js index c8c17d37..6c65785e 100644 --- a/modules/scanner/assets/js/app.js +++ b/modules/scanner/assets/js/app.js @@ -90,7 +90,9 @@ const App = () => { }>
+ {showResolvedMessage ? : getBlock()} +
diff --git a/modules/scanner/assets/js/components/block-button/index.js b/modules/scanner/assets/js/components/block-button/index.js index eea020ce..0a7acb4e 100644 --- a/modules/scanner/assets/js/components/block-button/index.js +++ b/modules/scanner/assets/js/components/block-button/index.js @@ -47,12 +47,17 @@ export const BlockButton = ({ color={resolved ? 'success' : 'default'} > - {title} + + {title} + + {showChip && ( )} + {resolved && } + {isManage && ( { > + - + {BLOCK_TITLES[openedBlock]} + {BLOCK_INFO[openedBlock] && ( { }); }; - const onRunNewScan = () => { + const onRescan = () => { runNewScan(); - sendOnClickEvent('New Scan'); + sendOnClickEvent('Rescan'); }; const goToManagement = () => { @@ -61,12 +61,14 @@ export const DropdownMenu = () => { > +
{ }} disablePortal > - + - {__('New Scan', 'pojo-accessibility')} + + {__('Rescan', 'pojo-accessibility')} + {!remediations.length ? ( { ], }} > - + @@ -117,6 +121,7 @@ export const DropdownMenu = () => { onClick={goToManagement} disabled={isManage} selected={isManage} + dense > @@ -133,6 +138,7 @@ export const DropdownMenu = () => { target="_blank" rel="noreferrer" onClick={() => sendOnClickEvent('View subscription')} + dense > diff --git a/modules/scanner/assets/js/components/header/index.js b/modules/scanner/assets/js/components/header/index.js index d02c384e..2140d56e 100644 --- a/modules/scanner/assets/js/components/header/index.js +++ b/modules/scanner/assets/js/components/header/index.js @@ -88,9 +88,10 @@ export const Header = () => { - + {window?.ea11yScannerData?.pageData?.title} + {showChip && ( { {isManage ? ( <> - + + {__('Manage AI fixes', 'pojo-accessibility')} @@ -127,7 +129,7 @@ export const Header = () => { <> - + {__('Accessibility Assistant', 'pojo-accessibility')} { + {showMainBlock && ( {isMainHeader ? ( @@ -176,6 +179,12 @@ const StyledCard = styled(Card)` `; const StyledTitle = styled(Typography)` + font-size: 16px; + font-weight: 500; + line-height: 130%; + letter-spacing: 0.15px; + margin: 0; + .MuiChip-root { margin-inline-start: ${({ theme }) => theme.spacing(1)}; diff --git a/modules/scanner/assets/js/components/manual-fix-form/index.js b/modules/scanner/assets/js/components/manual-fix-form/index.js index da314fbe..ebc31e9b 100644 --- a/modules/scanner/assets/js/components/manual-fix-form/index.js +++ b/modules/scanner/assets/js/components/manual-fix-form/index.js @@ -15,11 +15,7 @@ import { useToastNotification } from '@ea11y-apps/global/hooks'; import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { APIScanner } from '@ea11y-apps/scanner/api/APIScanner'; import { ResolveWithAi } from '@ea11y-apps/scanner/components/manual-fix-form/resolve-with-ai'; -import { - BLOCK_TITLES, - BLOCKS, - EXCLUDE_FROM_AI, -} from '@ea11y-apps/scanner/constants'; +import { BLOCKS, EXCLUDE_FROM_AI } from '@ea11y-apps/scanner/constants'; import { uxMessaging } from '@ea11y-apps/scanner/constants/ux-messaging'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { useCopyToClipboard } from '@ea11y-apps/scanner/hooks/use-copy-to-clipboard'; @@ -32,6 +28,7 @@ import { StyledSnippet, } from '@ea11y-apps/scanner/styles/manual-fixes.styles'; import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item'; +import { speak } from '@wordpress/a11y'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -54,16 +51,31 @@ export const ManualFixForm = ({ item, current, setOpen }) => { const sendMixpanelEvent = (event) => { mixpanelService.sendEvent(event, { - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, issue_type: item.message, element_selector: item.path.dom, }); }; + const focusOnActive = () => { + const rootNode = document.getElementById('ea11y-scanner-wizard-widget'); + const currentItem = rootNode.shadowRoot.querySelector( + `#manual-panel-${current}`, + ); + + if (currentItem) { + currentItem.focus(); + } + }; + const handleSkip = () => { closeExample(); setOpen(current + 1); + focusOnActive(); + sendMixpanelEvent(mixpanelEvents.issueSkipped); + + speak(__('Issue skipped', 'pojo-accessibility'), 'polite'); }; const handleMarkResolved = async () => { @@ -72,7 +84,11 @@ export const ManualFixForm = ({ item, current, setOpen }) => { closeExample(); markResolved(); + focusOnActive(); + sendMixpanelEvent(mixpanelEvents.markAsResolveClicked); + + speak(__('Issue resolved', 'pojo-accessibility'), 'polite'); } catch (e) { error(__('An error occurred.', 'pojo-accessibility')); } @@ -87,10 +103,12 @@ export const ManualFixForm = ({ item, current, setOpen }) => { - + {__('What’s the issue', 'pojo-accessibility')} + { 'pojo-accessibility', )} + {uxMessaging[item.ruleId].whyItMatters} @@ -115,17 +134,21 @@ export const ManualFixForm = ({ item, current, setOpen }) => { + {uxMessaging[item.ruleId]?.whatsTheIssue ?? item.message} + - + {__('Where is it', 'pojo-accessibility')} + {item.snippet} + { + {showAIBlock && } + {uxMessaging[item.ruleId] && ( <> - + {__('How to resolve it', 'pojo-accessibility')} + {uxMessaging[item.ruleId].howToResolve} + { justifyContent="space-between" alignItems="start" > - + {__('See an example', 'pojo-accessibility')} + { - + {__('Issue:', 'pojo-accessibility')} + { > {uxMessaging[item.ruleId].seeAnExample.issue} - + + {__('Resolution:', 'pojo-accessibility')} + {uxMessaging[item.ruleId].seeAnExample.resolution.flatMap( (resolution, index) => ( { ), )} + - diff --git a/modules/scanner/assets/js/context/scanner-wizard-context.js b/modules/scanner/assets/js/context/scanner-wizard-context.js index 765d47a1..9a9e484a 100644 --- a/modules/scanner/assets/js/context/scanner-wizard-context.js +++ b/modules/scanner/assets/js/context/scanner-wizard-context.js @@ -1,7 +1,6 @@ import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { APIScanner } from '@ea11y-apps/scanner/api/APIScanner'; import { - BLOCK_TITLES, BLOCKS, INITIAL_SORTED_VIOLATIONS, MANAGE_URL_PARAM, @@ -138,7 +137,7 @@ export const ScannerWizardContextProvider = ({ children }) => { issue_type: item.message, rule_id: item.ruleId, wcag_level: item.reasonCategory.match(/\((AAA?|AA?|A)\)/)?.[1] || '', - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, }); } }; @@ -168,8 +167,8 @@ export const ScannerWizardContextProvider = ({ children }) => { setAltTextData([]); setManualData(structuredClone(MANUAL_GROUPS)); setResolved( - initialViolations > data.summary?.counts?.violation - ? initialViolations - data.summary?.counts?.violation + initialViolations >= data.summary?.counts?.issuesResolved + ? data.summary?.counts?.issuesResolved : 0, ); } catch (e) { diff --git a/modules/scanner/assets/js/hooks/use-alt-text-form.js b/modules/scanner/assets/js/hooks/use-alt-text-form.js index 0143c009..8910590f 100644 --- a/modules/scanner/assets/js/hooks/use-alt-text-form.js +++ b/modules/scanner/assets/js/hooks/use-alt-text-form.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import { useToastNotification } from '@ea11y-apps/global/hooks'; import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { APIScanner } from '@ea11y-apps/scanner/api/APIScanner'; -import { BLOCK_TITLES, BLOCKS } from '@ea11y-apps/scanner/constants'; +import { BLOCKS } from '@ea11y-apps/scanner/constants'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item'; import { removeExistingFocus } from '@ea11y-apps/scanner/utils/focus-on-element'; @@ -115,7 +115,7 @@ export const useAltTextForm = ({ current, item }) => { }); if (e.target.checked) { mixpanelService.sendEvent(mixpanelEvents.markAsDecorativeSelected, { - category_name: BLOCK_TITLES[BLOCKS.altText], + category_name: BLOCKS.altText, }); } }; @@ -151,7 +151,7 @@ export const useAltTextForm = ({ current, item }) => { ? 'Mark as decorative' : fixMethod, issue_type: item.message, - category_name: BLOCK_TITLES[BLOCKS.altText], + category_name: BLOCKS.altText, }); }; @@ -169,7 +169,7 @@ export const useAltTextForm = ({ current, item }) => { issue_type: item.message, rule_id: item.ruleId, wcag_level: item.reasonCategory.match(/\((AAA?|AA?|A)\)/)?.[1] || '', - category_name: BLOCK_TITLES[BLOCKS.altText], + category_name: BLOCKS.altText, ai_text_response: text, }); }; diff --git a/modules/scanner/assets/js/hooks/use-copy-to-clipboard.js b/modules/scanner/assets/js/hooks/use-copy-to-clipboard.js index 7d06402d..8c47aaf9 100644 --- a/modules/scanner/assets/js/hooks/use-copy-to-clipboard.js +++ b/modules/scanner/assets/js/hooks/use-copy-to-clipboard.js @@ -1,6 +1,5 @@ import clipboardCopy from 'clipboard-copy'; import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; -import { BLOCK_TITLES } from '@ea11y-apps/scanner/constants'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { useState } from '@wordpress/element'; @@ -14,7 +13,7 @@ export const useCopyToClipboard = () => { mixpanelService.sendEvent(mixpanelEvents.copySnippetClicked, { snippet_type: type, snippet_content: snippet, - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, source, }); }; diff --git a/modules/scanner/assets/js/hooks/use-manage-actions.js b/modules/scanner/assets/js/hooks/use-manage-actions.js index 1f4ef5a4..755caa96 100644 --- a/modules/scanner/assets/js/hooks/use-manage-actions.js +++ b/modules/scanner/assets/js/hooks/use-manage-actions.js @@ -1,7 +1,6 @@ import { useToastNotification } from '@ea11y-apps/global/hooks'; import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { APIScanner } from '@ea11y-apps/scanner/api/APIScanner'; -import { BLOCK_TITLES } from '@ea11y-apps/scanner/constants'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -90,7 +89,7 @@ export const useManageActions = (current = null) => { mixpanelEvents[active ? 'remediationEnabled' : 'remediationDisabled'], { action_type: active ? 'enable_specific' : 'disable_specific', - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, issue_type: current.rule, }, ); @@ -119,7 +118,7 @@ export const useManageActions = (current = null) => { setIsManageChanged(true); mixpanelService.sendEvent(mixpanelEvents.remediationRemoved, { action_type: 'remove_specific', - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, issue_type: current.rule, }); } catch (e) { @@ -150,7 +149,7 @@ export const useManageActions = (current = null) => { mixpanelService.sendEvent(mixpanelEvents.applyFixButtonClicked, { fix_method: 'manual', snippet_content: strContent, - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, source: 'remediation', }); } catch (e) { diff --git a/modules/scanner/assets/js/hooks/use-manual-fix-form.js b/modules/scanner/assets/js/hooks/use-manual-fix-form.js index cbf0b018..9d31b60a 100644 --- a/modules/scanner/assets/js/hooks/use-manual-fix-form.js +++ b/modules/scanner/assets/js/hooks/use-manual-fix-form.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import { useToastNotification } from '@ea11y-apps/global/hooks'; import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { APIScanner } from '@ea11y-apps/scanner/api/APIScanner'; -import { BLOCK_TITLES, BLOCKS } from '@ea11y-apps/scanner/constants'; +import { BLOCKS } from '@ea11y-apps/scanner/constants'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { scannerItem } from '@ea11y-apps/scanner/types/scanner-item'; import { removeExistingFocus } from '@ea11y-apps/scanner/utils/focus-on-element'; @@ -102,7 +102,7 @@ export const useManualFixForm = ({ item, current }) => { fix_method: manualEdit ? 'manual' : 'AI', issue_type: item.message, snippet_content: replace, - category_name: BLOCK_TITLES[openedBlock], + category_name: openedBlock, source: 'assistant', }); diff --git a/modules/scanner/assets/js/layouts/alt-text-layout.js b/modules/scanner/assets/js/layouts/alt-text-layout.js index ea3d91cf..6c45a56d 100644 --- a/modules/scanner/assets/js/layouts/alt-text-layout.js +++ b/modules/scanner/assets/js/layouts/alt-text-layout.js @@ -1,7 +1,7 @@ import { mixpanelEvents, mixpanelService } from '@ea11y-apps/global/services'; import { AltTextForm } from '@ea11y-apps/scanner/components/alt-text-form'; import { AltTextNavigation } from '@ea11y-apps/scanner/components/alt-text-navigation'; -import { BLOCK_TITLES, BLOCKS } from '@ea11y-apps/scanner/constants'; +import { BLOCKS } from '@ea11y-apps/scanner/constants'; import { useScannerWizardContext } from '@ea11y-apps/scanner/context/scanner-wizard-context'; import { StyledContent } from '@ea11y-apps/scanner/styles/app.styles'; import { @@ -28,7 +28,7 @@ export const AltTextLayout = () => { issue_type: item.message, rule_id: item.ruleId, wcag_level: item.reasonCategory.match(/\((AAA?|AA?|A)\)/)?.[1] || '', - category_name: BLOCK_TITLES[BLOCKS.altText], + category_name: BLOCKS.altText, }); }, [current]); diff --git a/modules/scanner/assets/js/layouts/main-layout.js b/modules/scanner/assets/js/layouts/main-layout.js index ce75e7a9..1c1b9f72 100644 --- a/modules/scanner/assets/js/layouts/main-layout.js +++ b/modules/scanner/assets/js/layouts/main-layout.js @@ -7,9 +7,10 @@ import { __ } from '@wordpress/i18n'; export const MainLayout = () => { return ( - + {__('All issues', 'pojo-accessibility')} + ); diff --git a/modules/scanner/assets/js/layouts/manual-layout.js b/modules/scanner/assets/js/layouts/manual-layout.js index 01575ab4..f9981ed9 100644 --- a/modules/scanner/assets/js/layouts/manual-layout.js +++ b/modules/scanner/assets/js/layouts/manual-layout.js @@ -43,10 +43,12 @@ export const ManualLayout = () => { checked={manualData[openedBlock][index]?.resolved || false} aria-label={__('Resolved', 'pojo-accessibility')} /> - + + {uxMessaging[item.ruleId]?.violationName ?? item.category} + ))} diff --git a/modules/scanner/assets/js/services/scanner-wizard.js b/modules/scanner/assets/js/services/scanner-wizard.js index 9d7fc9c8..5a9f5940 100644 --- a/modules/scanner/assets/js/services/scanner-wizard.js +++ b/modules/scanner/assets/js/services/scanner-wizard.js @@ -11,6 +11,7 @@ const load = async () => { const script = document.createElement('script'); script.src = scriptSrc; + script.referrerPolicy = 'strict-origin-when-cross-origin'; script.async = true; script.onload = () => { diff --git a/modules/scanner/assets/js/styles/app.styles.js b/modules/scanner/assets/js/styles/app.styles.js index 15cd4453..88a728a4 100644 --- a/modules/scanner/assets/js/styles/app.styles.js +++ b/modules/scanner/assets/js/styles/app.styles.js @@ -113,6 +113,11 @@ export const StyledButton = styled(Button)` font-weight: 400; justify-content: start; padding: 0; + + &:focus .MuiPaper-root, + &:focus-visible .MuiPaper-root { + background-color: ${({ theme }) => theme.palette.action.hover}; + } `; export const UpgradeContentContainer = styled(Box)` diff --git a/modules/scanner/components/list-column.php b/modules/scanner/components/list-column.php index 77973ee1..09926b1d 100644 --- a/modules/scanner/components/list-column.php +++ b/modules/scanner/components/list-column.php @@ -5,6 +5,7 @@ use EA11y\Classes\Database\Exceptions\Missing_Table_Exception; use EA11y\Modules\Remediation\Database\Page_Entry; use EA11y\Modules\Remediation\Database\Page_Table; +use EA11y\Modules\Scanner\Database\Scan_Entry; if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. @@ -63,8 +64,19 @@ private function render_column_accessibility( string $url ) { $url_trimmed = rtrim( $url, '/' ); $page = $this->get_current_page( $url_trimmed ); $has_scan_data = $page->exists(); - $violation = $page->__get( Page_Table::VIOLATIONS ); - $resolved = $page->__get( Page_Table::RESOLVED ); + $latest_scan = Scan_Entry::get_scan_result( $url_trimmed, true ); + + if ( empty( $latest_scan ) ) { + $latest_scan = [ + 'counts' => [ + 'violation' => 0, + 'issuesResolved' => 0, + ], + ]; + } + + $violation = $latest_scan['counts']['violation']; + $resolved = $latest_scan['counts']['issuesResolved']; $passed = $has_scan_data && $resolved === $violation; diff --git a/modules/scanner/module.php b/modules/scanner/module.php index 1718fcde..45bb9399 100644 --- a/modules/scanner/module.php +++ b/modules/scanner/module.php @@ -88,6 +88,7 @@ public function enqueue_assets() : void { 'ea11yScannerData', [ 'wpRestNonce' => wp_create_nonce( 'wp_rest' ), + 'dashboardUrl' => admin_url( 'admin.php?page=accessibility-settings' ), 'scannerUrl' => self::get_scanner_wizard_url(), 'initialScanResult' => Scan_Entry::get_scan_result( $url ), 'pageData' => [ @@ -117,6 +118,19 @@ public function enqueue_admin_styles() : void { ); } + public static function is_active(): bool { + return self::has_required_permissions() && + self::is_connected_and_enabled(); + } + + private static function has_required_permissions(): bool { + return is_user_logged_in() && current_user_can( 'manage_options' ); + } + + private static function is_connected_and_enabled(): bool { + return Connect::is_connected() && ! \EA11y\Modules\Legacy\Module::is_active(); + } + public function __construct() { Scans_Table::install(); diff --git a/modules/scanner/rest/scanner-stats.php b/modules/scanner/rest/scanner-stats.php index 11797f14..a7d8dcb5 100644 --- a/modules/scanner/rest/scanner-stats.php +++ b/modules/scanner/rest/scanner-stats.php @@ -62,8 +62,8 @@ public function GET( $request ) { if ( count( $recent_scans ) > 0 ) { $output['scans'] ++; - $output['issues_total'] += $recent_scans[0]->summary['counts']['violation']; - $output['issues_fixed'] += $recent_scans[0]->summary['counts']['issuesResolved']; + $output['issues_total'] += $recent_scans[0]->summary['counts']['violation'] ?? 0; + $output['issues_fixed'] += $recent_scans[0]->summary['counts']['issuesResolved'] ?? 0; } } diff --git a/modules/settings/assets/js/components/sidebar-menu/menu.js b/modules/settings/assets/js/components/sidebar-menu/menu.js index 11ff28ca..2ba62b27 100644 --- a/modules/settings/assets/js/components/sidebar-menu/menu.js +++ b/modules/settings/assets/js/components/sidebar-menu/menu.js @@ -13,7 +13,7 @@ import AccessibilityStatementTooltip from './tooltips/accessibility-statement'; export const MenuItems = { scanner: { - name: __('Accessibility scans', 'pojo-accessibility'), + name: __('Accessibility Assistant', 'pojo-accessibility'), key: 'scanner', type: 'heading', }, diff --git a/modules/settings/assets/js/pages/assistant/results/dropdown-menu.js b/modules/settings/assets/js/pages/assistant/results/dropdown-menu.js index 5cc3cc91..67ec4b2d 100644 --- a/modules/settings/assets/js/pages/assistant/results/dropdown-menu.js +++ b/modules/settings/assets/js/pages/assistant/results/dropdown-menu.js @@ -38,7 +38,7 @@ export const DropdownMenu = ({ pageUrl, remediationCount }) => { }); }; - const onRunNewScan = () => { + const onRescan = () => { sendOnClickEvent('scan'); }; @@ -60,12 +60,15 @@ export const DropdownMenu = ({ pageUrl, remediationCount }) => { > + { href={ctaScan} target="_blank" rel="noreferrer" - onClick={onRunNewScan} + onClick={onRescan} + dense > - {__('New Scan', 'pojo-accessibility')} + + {__('Rescan', 'pojo-accessibility')} + {remediationCount < 1 ? ( { ], }} > - + @@ -124,6 +130,7 @@ export const DropdownMenu = ({ pageUrl, remediationCount }) => { target="_blank" rel="noreferrer" onClick={goToManagement} + dense > diff --git a/modules/widget/components/ally-trigger.php b/modules/widget/components/ally-trigger.php index 2f48a3ef..414c37ba 100644 --- a/modules/widget/components/ally-trigger.php +++ b/modules/widget/components/ally-trigger.php @@ -1,8 +1,8 @@ - - frontend->create_action_hash( 'allyWidget:open', [] ); } } diff --git a/modules/widget/module.php b/modules/widget/module.php index f17c118f..dc97c121 100644 --- a/modules/widget/module.php +++ b/modules/widget/module.php @@ -44,12 +44,12 @@ public function enqueue_accessibility_widget() : void { true ); - wp_enqueue_style( - 'ea11y-widget-fonts', - EA11Y_ASSETS_URL . 'build/fonts.css', - [], - EA11Y_VERSION - ); + wp_enqueue_style( + 'ea11y-widget-fonts', + EA11Y_ASSETS_URL . 'build/fonts.css', + [], + EA11Y_VERSION + ); $is_analytics_enabled = AnalyticsModule::is_active(); @@ -129,52 +129,52 @@ public function enqueue_accessibility_widget_admin( $hook ) : void { plan->features ) ) { - return $widget_settings; - } - - // Check if the feature is available in the plan - foreach( $features as $feature ) { - $feature_name = str_replace( '_', '-', $feature ); - - // Assuming feature does not exist in the plan. - $feature_in_plan_data = false; - - // Check if it exists in the plan. A contingency to handle downgrading of plans. - if ( isset( $plan_data->plan->features->{$feature} ) ) { - if ($plan_data->plan->features->{$feature} ) { - $feature_in_plan_data = $plan_data->plan->features->{$feature}; - } else { - // Auto disable plan if it is set to false in the plan data. - $widget_settings[$feature_name]['enabled'] = false; - Settings::set( Settings::WIDGET_MENU_SETTINGS, $widget_settings ); - } - } else { - continue; - } - - $feature_in_widget_settings = isset( $widget_settings[$feature_name] ); - - if ( ! $feature_in_plan_data && $feature_in_widget_settings ) { - $widget_settings[$feature_name]['enabled'] = false; - } - } - - return $widget_settings; - } + /** + * Get widget's tools/menu settings + * @return array|mixed + */ + public function get_tools_settings() { + // Features to check + $features = [ 'screen_reader', 'remove_elementor_label' ]; + + // Get the data from the settings + $widget_settings = Settings::get( Settings::WIDGET_MENU_SETTINGS ); + $plan_data = Settings::get( Settings::PLAN_DATA ); + + // Return settings if features object in not present in plan data. + if ( ! isset( $plan_data->plan->features ) ) { + return $widget_settings; + } + + // Check if the feature is available in the plan + foreach ( $features as $feature ) { + $feature_name = str_replace( '_', '-', $feature ); + + // Assuming feature does not exist in the plan. + $feature_in_plan_data = false; + + // Check if it exists in the plan. A contingency to handle downgrading of plans. + if ( isset( $plan_data->plan->features->{$feature} ) ) { + if ( $plan_data->plan->features->{$feature} ) { + $feature_in_plan_data = $plan_data->plan->features->{$feature}; + } else { + // Auto disable plan if it is set to false in the plan data. + $widget_settings[ $feature_name ]['enabled'] = false; + Settings::set( Settings::WIDGET_MENU_SETTINGS, $widget_settings ); + } + } else { + continue; + } + + $feature_in_widget_settings = isset( $widget_settings[ $feature_name ] ); + + if ( ! $feature_in_plan_data && $feature_in_widget_settings ) { + $widget_settings[ $feature_name ]['enabled'] = false; + } + } + + return $widget_settings; + } /** * Remove person object from the icon settings for frontend. @@ -211,16 +211,57 @@ public function script_loader_tag( $tag, $handle, $src ) { * register_dynamic_tag * @param \Elementor\Core\DynamicTags\Manager $dynamic_tags_manager */ - function register_dynamic_tag( $dynamic_tags_manager ) { + public function register_dynamic_tag( $dynamic_tags_manager ) { $dynamic_tags_manager->register( new Components\Ally_Trigger() ); } + public function render_dynamic_tag_handler() { + ?> + + register_components(); + add_action( 'wp_footer', [ $this, 'render_dynamic_tag_handler' ] ); add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_accessibility_widget' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_accessibility_widget_admin' ] ); // Add referrer policy to widget script tag diff --git a/package-lock.json b/package-lock.json index 8fbf75b7..163ed2c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pojo-accessibility", - "version": "3.5.0", + "version": "3.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pojo-accessibility", - "version": "3.5.0", + "version": "3.5.2", "dependencies": { "@elementor/design-tokens": "^1.1.4", "@elementor/icons": "^1.46.0", diff --git a/package.json b/package.json index 2358a9f3..a888695b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "slug": "pojo-accessibility", "homepage": "http://pojo.me/", "description": "", - "version": "3.5.0", + "version": "3.5.2", "scripts": { "build": "wp-scripts build", "start": "wp-scripts start", diff --git a/pojo-accessibility.php b/pojo-accessibility.php index ccda980e..a50721bc 100644 --- a/pojo-accessibility.php +++ b/pojo-accessibility.php @@ -5,7 +5,7 @@ * Description: Improve your website’s accessibility with ease. Customize capabilities such as text resizing, contrast modes, link highlights, and easily generate an accessibility statement to demonstrate your commitment to inclusivity. * Author: Elementor.com * Author URI: https://elementor.com/ - * Version: 3.5.0 + * Version: 3.5.2 * Text Domain: pojo-accessibility * Domain Path: /languages/ */ @@ -16,7 +16,7 @@ // Legacy define( 'POJO_A11Y_CUSTOMIZER_OPTIONS', 'pojo_a11y_customizer_options' ); -define( 'EA11Y_VERSION', '3.5.0' ); +define( 'EA11Y_VERSION', '3.5.2' ); define( 'EA11Y_MAIN_FILE', __FILE__ ); define( 'EA11Y_BASE', plugin_basename( EA11Y_MAIN_FILE ) ); define( 'EA11Y_PATH', plugin_dir_path( __FILE__ ) ); diff --git a/readme.txt b/readme.txt index eff6b0c3..df3d206d 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: Web Accessibility, Accessibility, A11Y, WCAG, Accessibility Statement Requires at least: 6.6 Tested up to: 6.8 Requires PHP: 7.4 -Stable tag: 3.5.0 +Stable tag: 3.5.2 License: GPLv2 or later Ally: Make your site more inclusive by scanning for accessibility violations, fixing them easily, and adding a usability widget and accessibility statement. @@ -184,6 +184,19 @@ These are smart suggestions generated by Ally to help you resolve issues more ef 5. Widget on Site: This is how the accessibility widget appears on a live website. == Changelog == += 3.5.2 - 2025-07-28 = +* Tweak: Improved performance by enqueuing Assistant only when logged in +* Fix: Admin post columns offset warning + += 3.5.1 - 2025-07-23 = +* Tweak: Admin panel UI updates +* Tweak: Assistant UI updates +* Tweak: Ensure case sensitive attributes in remediations +* Fix: Assistant accessibility issues +* Fix: Custom Icon issue in edge cases +* Fix: Critical Error on plugin conflict + + = 3.5.0 - 2025-07-08 = * New: Introducing URL Scanner – find 180+ issues instantly (WCAG 2.1 AA) * New: Introducing Remediation Engine – get in-context guided, AI-powered accessibility fixes diff --git a/ruleset.xml b/ruleset.xml index 654bb9fe..475e13b3 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -12,6 +12,13 @@ node_modules/ includes/libraries/ + + . + + + + +