diff --git a/README.md b/README.md index b798a5fd..bed03d23 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ **Requires at least:** 6.6 \ **Tested up to:** 6.9 \ **Requires PHP:** 7.4 \ -**Stable tag:** 4.1.0 \ +**Stable tag:** 4.1.1 \ **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. @@ -227,6 +227,11 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro ## Changelog +### 4.1.1 – 2026-03-31 + +* Tweak: Improved WPML support +* Tweak: WordPress 7.0 design compatibility + ### 4.1.0 – 2026-02-23 * New: Added bulk remediation flow to generate AI alt text or mark multiple images as decorative diff --git a/assets/css/admin.css b/assets/css/admin.css index 6ffadf82..1970c6a3 100644 --- a/assets/css/admin.css +++ b/assets/css/admin.css @@ -7,6 +7,11 @@ filter: none; } -.error-elementor { - border-left-color: #93003F; +.notice_wrapper { + border: 1px solid #ccd0d4; + box-shadow: 0 1px 4px #00000026; +} + +.notice_wrapper.error-elementor { + border-inline-start: 4px solid #93003F; } diff --git a/assets/css/notice.css b/assets/css/notice.css new file mode 100644 index 00000000..2166c661 --- /dev/null +++ b/assets/css/notice.css @@ -0,0 +1,171 @@ +:root { + --ea11y-notice-dismiss-icon: #1e1e1e; + --ea11y-notice-error: #dc2626; + --ea11y-notice-error-bg: #fde8ec; + --ea11y-notice-error-custom-border: #93003f; + --ea11y-notice-white: #fff; + --ea11y-notice-warning-bg: #fef8ee; + --ea11y-notice-warning-border: #f0b849; + --ea11y-notice-warning-btn: #bb5b1d; + --ea11y-notice-warning-btn-hover: rgb(187 91 29 / 0.08); + --ea11y-notice-success-bg: #eff9f1; + --ea11y-notice-success-border: #4ab866; + --ea11y-notice-info-bg: #f1f6fb; + --ea11y-notice-info-border: #0288d1; + --ea11y-notice-pink-border: #d8d8da; + --ea11y-notice-pink-accent: #ff7be5; + --ea11y-notice-pink-text: #444950; + --ea11y-notice-pink-link: #d004d4; + --ea11y-notice-pink-icon-bg: #fae8ff; + --ea11y-notice-heading: #0c0d0e; + --ea11y-notice-subheading: #6d7882; + --ea11y-notice-error-btn-hover: rgb(220 38 38 / 0.08); +} + +.ea11y__notice { + padding: 12px; + border: none; +} + +.ea11y__notice p { + margin: 0; +} + +.ea11y__notice .notice-dismiss::before { + content: "\f335"; + font-family: dashicons, sans-serif; + font-weight: 400; + font-size: 22px; + line-height: 24px; + color: var(--ea11y-notice-dismiss-icon); + width: 24px; +} + +.ea11y__notice button:not(.notice-dismiss) { + display: block; + padding: 0; + background: none; + color: var(--ea11y-notice-error); + border: none; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 1.5; + letter-spacing: -0.247px; + text-decoration-line: underline; + cursor: pointer; +} + +.ea11y__notice--error { + background-color: var(--ea11y-notice-error-bg); + border-inline-start: 4px solid var(--ea11y-notice-error); +} + +.ea11y__notice--error-custom { + background-color: var(--ea11y-notice-white); + border-inline-start: 4px solid var(--ea11y-notice-error-custom-border); +} + +.ea11y__notice--warning { + background-color: var(--ea11y-notice-warning-bg); + border-inline-start: 4px solid var(--ea11y-notice-warning-border); +} + +.ea11y__notice--success { + background-color: var(--ea11y-notice-success-bg); + border-inline-start: 4px solid var(--ea11y-notice-success-border); +} + +.ea11y__notice--info { + background-color: var(--ea11y-notice-info-bg); + border-inline-start: 4px solid var(--ea11y-notice-info-border); +} + +.ea11y__renewal-notice { + border-inline-start: none; +} + +.ea11y__notice--pink { + padding-left: 54px; + position: relative; + background-color: var(--ea11y-notice-white); + border: 1px solid var(--ea11y-notice-pink-border); + border-inline-start: 4px solid var(--ea11y-notice-pink-accent); +} + +.ea11y__notice--pink b { + font-weight: 500; + color: var(--ea11y-notice-pink-text); +} + +.ea11y__notice--pink a { + font-weight: 500; + color: var(--ea11y-notice-pink-link); + margin-inline-start: 10px; + text-decoration: none; +} + +.ea11y__notice--pink .ea11y__icon-block { + display: flex; + justify-content: center; + align-items: center; + width: 48px; + position: absolute; + left: 0; + top: 0; + bottom: 0; + background-color: var(--ea11y-notice-pink-icon-bg); +} + +.ea11y__notice .primary-heading { + font-size: 17px; + color: var(--ea11y-notice-heading); + font-weight: 700; + margin-bottom: 8px; +} + +.ea11y__notice .sub-heading { + font-size: 13px; + color: var(--ea11y-notice-subheading); + margin-top: 0; + margin-bottom: 12px; +} + +/** Renewal notice styles **/ + +.ea11y__content-block { + display: flex; + gap: 15px; +} + +.ea11y__content-block .ea11y__notice-content { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 10px; +} + +.ea11y__renewal-notice-btn { + text-decoration: none; + padding: 4px 10px; + border-radius: 4px; + width: auto; +} + +.ea11y__renewal-notice.ea11y__notice--warning .ea11y__renewal-notice-btn { + color: var(--ea11y-notice-warning-btn); + border: 1px solid var(--ea11y-notice-warning-btn); +} + +.ea11y__renewal-notice.ea11y__notice--error .ea11y__renewal-notice-btn { + color: var(--ea11y-notice-error); + border: 1px solid var(--ea11y-notice-error); +} + +.ea11y__renewal-notice.ea11y__notice--warning .ea11y__renewal-notice-btn:hover { + background: var(--ea11y-notice-warning-btn-hover); +} + +.ea11y__renewal-notice.ea11y__notice--error .ea11y__renewal-notice-btn:hover { + background-color: var(--ea11y-notice-error-btn-hover); +} diff --git a/classes/utils.php b/classes/utils.php index 38df00c0..ec86f3f0 100644 --- a/classes/utils.php +++ b/classes/utils.php @@ -15,7 +15,21 @@ public static function get_api_client(): ?Client { public static function is_plugin_settings_page(): bool { $current_screen = get_current_screen(); - return str_contains( $current_screen->id, '_page_accessibility-settings' ); + return $current_screen && str_contains( $current_screen->id, '_page_accessibility-settings' ); + } + + public static function is_wp_dashboard_page(): bool { + $current_screen = get_current_screen(); + return $current_screen && 'dashboard' === $current_screen->id; + } + + public static function is_wp_settings_page(): bool { + $current_screen = get_current_screen(); + return $current_screen && 'options-general' === $current_screen->id; + } + + public static function is_plugin_page(): bool { + return self::is_plugin_settings_page(); } public static function is_elementor_installed() :bool { diff --git a/classes/utils/notice-base.php b/classes/utils/notice-base.php index 4a1e56db..5b8cc9e2 100644 --- a/classes/utils/notice-base.php +++ b/classes/utils/notice-base.php @@ -157,7 +157,8 @@ public function handle_dismiss() { } public function render() { - echo sprintf( '
%6$s
', + echo sprintf( '
%7$s
', + esc_attr__( 'Notice', 'pojo-accessibility' ), $this->is_dismissible ? 'ea11y-dismiss is-dismissible' : '', esc_attr( $this->type ), esc_attr( $this->get_id() ), diff --git a/composer.json b/composer.json index de1d640b..300f5947 100644 --- a/composer.json +++ b/composer.json @@ -1,14 +1,5 @@ { - "name": "pojome/pojo-accessibility", - "description": "pojo accessibility", - "minimum-stability": "dev", - "license": "gpl2", - "authors": [ - { - "name": "Yakir Sitbon", - "email": "yakir@pojo.me" - } - ], + "name": "elementor/pojo-accessibility", "require-dev": { "johnpbloch/wordpress-core": "^6.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1", @@ -38,7 +29,7 @@ "ext-ctype": "*", "ext-mbstring": "*", "elementor/wp-notifications-package": "^1.2.0", - "elementor/wp-one-package": "1.0.54" + "elementor/wp-one-package": "1.0.59" }, "config": { "allow-plugins": { diff --git a/modules/legacy/components/admin.php b/modules/legacy/components/admin.php index ffc15fd2..6cefb2d7 100644 --- a/modules/legacy/components/admin.php +++ b/modules/legacy/components/admin.php @@ -37,102 +37,71 @@ public function admin_notices() { ?> -
-
-
- - - - - - - -
- -
-

-

- .

-
- +
+
+ + + + + + + +
+
+

+

+ .

- +
diff --git a/modules/legacy/notices/sticky-deprecated-nag.php b/modules/legacy/notices/sticky-deprecated-nag.php index 0911d3cb..9d98d1d7 100644 --- a/modules/legacy/notices/sticky-deprecated-nag.php +++ b/modules/legacy/notices/sticky-deprecated-nag.php @@ -20,7 +20,7 @@ class Sticky_Deprecated_Nag extends Notice_Base { public string $id = 'sticky-deprecated-nag'; public function content(): string { - return sprintf( '

%s

%s%s

%s

', + return sprintf( '

%s

%s%s

%s

', __( 'New accessibility plugin available!', 'pojo-accessibility' ), __( 'Your current plugin is no longer supported. Switch to Ally - Web Accessibility now to access more customization, control, and tools for a more inclusive site.', 'pojo-accessibility' ), Upgrade::get_learn_more_link( 'acc-notice-switch-oc' ), // link to learn more diff --git a/modules/settings/module.php b/modules/settings/module.php index 02ea3526..106c7867 100644 --- a/modules/settings/module.php +++ b/modules/settings/module.php @@ -632,6 +632,7 @@ public function register_notices( Notices $notice_manager ) { $notices = [ 'Quota_80', 'Quota_100', + 'Renewal_Notice', ]; foreach ( $notices as $notice ) { diff --git a/modules/settings/notices/quota-100.php b/modules/settings/notices/quota-100.php index 75760718..0f02beec 100644 --- a/modules/settings/notices/quota-100.php +++ b/modules/settings/notices/quota-100.php @@ -23,8 +23,8 @@ class Quota_100 extends Notice_Base { public function content(): string { $plan = Settings::get( Settings::PLAN_DATA )->plan->name; - if ( $plan === 'Free' ) { - return sprintf( '

%s

%s

%s

', + if ( 'Free' === $plan ) { + return sprintf( '

%s

%s

%s

', __( 'You’ve reached your free plan limit', 'pojo-accessibility' ), __( 'Upgrade to scan more pages, unlock AI fixes, and access all accessibility features.', 'pojo-accessibility' ), SettingsModule::get_upgrade_link( 'acc-100-quota' ), @@ -32,7 +32,7 @@ public function content(): string { ); } - return sprintf( '

%s

%s

%s

', + return sprintf( '

%s

%s

%s

', __( 'You’ve reached your monthly plan usage', 'pojo-accessibility' ), __( 'Upgrade now to raise your limit and maintain complete access to all accessibility features.', 'pojo-accessibility' ), SettingsModule::get_upgrade_link( 'acc-100-quota' ), diff --git a/modules/settings/notices/renewal-notice.php b/modules/settings/notices/renewal-notice.php new file mode 100644 index 00000000..0eff1046 --- /dev/null +++ b/modules/settings/notices/renewal-notice.php @@ -0,0 +1,225 @@ +diff( $given_date ); + $this->days_diff = $interval->invert ? -$interval->days : $interval->days; + return $this->days_diff; + } + + private function get_notice_icon( $type ) { + if ( 'warning' === $type ) { + return ' + + '; + } + if ( 'error' === $type ) { + return ' + + '; + } + return ''; + } + + public function get_renewal_text(): array { + if ( $this->days_diff <= 30 && $this->days_diff > 0 ) { + return [ + 'title' => esc_html__( 'Ally Subscription Ending Soon!', 'pojo-accessibility' ), + 'description' => esc_html__( 'Renew now to keep access to Ally Assistant and continue improving your website\'s accessibility with guided fixes and scans.', 'pojo-accessibility' ), + 'btn' => esc_html__( 'Enable Auto-Renew', 'pojo-accessibility' ), + 'link' => esc_url( SettingsModule::get_upgrade_link( 'acc-renew-30' ) ), + 'type' => 'warning', + ]; + } + if ( $this->days_diff <= 0 && $this->days_diff > -7 ) { + return [ + 'title' => esc_html__( 'Your Ally subscription has expired', 'pojo-accessibility' ), + 'description' => esc_html__( 'Ally Assistant is no longer active. Renew now to resume accessibility scans and step by step fixes for your site.', 'pojo-accessibility' ), + 'btn' => esc_html__( 'Renew Now', 'pojo-accessibility' ), + 'link' => esc_url( SettingsModule::get_upgrade_link( 'acc-renew-expire' ) ), + 'type' => 'error', + ]; + } + return [ + 'title' => esc_html__( "It's not too late - renew Ally", 'pojo-accessibility' ), + 'description' => esc_html__( "Reactivate your subscription to restore Ally Assistant and continue improving your website's accessibility.", 'pojo-accessibility' ), + 'btn' => esc_html__( 'Reactivate Now', 'pojo-accessibility' ), + 'link' => esc_url( SettingsModule::get_upgrade_link( 'acc-renew-post-expire' ) ), + 'type' => 'error', + ]; + } + + /** + * Inner HTML for the renewal notice (icon + title + description + CTA). + */ + public function content(): string { + if ( null === $this->days_diff ) { + return ' '; + } + $text = $this->get_renewal_text(); + $this->type = $text['type']; + $icon = $this->get_notice_icon( $text['type'] ); + return sprintf( + '
%s%s%s
', + $icon, + $text['title'], + $text['description'], + esc_url( $text['link'] ), + $text['btn'] + ); + } + + /** + * Custom wrapper for renewal notice with dismiss attributes. + */ + public function render() { + $text = ( null !== $this->days_diff ) ? $this->get_renewal_text() : [ 'type' => $this->type ]; + $type_attr = isset( $text['type'] ) ? $text['type'] : $this->type; + printf( + '
%6$s
', + esc_attr__( 'Notice', 'pojo-accessibility' ), + esc_attr( $type_attr ), + esc_attr( $this->get_id() ), + esc_attr( $this->get_action_name() ), + esc_attr( wp_create_nonce( $this->get_action_name() ) ), + $this->content() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ); + } + + public function is_dismissed(): bool { + $user_id = get_current_user_id(); + if ( ! $user_id ) { + return false; + } + $dismissed_at = get_user_meta( $user_id, self::DISMISSED_AT_USER_META, true ); + if ( ! $dismissed_at || ! is_numeric( $dismissed_at ) ) { + return false; + } + return ( time() - (int) $dismissed_at ) < DAY_IN_SECONDS; + } + + public function dismiss_per_user(): void { + $user_id = get_current_user_id(); + if ( ! $user_id ) { + wp_send_json_error( [ 'message' => 'Invalid user' ] ); + } + update_user_meta( $user_id, self::DISMISSED_AT_USER_META, (string) time() ); + } + + public function maybe_set_conditions() { + if ( ! Connect::is_connected() || ! Utils::user_is_admin() ) { + return; + } + + $info = Settings::get( Settings::PLAN_DATA ); + + if ( empty( $info ) || is_wp_error( $info ) ) { + SettingsModule::refresh_plan_data(); + $info = Settings::get( Settings::PLAN_DATA ); + } + + if ( empty( $info ) || ! isset( $info->plan->next_cycle_date ) ) { + return; + } + + if ( isset( $info->plan->features->retention ) && 'None' !== $info->plan->features->retention ) { + return; + } + + if ( $this->date_diff_from_current( $info->plan->next_cycle_date ) > 30 ) { + return; + } + + if ( ! Utils::is_wp_dashboard_page() && ! Utils::is_wp_settings_page() && ! Utils::is_plugin_page() ) { + return; + } + + $this->conditions = true; + $this->days_diff = $this->date_diff_from_current( $info->plan->next_cycle_date ); + } + + /** + * Enqueue notice styles on screens where the renewal notice may appear. + * Must run on admin_enqueue_scripts so the stylesheet is output in the page head. + */ + public function enqueue_styles() { + if ( ! Utils::is_wp_dashboard_page() && ! Utils::is_wp_settings_page() && ! Utils::is_plugin_page() ) { + return; + } + wp_enqueue_style( + 'ea11y-renewal-notice', + EA11Y_ASSETS_URL . 'css/notice.css', + [], + EA11Y_VERSION + ); + } + + /** + * @throws \Exception + */ + public function __construct() { + add_action( 'current_screen', [ $this, 'maybe_set_conditions' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] ); + parent::__construct(); + } +} diff --git a/package-lock.json b/package-lock.json index cf6da5e3..a83f4cc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "pojo-accessibility", - "version": "4.1.0", + "version": "4.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pojo-accessibility", - "version": "4.1.0", + "version": "4.1.1", "dependencies": { "@elementor/design-tokens": "^1.1.4", - "@elementor/elementor-one-assets": "0.4.24", + "@elementor/elementor-one-assets": "0.4.32", "@elementor/icons": "^1.65.0", "@elementor/ui": "^1.37.3", "@emotion/cache": "^11.14.0", @@ -2206,9 +2206,9 @@ "license": "GPL-3.0-or-later" }, "node_modules/@elementor/elementor-one-assets": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/@elementor/elementor-one-assets/-/elementor-one-assets-0.4.24.tgz", - "integrity": "sha512-bUKCt23db2SPgZEDGWosiXVsJVSGZ6ZzGhtDzRthyi8yYZhr0MOrXACZ5OJt4INdSohWWpTypWKxsaK3EE9T2g==", + "version": "0.4.32", + "resolved": "https://registry.npmjs.org/@elementor/elementor-one-assets/-/elementor-one-assets-0.4.32.tgz", + "integrity": "sha512-SP1RIK0cGlgYCICh3UGXivVFe8lZg7TscLdHHLA4kVr/AKc5/FqFX1E6UCfa27YoL6bC6Qw/lvzu1+txWRBvGA==", "dependencies": { "@elementor/icons": "^1.60.0", "@elementor/ui": "1.37.2", diff --git a/package.json b/package.json index 66f8397d..d4adb50e 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,7 @@ { "name": "pojo-accessibility", - "slug": "pojo-accessibility", - "homepage": "http://pojo.me/", "description": "", - "version": "4.1.0", + "version": "4.1.1", "scripts": { "build": "NODE_ENV=production wp-scripts build", "start": "NODE_ENV=development wp-scripts start", @@ -47,7 +45,7 @@ }, "dependencies": { "@elementor/design-tokens": "^1.1.4", - "@elementor/elementor-one-assets": "0.4.24", + "@elementor/elementor-one-assets": "0.4.32", "@elementor/icons": "^1.65.0", "@elementor/ui": "^1.37.3", "@emotion/cache": "^11.14.0", diff --git a/pojo-accessibility.php b/pojo-accessibility.php index 53e70e4f..2f68c6ee 100644 --- a/pojo-accessibility.php +++ b/pojo-accessibility.php @@ -5,8 +5,18 @@ * 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: 4.1.0 + * Version: 4.1.1 * Text Domain: pojo-accessibility + * + * Ally is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * Ally is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ if ( ! defined( 'ABSPATH' ) ) { @@ -15,7 +25,7 @@ // Legacy define( 'POJO_A11Y_CUSTOMIZER_OPTIONS', 'pojo_a11y_customizer_options' ); -define( 'EA11Y_VERSION', '4.1.0' ); +define( 'EA11Y_VERSION', '4.1.1' ); 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 e65060c2..e298405e 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.9 Requires PHP: 7.4 -Stable tag: 4.1.0 +Stable tag: 4.1.1 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. @@ -191,6 +191,11 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro 7. Scanner dashboard: Track your site’s accessibility scans, monitor open issues, and follow progress over time. == Changelog == + += 4.1.1 – 2026-03-31 = +* Tweak: Improved WPML support +* Tweak: WordPress 7.0 design compatibility + = 4.1.0 – 2026-02-23 = * New: Added bulk remediation flow to generate AI alt text or mark multiple images as decorative * Tweak: Added the ability to disable the accessibility widget to prevent it from loading on your site