Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/a11y-admin-nav.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: corrected ARIA semantics on the top admin navigation bar.
5 changes: 5 additions & 0 deletions .changeset/a11y-context-title.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: improvements to the document context title (admin bar middle group) and the underlying `AposContextMenu` machinery.
5 changes: 5 additions & 0 deletions .changeset/a11y-locale-switcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: improve the locale switcher (`AposLocalePicker`).
5 changes: 5 additions & 0 deletions .changeset/a11y-recently-edited-icon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: the Recently Edited Documents tray icon (admin bar) now exposes its action through `aria-label`.
5 changes: 5 additions & 0 deletions .changeset/a11y-sr-only.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: fix `.apos-sr-only` so screen-reader-only content is exposed to the accessibility tree.
5 changes: 5 additions & 0 deletions .changeset/a11y-tray-aria.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Accessibility: icon-only context-utility buttons in the admin bar tray (e.g. the global settings cog) now expose their action through `aria-label`.
5 changes: 5 additions & 0 deletions .changeset/clean-actors-laugh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Fix illegal HTML id attribute values generated by the admin UI
5 changes: 5 additions & 0 deletions .changeset/cozy-wombats-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/cli": patch
---

Security: bump and clean up dependencies. This closes vulnerabilities in `uuid` and `fast-xml-parser` although they were not used in a sensitive or vulnerable way within ApostropheCMS. This also closes a vulnerability in `shelljs` which ould only be exploited if the developer could be convinced to enter malicious commands as part of their CLI input.
7 changes: 7 additions & 0 deletions .changeset/fifty-hornets-follow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"apostrophe": patch
---

- Removed duplicate <meta charset> tag from `outerLayoutBase.html`
- Standardized charset to utf-8 (the legacy configuration option is now ignored). Per the spec this is the only legal setting, so we classify this as a bug fix
- Altered unused/legacy i18n template helper to return `utf-8`, ensuring backwards compatibility
5 changes: 5 additions & 0 deletions .changeset/full-symbols-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Keyboard shortcuts for widget operations (copy, cut, paste, duplicate, remove) no longer block the browser's native clipboard behavior when no widget is focused. Previously, selecting and copying text on a page while logged in was prevented by the admin UI intercepting those shortcuts unconditionally.
5 changes: 5 additions & 0 deletions .changeset/quick-guests-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": minor
---

Fix layout widget not regaining full focus on switching back to Edit content mode.
6 changes: 6 additions & 0 deletions .changeset/seven-emus-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@apostrophecms/apostrophe-astro": minor
"apostrophe": minor
---

Editors can now control the layout-widget gap through the styles system, both site-wide via a global `layoutGap` preset and per widget via a `gap` styles field. New Layout widget option `className` allows for additional CSS class names to be added to the widget Grid container.
5 changes: 5 additions & 0 deletions .changeset/stale-shirts-read.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

fixes issue where orderable table array items drag the entire floating window
15 changes: 15 additions & 0 deletions .changeset/wild-forks-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
"apostrophe": patch
---

apostrophe and oembetter have been updated to eliminate a number of services that formerly supported
oembed for the general public, but no longer do so. While there is no security risk today, removing
these ensures that if these domains are ever allowed to lapse, they do not become an XSS
attack vector in the future.

Because oembed responses are not always iframes, it is important that this list be maintained
over time. In addition, developers always have the option to prune it on their own by setting
the new minimumAllowlist and minimumEndpoints options of the @apostrophecms/oembed module.

Thanks to [Sainithin0309](https://github.com/Sainithin0309) for pointing out the potential
long-term security concern.
5 changes: 5 additions & 0 deletions .changeset/wild-lies-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/apostrophe-astro": minor
---

Log aposResponse errors server side in the Astro process. Thanks to [Harouna Traore](https://github.com/haroun).
5 changes: 4 additions & 1 deletion packages/apostrophe-astro/lib/aposResponse.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default async function aposResponse(req) {

const aposUrl = new URL(aposHost + pathname);
aposUrl.search = url.search;

// Prepare headers, excluding any specified in config
const requestHeaders = {};
for (const [name, value] of req.headers) {
Expand Down Expand Up @@ -111,6 +111,7 @@ export default async function aposResponse(req) {
}
// Skip unknown encodings silently
} catch (decompressError) {
console.error(decompressError);
// If decompression fails, return original response
return new Response(new Uint8Array(bodyArrayBuffer), {
...rest,
Expand All @@ -133,10 +134,12 @@ export default async function aposResponse(req) {
headers: responseHeaders
});
} catch (bodyError) {
console.error(bodyError);
// If we can't process the body, fall back to the original response
return new Response(res.body, { ...rest, status: statusCode, headers: responseHeaders });
}
} catch (error) {
console.error(error);
// Handle any unexpected errors
return new Response(`Server error: ${error.message}`, { status: 500 });
}
Expand Down
32 changes: 27 additions & 5 deletions packages/apostrophe-astro/widgets/LayoutWidget.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,39 @@
const { widget, options } = Astro.props;
import AposArea from "../components/AposArea.astro";
import LayoutColumn from "./LayoutColumn.astro";

const gapMeta = widget._options || {};
const resolvedGap = options._gap ?? gapMeta._gap ?? null;
const gapHasGlobal = options._gapHasGlobal ?? gapMeta._gapHasGlobal ?? false;

// Resolve the inline `--grid-gap` value
const hasWidgetGap = resolvedGap != null;
const omitGridGap = !hasWidgetGap && gapHasGlobal;
const gridGap = hasWidgetGap
? resolvedGap
: omitGridGap
? undefined
: options.gap || "0";

// Mirror the Nunjucks `parentOptionsForArea` helper so the in-place
// layout editor (AposAreaLayoutEditor) sees the same `gap` signal in
// its `data-parent-options` JSON as it would on the Nunjucks side:
// - widget value present → carries through (string with unit).
// - widget value absent + global enabled → `gap: null` (signal omit).
// - otherwise → no `gap` key (use the static module default / BC).
const parentOptions = {
...options,
widgetId: widget._id,
...(hasWidgetGap ? { gap: resolvedGap } : omitGridGap ? { gap: null } : {}),
};
---

<AposArea
area={widget.columns}
aposClassName="layout-widget"
aposStyle={{
"--grid-columns": options.columns,
"--grid-gap": options.gap || "0",
...(gridGap !== undefined ? { "--grid-gap": gridGap } : {}),
"--grid-rows": "auto",
"--mobile-grid-rows": "auto",
"--tablet-grid-rows": "auto",
Expand All @@ -20,9 +45,6 @@ import LayoutColumn from "./LayoutColumn.astro";
"data-tablet-auto": true,
"data-mobile-auto": true,
}}
aposParentOptions={{
...options,
widgetId: widget._id,
}}
aposParentOptions={parentOptions}
widgetComponent={LayoutColumn}
/>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
<nav
ref="adminBar"
:class="classes"
role="menubar"
aria-label="Apostrophe Admin Bar"
>
<div class="apos-admin-bar__row">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<ol
class="apos-admin-bar__items"
role="menu"
>
<ol class="apos-admin-bar__items">
<li
v-if="pageTree"
class="apos-admin-bar__item"
Expand All @@ -12,7 +9,6 @@
label="apostrophe:pages"
class="apos-admin-bar__btn"
:modifiers="['no-motion']"
role="menuitem"
action-test-label="page-manager-button"
@click="emitEvent({ action: '@apostrophecms/page:manager' })"
/>
Expand All @@ -33,7 +29,6 @@
class: 'apos-admin-bar__btn',
type: 'subtle'
}"
role="menuitem"
@item-clicked="emitEvent"
/>
<Component
Expand All @@ -44,7 +39,6 @@
:modifiers="['no-motion']"
class="apos-admin-bar__btn"
:action-test-label="`${item.name}-manager-button`"
role="menuitem"
@click="emitEvent(item)"
/>
</li>
Expand All @@ -62,7 +56,6 @@
type: 'primary',
modifiers: ['round', 'no-motion']
}"
role="menuitem"
@item-clicked="emitEvent"
/>
</li>
Expand All @@ -89,6 +82,7 @@
:label="item.label"
:action="item.action"
:state="trayItemState[item.name] ? [ 'active' ] : []"
:attrs="trayItemAttrs(item)"
@click="emitEvent(item)"
/>
</template>
Expand Down Expand Up @@ -185,6 +179,29 @@ export default {
} else {
return item.options.tooltip;
}
},
// Tray utility buttons render icon-only, so the visible label
// (e.g. "Global Content") is sr-only and doesn't describe what the
// button does. Make them accessible by providing an aria-label based on
// the tooltip content.
trayItemAttrs(item) {
const tooltip = item.options?.tooltip;
let key = null;
if (item.options?.toggle) {
if (this.trayItemState[item.name] && tooltip?.deactivate) {
key = tooltip.deactivate;
} else if (tooltip?.activate) {
key = tooltip.activate;
}
} else if (typeof tooltip === 'string') {
key = tooltip;
} else if (tooltip && typeof tooltip.content === 'string') {
key = tooltip.content;
}
if (!key) {
return {};
}
return { 'aria-label': this.$t(key) };
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
:label="screen.label"
:tooltip="$t(screen.label)"
:title="$t(screen.label)"
:attrs="shortcutAttrs(screen)"
:icon="screen.icon"
:icon-only="true"
type="subtle"
Expand All @@ -36,6 +37,7 @@
:active-item="mode"
:center-on-icon="true"
menu-placement="bottom-end"
:dialog-label="'apostrophe:breakpointPreviewSelectMenu'"
@item-clicked="selectBreakpoint"
/>
<Transition>
Expand Down Expand Up @@ -320,6 +322,13 @@ export default {
},
setShowDropdown() {
this.showDropdown = Object.values(this.screens).some(({ shortcut }) => !shortcut);
},
shortcutAttrs(screen) {
return {
'aria-label': this.$t('apostrophe:breakpointPreviewShortcut', {
breakpoint: this.$t(screen.label)
})
};
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
:disabled="hasCustomUi || isUnpublished"
:center-on-icon="true"
menu-placement="bottom-end"
:dialog-label="'apostrophe:publicationStatusMenu'"
:trigger-aria-label="draftTriggerAriaLabel"
@item-clicked="switchDraftMode"
/>
<AposLabel
Expand Down Expand Up @@ -91,6 +93,13 @@ export default {
type: 'quiet'
};
},
draftTriggerAriaLabel() {
return this.$t('apostrophe:publicationStatusTrigger', {
status: this.$t(
this.draftMode === 'draft' ? 'apostrophe:draft' : 'apostrophe:published'
)
});
},
isUnpublished() {
return !this.context.lastPublishedAt;
},
Expand Down
15 changes: 10 additions & 5 deletions packages/apostrophe/modules/@apostrophecms/area/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,44 @@ module.exports = {
action: {
type: 'command-menu-area-cut-widget'
},
shortcut: 'Ctrl+X Meta+X'
shortcut: 'Ctrl+X Meta+X',
requireWidgetFocus: true
},
[`${self.__meta.name}:copy-widget`]: {
type: 'item',
label: 'apostrophe:commandMenuWidgetCopy',
action: {
type: 'command-menu-area-copy-widget'
},
shortcut: 'Ctrl+C Meta+C'
shortcut: 'Ctrl+C Meta+C',
requireWidgetFocus: true
},
[`${self.__meta.name}:paste-widget`]: {
type: 'item',
label: 'apostrophe:commandMenuWidgetPaste',
action: {
type: 'command-menu-area-paste-widget'
},
shortcut: 'Ctrl+V Meta+V'
shortcut: 'Ctrl+V Meta+V',
requireWidgetFocus: true
},
[`${self.__meta.name}:duplicate-widget`]: {
type: 'item',
label: 'apostrophe:commandMenuWidgetDuplicate',
action: {
type: 'command-menu-area-duplicate-widget'
},
shortcut: 'Ctrl+Shift+D Meta+Shift+D'
shortcut: 'Ctrl+Shift+D Meta+Shift+D',
requireWidgetFocus: true
},
[`${self.__meta.name}:remove-widget`]: {
type: 'item',
label: 'apostrophe:commandMenuWidgetRemove',
action: {
type: 'command-menu-area-remove-widget'
},
shortcut: 'Backspace'
shortcut: 'Backspace',
requireWidgetFocus: true
}
},
modal: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ export default {
apos.bus.$on('widget-focus-parent', this.focusParent);
apos.bus.$on('context-menu-toggled', this.getFocusForMenu);
apos.bus.$on('suppress-focused-widget-controls', this.doSuppressWidgetControls);
apos.bus.$on('clear-focused-widget-control-suppression', this.clearSuppressionFlags);

this.breadcrumbs.$lastEl = this.$el;

Expand Down Expand Up @@ -573,6 +574,7 @@ export default {
// Remove the focus parent listener when unmounted
apos.bus.$off('widget-focus-parent', this.focusParent);
apos.bus.$off('suppress-focused-widget-controls', this.doSuppressWidgetControls);
apos.bus.$off('clear-focused-widget-control-suppression', this.clearSuppressionFlags);
window.removeEventListener('scroll', this.stickyControlsScroll);
window.removeEventListener('resize', this.stickyControlsResize);
this.unregisterFromGraph();
Expand Down
Loading