diff --git a/.github/workflows/bridge-maintenance-checks.yml b/.github/workflows/bridge-maintenance-checks.yml index 708fd6c..f7c711a 100644 --- a/.github/workflows/bridge-maintenance-checks.yml +++ b/.github/workflows/bridge-maintenance-checks.yml @@ -29,6 +29,8 @@ jobs: run: node --test tools/tests/calorieapp_embed_readiness.test.mjs tools/tests/wordpress_site_session.test.mjs - name: Check responsive header correction lifecycle run: node --test tools/tests/wordpress_site_layout.test.mjs + - name: Check floating shortcut destinations and current-page visibility + run: node --test tools/tests/wordpress_site_navigation.test.mjs - name: Verify deterministic plugin package run: | python -m unittest tools.tests.test_build_wordpress_plugin_release diff --git a/contracts/identity-bridge/v1/code-provenance.json b/contracts/identity-bridge/v1/code-provenance.json index f39a642..8e63215 100644 --- a/contracts/identity-bridge/v1/code-provenance.json +++ b/contracts/identity-bridge/v1/code-provenance.json @@ -135,6 +135,16 @@ "declared_distribution_licence": "GPL-2.0-or-later", "clearance_status": "contributor-source-and-similarity-review-pending" }, + { + "path": "assets/calorieapp-logo.svg", + "origin_class": "existing-project-brand-asset-vector-extraction", + "repository_first_add_commit": "7191b0fc150fd5e4c1e928f0ba95282af7b52bf2", + "declared_distribution_licence": "project-mark-no-general-trademark-licence-granted", + "clearance_status": "requested-use-on-existing-project-website-no-expanded-mark-licence", + "source": "https://drive.google.com/file/d/1FMepF9HinYi9Pd5AuR7BSOO6peY5gIPu/view", + "source_file": "calorieappdraft3.ai", + "adaptation": "Original phone-mark vector paths and colours retained; surrounding artboard and border omitted." + }, { "path": "assets/calorieapp-site-layout.css", "origin_class": "project-repository-material", @@ -149,6 +159,13 @@ "declared_distribution_licence": "GPL-2.0-or-later", "clearance_status": "contributor-source-and-similarity-review-pending" }, + { + "path": "assets/calorieapp-site-navigation.js", + "origin_class": "project-repository-material", + "repository_first_add_commit": "7191b0fc150fd5e4c1e928f0ba95282af7b52bf2", + "declared_distribution_licence": "GPL-2.0-or-later", + "clearance_status": "contributor-source-and-similarity-review-pending" + }, { "path": "assets/calorieapp-site-session.js", "origin_class": "project-repository-material", diff --git a/docs/STEP_3_SESSION_NAVIGATION.md b/docs/STEP_3_SESSION_NAVIGATION.md new file mode 100644 index 0000000..fdc209f --- /dev/null +++ b/docs/STEP_3_SESSION_NAVIGATION.md @@ -0,0 +1,66 @@ +# Step 3: session labels and floating shortcuts + +## Requested changes + +- Move the existing authenticated CalorieApp-page logout control from above + the iframe into the XUMM widget. Use “Log out” on all website controls, with + an accessible explanation that the website and app sessions end on this device. +- Hide a floating Home shortcut on Home and a floating CalorieApp shortcut on + CalorieApp, including equivalent URLs without `index.php` or trailing slashes. +- Replace the floating Integrated Exchange shortcut with CalorieApp and the + original transparent phone mark, retaining Brizy's icon size and wrapper. +- Prepare the app's matching label and Open Food Facts contribution footer in + a separate main-based PR: https://github.com/CalorieToken/CalorieApp/pull/129. + +## Maintenance boundary + +Plugin 0.3.22 starts at installed maintenance merge +`6f754c837cf80b05422d347b5a43c512aa3c9dea` (0.3.21). This branch must not be merged +into app main. The full embed controller, PHP authentication methods, accepted +startup URL and session validation are preserved. The site-session script moves +the same button/status nodes after the embed controller has captured them; it +does not register another logout handler for the embedded page. + +The navigation script targets existing Brizy icon links in fixed containers. +It does not rewrite inline exchange content, the header logo, other floating +destinations, or create a new floating bar where one is absent. The live Home +and FAQ pages have the older exchange shortcut; the current CalorieApp page +has no such Brizy shortcuts. Its current-page rule is ready for any matching +shortcuts subsequently added to that template. + +## Logo source + +The transparent SVG uses the nine original phone-mark paths from page 1 of +`calorieappdraft3.ai`, in the CalorieToken Google Drive: +https://drive.google.com/file/d/1FMepF9HinYi9Pd5AuR7BSOO6peY5gIPu/view + +The phone mark is visually matched to `logo app.png`. Vector forms, speaker, +home button, fork, knife and original colours are retained. The artboard, +surrounding square and duplicate fork/knife paths are excluded. There is no +embedded raster image, white background, script or external asset in the SVG. +Its source and mark boundary are recorded in the release provenance and notices. + +## Verification + +The real embed and site-session scripts run together in the existing session +test. After relocation, the captured logout button still requests app logout, +recovers after a timeout, and completes WordPress logout on the accepted iframe +message. The other session tests retain origin/source/locale and retry coverage. +Navigation tests cover Home, CalorieApp, FAQ, URL variants, inline content, +unrelated icons, shared wrappers and invalid external asset configuration. +The existing responsive header tests cover changes in the account card height. + +Run: + +```sh +node --test tools/tests/calorieapp_embed_readiness.test.mjs tools/tests/wordpress_site_session.test.mjs tools/tests/wordpress_site_layout.test.mjs tools/tests/wordpress_site_navigation.test.mjs +php tools/tests/wordpress_site_session_markup.test.php +python -m unittest tools.tests.test_build_wordpress_plugin_release +python tools/build_wordpress_plugin_release.py --expected-version 0.3.22 +``` + +CI also lints plugin PHP. The original vector extraction is rendered and +visually inspected locally. No candidate is installed by this PR. Native +desktop/mobile rendering of the candidate follows the user's installation +approval; synthetic DOM checks are not a live browser rendering claim. +The previously observed small-desktop card clipping remains a separate followup. diff --git a/tools/build_wordpress_plugin_release.py b/tools/build_wordpress_plugin_release.py index 05aea30..d131fa4 100644 --- a/tools/build_wordpress_plugin_release.py +++ b/tools/build_wordpress_plugin_release.py @@ -28,7 +28,7 @@ "THIRD_PARTY_NOTICES.md", f"{PLUGIN_SLUG}.php", ) -RELEASE_GLOBS = ("includes/*.php", "assets/*.css", "assets/*.js", "config/*.json") +RELEASE_GLOBS = ("includes/*.php", "assets/*.css", "assets/*.js", "assets/*.svg", "config/*.json") FORBIDDEN_SUFFIXES = ( ".bak", ".db", diff --git a/tools/tests/calorieapp_embed_readiness.test.mjs b/tools/tests/calorieapp_embed_readiness.test.mjs index 71ce50e..281273f 100644 --- a/tools/tests/calorieapp_embed_readiness.test.mjs +++ b/tools/tests/calorieapp_embed_readiness.test.mjs @@ -12,6 +12,14 @@ function element(hidden = true) { const listeners = {}; return { hidden, + children: [], + appendChild(child) { + if (child.parentElement) { + child.parentElement.children = child.parentElement.children.filter(item => item !== child); + } + this.children.push(child); + child.parentElement = this; + }, href: "", src: "", style: {}, @@ -59,7 +67,7 @@ test("Xaman waits for CalorieApp and verified completion refreshes WordPress", a siteLogoutButton.dataset = { logoutUrl: "https://calorietoken.net/wp-login.php?action=logout&redirect_to=calorieapp", - idleLabel: "Log out of website and CalorieApp", + idleLabel: "Log out", }; siteLogoutButton.textContent = siteLogoutButton.dataset.idleLabel; const siteLogoutStatus = element(true); @@ -386,6 +394,35 @@ test("Xaman waits for CalorieApp and verified completion refreshes WordPress", a assert.equal(fetchCalls.at(-1), "/authorize"); assert.equal(status.textContent, "CalorieApp startup failed"); + // WordPress queues the site controller after the embed controller. Exercise + // both real scripts: the existing button must keep its handler after moving. + const actions = element(false); + actions.appendChild(siteLogoutButton); + actions.appendChild(siteLogoutStatus); + const embedShell = element(false); + embedShell.appendChild(actions); + selectors.set(".calorieapp-site-session-actions", actions); + const config = { dataset: { + appPage: "https://calorietoken.net/index.php/calorieapp/", + frameSrc: appOrigin + "/?embedded=1&locale=nl", + appOrigin, locale: "nl", + } }; + document.querySelector = (selector) => ({ + "[data-calorieapp-site-integration]": config, + "[data-calorieapp-embed]": root, + ".xl-card": legacySigninCard, + })[selector] ?? null; + const siteSource = await readFile(new URL( + "../../wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-session.js", + import.meta.url + ), "utf8"); + vm.runInNewContext(siteSource, { + document, window, URL, + fetch() { throw new Error("Relocating logout must not create another session flow"); }, + }); + assert.equal(legacySigninCard.children[0], actions); + assert.equal(embedShell.children.length, 0, "remove the separate control above the iframe"); + const logoutRequest = { data: { type: "calorieapp:logout:request", locale: "nl" }, origin: appOrigin, diff --git a/tools/tests/wordpress_site_navigation.test.mjs b/tools/tests/wordpress_site_navigation.test.mjs new file mode 100644 index 0000000..9208d75 --- /dev/null +++ b/tools/tests/wordpress_site_navigation.test.mjs @@ -0,0 +1,121 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import vm from "node:vm"; + +const source = await readFile(new URL( + "../../wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-navigation.js", + import.meta.url +), "utf8"); +const site = "https://calorietoken.net"; +const app = `${site}/index.php/calorieapp/`; +const logo = `${site}/wp-content/plugins/calorieapp-identity-bridge/assets/calorieapp-logo.svg`; + +function harness({ pathname = "/index.php/faq/", embedded = false, isHome = "0", appLogo = logo } = {}) { + const config = { dataset: { homePage: `${site}/`, appPage: app, appLogo, isHome } }; + const body = {}; + const floatingWrapper = { parentElement: body, position: "fixed", hidden: false }; + const inlineWrapper = { parentElement: body, position: "relative" }; + function link(href, floating = true) { + const container = { + parentElement: floating ? floatingWrapper : inlineWrapper, + position: "static", hidden: false, + style: { setProperty(name, value) { this[name] = value; } }, + }; + const item = { + href, attributes: {}, + closest() { return container; }, + setAttribute(name, value) { this.attributes[name] = value; }, + querySelector() { return this.icon; }, + icon: { replaceWith(image) { item.image = image; item.icon = image; } }, + }; + return { item, container }; + } + const home = link(`${site}/`); + const legacy = link(`${site}/index.php/integrated-exchange/`); + const currentApp = link(app); + const inline = link(`${site}/index.php/integrated-exchange/`, false); + const inlineHome = link(`${site}/`, false); + const other = link(`${site}/index.php/contact/`); + const foreign = link("https://example.com/index.php/calorieapp/"); + const fragment = link(`${site}/#top`); + const links = [home, legacy, currentApp, inline, inlineHome, other, foreign, fragment]; + let created = 0; + const document = { + body, readyState: "complete", + querySelector(selector) { + if (selector === "[data-calorieapp-site-integration]") return config; + if (selector === "[data-calorieapp-embed]") return embedded ? {} : null; + return null; + }, + querySelectorAll(selector) { + assert.equal(selector, ".brz-icon__container a[href]"); + return links.map(link => link.item); + }, + createElement(tag) { + assert.equal(tag, "img"); + created++; + return { style: {}, attributes: {}, setAttribute(name, value) { this.attributes[name] = value; } }; + }, + }; + const context = { document, URL, window: { + location: { href: site + pathname }, + getComputedStyle(node) { return { position: node.position }; }, + } }; + const run = () => vm.runInNewContext(source, context); + run(); + return { home, legacy, currentApp, inline, inlineHome, other, foreign, fragment, floatingWrapper, run, count: () => created }; +} + +test("Home hides its own floating shortcut and retains the original transparent app mark", () => { + for (const pathname of ["/", "/?campaign=test", "/index.php/"]) { + const h = harness({ pathname }); + assert.equal(h.home.container.hidden, true); + assert.equal(h.home.container.style.display, "none"); + assert.equal(h.floatingWrapper.hidden, false, "a shared wrapper must not hide its other shortcuts"); + assert.equal(h.legacy.item.href, app); + assert.equal(h.legacy.item.image.src, logo); + assert.equal(h.legacy.item.image.style.width, "1em"); + assert.equal(h.legacy.item.image.style.height, "1em"); + assert.equal(h.legacy.item.image.attributes["aria-hidden"], "true"); + assert.equal(h.legacy.item.attributes["aria-label"], "Open CalorieApp"); + } + assert.equal(harness({ pathname: "/welcome/", isHome: "1" }).home.container.hidden, true); +}); + +test("CalorieApp hides both its canonical and former exchange shortcuts, retaining Home", () => { + for (const options of [ + { pathname: "/index.php/calorieapp/" }, + { pathname: "/calorieapp?locale=nl" }, + { pathname: "/app-preview/", embedded: true }, + ]) { + const h = harness(options); + assert.equal(h.legacy.container.hidden, true); + assert.equal(h.currentApp.container.hidden, true); + assert.equal(h.home.container.hidden, false); + assert.equal(h.home.item.attributes["aria-label"], "Go to Home"); + } +}); + +test("FAQ retains both shortcuts; inline content, other icons and fragment links remain intact", () => { + const h = harness(); + assert.equal(h.home.container.hidden, false); + assert.equal(h.currentApp.container.hidden, false); + assert.equal(h.currentApp.item.image.src, logo); + assert.match(h.inline.item.href, /integrated-exchange/); + for (const link of [h.inline, h.inlineHome, h.other, h.foreign, h.fragment]) { + assert.equal(link.container.hidden, false); + assert.equal(link.item.image, undefined); + assert.deepEqual(link.item.attributes, {}); + } + const count = h.count(); + h.run(); + assert.equal(h.count(), count, "reinitialization must not duplicate assets"); +}); + +test("foreign asset configuration cannot rewrite website navigation", () => { + const h = harness({ pathname: "/", appLogo: "https://untrusted.example/logo.svg" }); + assert.equal(h.count(), 0); + assert.equal(h.home.container.hidden, false); + assert.match(h.legacy.item.href, /integrated-exchange/); +}); diff --git a/tools/tests/wordpress_site_session.test.mjs b/tools/tests/wordpress_site_session.test.mjs index 30bcfc4..950d46e 100644 --- a/tools/tests/wordpress_site_session.test.mjs +++ b/tools/tests/wordpress_site_session.test.mjs @@ -38,7 +38,7 @@ function harness({ embedded = false, signedIn = false, storage = new Map(), stor const card = element(); const body = element(); const button = element(); - button.dataset = { logoutUrl: `${site}/wp-login.php?action=logout&_wpnonce=test`, idleLabel: "Sign out both" }; + button.dataset = { logoutUrl: `${site}/wp-login.php?action=logout&_wpnonce=test`, idleLabel: "Log out" }; const status = element(); status.hidden = true; const actions = element(); @@ -51,13 +51,20 @@ function harness({ embedded = false, signedIn = false, storage = new Map(), stor return item; } const appFrame = frame(); - const root = { dataset: { appOrigin: app, locale: "en" }, querySelector: () => appFrame }; + const root = { + dataset: { appOrigin: app, locale: "en" }, + querySelector(selector) { + if (selector === ".calorieapp-embed-frame") return appFrame; + if (selector === ".calorieapp-site-session-actions") return signedIn ? actions : null; + return null; + }, + }; const document = { body, readyState: "complete", querySelector(selector) { if (selector === "[data-calorieapp-site-integration]") return config; if (selector === "[data-calorieapp-embed]") return embedded ? root : null; - if (selector === "[data-calorieapp-sitewide-session-actions]") return signedIn ? actions : null; + if (selector === "[data-calorieapp-sitewide-session-actions]") return signedIn && !embedded ? actions : null; if (selector === ".xl-card") return card; return null; }, diff --git a/tools/tests/wordpress_site_session_markup.test.php b/tools/tests/wordpress_site_session_markup.test.php index c4ad68d..fb91e84 100644 --- a/tools/tests/wordpress_site_session_markup.test.php +++ b/tools/tests/wordpress_site_session_markup.test.php @@ -57,6 +57,8 @@ function check($condition, $message): void { check($enqueued_scripts === ['calorieapp-identity-bridge-site-session'], 'The footer queues only the smaller site controller on ordinary pages.'); check(str_contains($html, 'data-calorieapp-site-integration'), 'Anonymous pages need the common sign-in navigation.'); check(str_contains($html, 'data-locale="en"'), 'The renderer resolves the canonical locale.'); +check(str_contains($html, 'data-home-page="https://calorietoken.net/"'), 'Floating navigation uses the configured website home.'); +check(str_contains($html, 'assets/calorieapp-logo.svg'), 'Floating navigation receives the bundled original vector logo.'); check(str_contains($html, 'https://calorieapp-backend-rvul.onrender.com/health?resume_login=true'), 'Use the existing accepted startup route.'); check(!str_contains($html, 'data-calorieapp-sitewide-session-actions'), 'Anonymous pages must not claim a signed-in session.'); check(!str_contains($html, ' [data-calorieapp-sitewide-session-actions] { +.xl-card > .calorieapp-site-session-actions { margin: 8px 0 0; max-width: 100%; } -.xl-card > [data-calorieapp-sitewide-session-actions] .calorieapp-site-logout { +.xl-card > .calorieapp-site-session-actions .calorieapp-site-logout { min-height: 32px; max-width: 100%; padding: 5px 10px; diff --git a/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-logo.svg b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-logo.svg new file mode 100644 index 0000000..9208244 --- /dev/null +++ b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-logo.svg @@ -0,0 +1,11 @@ + +CalorieApp + + + + + + + + + diff --git a/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-navigation.js b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-navigation.js new file mode 100644 index 0000000..fb03bbb --- /dev/null +++ b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-navigation.js @@ -0,0 +1,100 @@ +(function () { + "use strict"; + + function path(url) { + return url.pathname.replace(/\/index\.php(?=\/|$)/, "").replace(/\/+$/, "") || "/"; + } + + function isFloating(container) { + for (var node = container; node && node !== document.body; node = node.parentElement) { + if (window.getComputedStyle(node).position === "fixed") { + return true; + } + } + return false; + } + + function init() { + var config = document.querySelector("[data-calorieapp-site-integration]"); + if (!config || config.dataset.navigationReady === "1") { + return; + } + var settings = config.dataset; + var homePage; + var appPage; + var appLogo; + var legacyAppPage; + var currentPage = new URL(window.location.href); + try { + homePage = new URL(settings.homePage); + appPage = new URL(settings.appPage); + appLogo = new URL(settings.appLogo); + legacyAppPage = new URL("index.php/integrated-exchange/", homePage); + if ([homePage, appPage, appLogo].some(function (url) { + return url.origin !== currentPage.origin || url.protocol !== "https:" || url.username || url.password; + })) { + return; + } + } catch (_error) { + return; + } + config.dataset.navigationReady = "1"; + var onHome = settings.isHome === "1" || path(currentPage) === path(homePage); + var onApp = !!document.querySelector("[data-calorieapp-embed]") || path(currentPage) === path(appPage); + + document.querySelectorAll(".brz-icon__container a[href]").forEach(function (link) { + var container = link.closest(".brz-icon__container"); + if (!container || !isFloating(container)) { + return; + } + var destination; + try { + destination = new URL(link.href); + } catch (_error) { + return; + } + if (destination.origin !== currentPage.origin || destination.hash || destination.search) { + return; + } + var homeLink = path(destination) === path(homePage); + var appLink = path(destination) === path(appPage) || path(destination) === path(legacyAppPage); + if (!homeLink && !appLink) { + return; + } + if ((homeLink && onHome) || (appLink && onApp)) { + container.hidden = true; + container.style.setProperty("display", "none", "important"); + return; + } + if (homeLink) { + link.title = "Home"; + link.setAttribute("aria-label", "Go to Home"); + return; + } + var icon = link.querySelector(".brz-icon-svg"); + if (!icon) { + return; + } + var image = document.createElement("img"); + image.src = appLogo.href; + image.alt = ""; + image.setAttribute("aria-hidden", "true"); + image.className = "brz-icon-svg"; + image.style.width = "1em"; + image.style.height = "1em"; + image.style.objectFit = "contain"; + image.style.background = "transparent"; + image.style.display = "block"; + icon.replaceWith(image); + link.href = appPage.href; + link.title = "CalorieApp"; + link.setAttribute("aria-label", "Open CalorieApp"); + }); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-session.js b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-session.js index 1bd2a08..d85f28b 100644 --- a/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-session.js +++ b/wordpress-plugins/calorieapp-identity-bridge/assets/calorieapp-site-session.js @@ -100,6 +100,14 @@ } } + // The embed controller initializes first and retains its existing button + // and status references. Move those same nodes without adding a logout flow. + var card = document.querySelector(".xl-card"); + var embeddedActions = root && root.querySelector(".calorieapp-site-session-actions"); + if (card && embeddedActions) { + card.appendChild(embeddedActions); + } + var actions = document.querySelector("[data-calorieapp-sitewide-session-actions]"); if (!actions) { return; @@ -118,7 +126,6 @@ } catch (_error) { return; } - var card = document.querySelector(".xl-card"); if (card) { card.appendChild(actions); } diff --git a/wordpress-plugins/calorieapp-identity-bridge/calorieapp-identity-bridge.php b/wordpress-plugins/calorieapp-identity-bridge/calorieapp-identity-bridge.php index c1fae2f..a2ab27a 100644 --- a/wordpress-plugins/calorieapp-identity-bridge/calorieapp-identity-bridge.php +++ b/wordpress-plugins/calorieapp-identity-bridge/calorieapp-identity-bridge.php @@ -3,7 +3,7 @@ * Plugin Name: CalorieApp Identity Bridge * Plugin URI: https://calorietoken.net * Description: Companion bridge plugin that maps authenticated WordPress/XUMM Login sessions to short-lived CalorieApp authorization codes. - * Version: 0.3.21 + * Version: 0.3.22 * Author: CalorieApp * License: GPL-2.0-or-later * License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -19,7 +19,7 @@ } if (!defined('CALORIEAPP_IDENTITY_BRIDGE_VERSION')) { - define('CALORIEAPP_IDENTITY_BRIDGE_VERSION', '0.3.21'); + define('CALORIEAPP_IDENTITY_BRIDGE_VERSION', '0.3.22'); } require_once plugin_dir_path(__FILE__) . 'includes/class-calorieapp-identity-bridge.php'; @@ -31,4 +31,5 @@ $assets = plugin_dir_url(__FILE__) . 'assets/'; wp_enqueue_style('calorieapp-identity-bridge-layout', $assets . 'calorieapp-site-layout.css', [], CALORIEAPP_IDENTITY_BRIDGE_VERSION); wp_enqueue_script('calorieapp-identity-bridge-layout', $assets . 'calorieapp-site-layout.js', [], CALORIEAPP_IDENTITY_BRIDGE_VERSION, true); + wp_enqueue_script('calorieapp-identity-bridge-navigation', $assets . 'calorieapp-site-navigation.js', [], CALORIEAPP_IDENTITY_BRIDGE_VERSION, true); }, 20); diff --git a/wordpress-plugins/calorieapp-identity-bridge/includes/class-calorieapp-identity-bridge-integrated-login.php b/wordpress-plugins/calorieapp-identity-bridge/includes/class-calorieapp-identity-bridge-integrated-login.php index a4e09a4..7c53166 100644 --- a/wordpress-plugins/calorieapp-identity-bridge/includes/class-calorieapp-identity-bridge-integrated-login.php +++ b/wordpress-plugins/calorieapp-identity-bridge/includes/class-calorieapp-identity-bridge-integrated-login.php @@ -81,7 +81,7 @@ public function register_assets(): void { $base_url = plugin_dir_url(CALORIEAPP_IDENTITY_BRIDGE_FILE); $version = defined('CALORIEAPP_IDENTITY_BRIDGE_VERSION') ? CALORIEAPP_IDENTITY_BRIDGE_VERSION - : '0.3.20'; + : '0.3.22'; wp_register_style( 'calorieapp-identity-bridge-embed', @@ -142,6 +142,9 @@ public function render_site_integration(): void { data-app-origin="url_origin($frontend_url)); ?>" data-frame-src="" data-app-page="" + data-home-page="" + data-is-home="" + data-app-logo="" data-startup-url="" data-locale="" hidden @@ -152,8 +155,10 @@ public function render_site_integration(): void { type="button" class="calorieapp-site-logout" data-logout-url="" - data-idle-label="" - > + data-idle-label="" + title="" + aria-label="" + > @@ -229,8 +234,10 @@ class="calorieapp-embed-shell" type="button" class="calorieapp-site-logout" data-logout-url="" - data-idle-label="" - > + data-idle-label="" + title="" + aria-label="" + >