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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ serve it, and open each example in it, checking that the status line reaches
`running` and that the app frame contains something:

```sh
npm run runnable -- --json > /tmp/runnable.json # 63 examples, ABAP included
npm run runnable -- --json > /tmp/runnable.json # 64 examples, ABAP included
git clone https://github.com/abap2UI5/playground && cd playground
npm ci && npm run build # the first build is a few minutes
RUNNABLE_JSON=/tmp/runnable.json npm test -- docs-examples
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ npm run docs:dev # the site at localhost, with hot reload, while writing
npm run check # everything CI runs on a pull request
```

`npm run check` is `check.yml`: the test suite, then the VitePress build, then
the gates that hold the pages to what the other repositories actually ship —
`npm run check` is `check.yml`: the test suite, then the site build (and the
VitePress build as a second opinion), then the gates that hold the pages to what the other repositories actually ship —
the version the site names, the examples it prints, the Run buttons the
playground can honour, the API names and the sample links. A page that quotes
another repository is checked against that repository, not against memory,
Expand Down
23 changes: 14 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,25 @@ npm run check # what CI runs, all fifteen steps

A documentation repository has no compiler for its prose, but fifteen things in
it are decidable, and `npm run check` decides all fifteen before a merge — the
prose builds (`docs:build`), the four bars are still made of the same palette,
site builds (`build`, the pages as they are published, and `docs:build`, the
VitePress second opinion), the four bars are still made of the same palette,
type and radii as the playground's (`check:design`), every link into a
neighbouring site on this origin
— the playground, the catalogue — still leads there rather than to this site's
own 404 (`check:cross-site`), the fenced ABAP examples compile and the views
they build name real UI5 API (`check:examples`), those examples are written
in the same house style as the sample corpora — chain layout and class shell —
(`check:conventions`), every `client->` name and
`cs_*` constant the prose and snippets mention still exists in the release the
site names (`check:api-names`), the generated client API reference still
matches the interface at that release (`check:api-reference`), the sample
`cs_*` constant the prose and snippets mention still exists in the framework
on `main` (`check:api-names`), the generated client API reference still
matches the interface there (`check:api-reference`), the sample
links still match the sample repositories (`check:samples`), every complete
app example carries a playground Run button or its declared reason not to
(`check:playground`), the release
number in the nav bar still matches the framework (`check:version`), and the
number in the nav bar still matches the framework (`check:version`), every
image is the format and the size a page can afford (`check:images`), a page
that is wrapped stays wrapped (`check:line-length`), every prose word is one
a dictionary or the project's own list knows (`check:vocabulary`), and the
catalogue parser still parses (`test`).
`.github/workflows/check.yml` runs the same list in the same order, so a green
`npm run check` locally is a green pull request — and `deploy.yml` runs it
Expand All @@ -53,7 +57,7 @@ changing anything beyond prose.

The bar is the mark, then the four sections — **Home**, **Documentation**,
**Samples**, **Playground** — then one search box, then the project's links.
The two middle sections are this site; Samples and Playground are the two
The first two sections are this site; Samples and Playground are the two
neighbouring deployments on the same origin, and all four bars across them are
kept identical by hand.

Expand Down Expand Up @@ -83,7 +87,7 @@ than on the site.

The client API is published for the same reader as one JSON document,
[client-api.json](https://abap2ui5.github.io/docs/api/client-api.json) —
generated from `z2ui5_if_client` at the pinned release by
generated from `z2ui5_if_client` on the framework's `main` by
`npm run generate:api`, committed, and held fresh by `check:api-reference`.
The human-readable half is the [Client API page](https://abap2ui5.github.io/docs/resources/api.html).

Expand All @@ -103,8 +107,9 @@ Which blocks get a button is decided in `docs/.vitepress/playground.mjs`, and
the rule is narrow on purpose — a button on an example that cannot run is worse
than no button. It has to be a complete class implementing `z2ui5_if_app` that
displays something and needs nothing the browser has not got: no table of its
own, no CDS entity, no add-on repository, no on-premise SAP class. **38 of the
261 ABAP blocks here** clear that today. Every rule was written from an example
own, no CDS entity, no add-on repository, no on-premise SAP class. **64 of the
83 complete app classes here** clear that today (`npm run check:playground`
prints the current count). Every rule was written from an example
watched failing in a real playground; `test/playground.test.mjs` keeps one
fixture per shape, and [AGENTS.md](AGENTS.md) says how to redo the measurement.

Expand Down
16 changes: 10 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ deployed by merging it.

## What this site is, from a security point of view

- **There is no server.** The site is static files built by VitePress and
served from GitHub Pages. No backend, no SAP system, no account, no session,
- **There is no server.** The site is static files, written by
`scripts/build-site.mjs` and served from GitHub Pages. No backend, no SAP system, no account, no session,
and nothing a reader types is sent anywhere.
- **The one thing that executes is the Run button.** A fenced example that
carries one links into
Expand All @@ -34,15 +34,19 @@ deployed by merging it.
checked against, `abap2UI5` and its sibling repositories at a pinned release.
A wrong statement here is usually wrong there first — but report it here if
that is where you found it.
- **Dependencies are pinned**, and the generators fetch only from
`raw.githubusercontent.com` at that pin.
- **Dependencies are pinned.** What the build fetches beyond them is the
organisation's own: the framework's interface and the sample catalogues from
`raw.githubusercontent.com`, and the bar, two stylesheets, the search box and
the ABAP highlighter from the playground's published site
(`abap2ui5.github.io/playground`), which the build runs to colour the
examples - so a compromise of that deployment would reach this build.

## Out of scope

- A page that is out of date, incomplete, or wrong about the framework. That is
a documentation bug and belongs in
[an issue](https://github.com/abap2UI5/docs/issues) — it is what the eight
checks in [AGENTS.md](AGENTS.md) exist to catch, so a report that names one
[an issue](https://github.com/abap2UI5/docs/issues) — it is what the fifteen
gates in [AGENTS.md](AGENTS.md) exist to catch, so a report that names one
they missed is genuinely useful.
- Anything a reader does to their own browser tab with code they wrote
themselves.
2 changes: 1 addition & 1 deletion docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default defineConfig({
["link", { rel: "shortcut icon", href: "/docs/favicon.ico" }],
[
"link",
{ rel: "apple-touch-icon", sizes: "180x180", href: "/docs/favicon.ico" },
{ rel: "apple-touch-icon", sizes: "180x180", href: "/docs/apple-touch-icon.png" },
],
// THE TYPE. One file, from this origin, and preloaded because it is what
// the first line of every page is set in — a `@font-face` is only
Expand Down
4 changes: 2 additions & 2 deletions docs/.vitepress/playground.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const DECLARED_NAME = /^\s*(?:CLASS|INTERFACE)\s+([a-zA-Z_]\w*)\s+(?:DEFINITION|
const MAX_NAME = 30;

/** Something has to arrive on screen. */
const DISPLAYS_SOMETHING = /->\s*(?:view|popup|message_box|message_toast|nest_view)[a-z_]*_display\s*\(/i;
const DISPLAYS_SOMETHING = /->\s*(?:view|popup|popover|message_box|message_toast|nest_view)[a-z_]*_display\s*\(/i;

/* The database in the page holds the framework's own tables and what open-abap
* ships. A business table is not among them, and `SELECT` from one is the
Expand Down Expand Up @@ -206,7 +206,7 @@ function tablesUsed(code) {
function unimplementedMethods(code) {
const definition = code.split(CLASS_IMPLEMENTATION)[0];
const declared = [];
for (const m of definition.matchAll(/^\s*METHODS:?\s+([^.]+)\./gim)) {
for (const m of definition.matchAll(/^\s*(?:CLASS-)?METHODS:?\s+([^.]+)\./gim)) {
for (const one of m[1].split(',')) {
const name = /^\s*([a-z_]\w*)/i.exec(one)?.[1];
/* A redefinition or an interface method is implemented under another
Expand Down
13 changes: 8 additions & 5 deletions docs/.vitepress/theme/SiteNav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const onHome = computed(() => page.value.relativePath === "index.md");
* section. */
const samplesHref = ref(SAMPLES);
const docsHref = ref(DOCS);
/* And the Playground's, which was bound in the markup and declared nowhere:
* the item rendered without an href, and leave( ) then sent it to the
* catalogue - the one branch it had for anything that was not the manual. */
const playgroundHref = ref(PLAYGROUND);

/* Documentation restores WHEREVER IN THE MANUAL you were, which is why it
* passes a scope: the link is written at the manual's first page, and a stored
Expand All @@ -65,6 +69,7 @@ const docsHref = ref(DOCS);
const lift = () => {
samplesHref.value = lastVisited("samples", SAMPLES);
docsHref.value = lastVisited("docs", DOCS, HOME);
playgroundHref.value = lastVisited("playground", PLAYGROUND);
};
/* The lift that cannot be missed: on the click itself, on the element, because
* a ref set in the handler reaches the DOM a tick too late.
Expand All @@ -85,11 +90,9 @@ const leave = (e) => {
* in theme/index.js is throttled, and a click that lands inside its window
* would otherwise store an offset from before the last scroll. */
rememberScroll();
if (el.dataset.site) {
el.href = el.dataset.site === "docs"
? lastVisited("docs", DOCS, HOME)
: lastVisited("samples", SAMPLES);
}
if (el.dataset.site === "docs") el.href = lastVisited("docs", DOCS, HOME);
else if (el.dataset.site === "samples") el.href = lastVisited("samples", SAMPLES);
else if (el.dataset.site === "playground") el.href = lastVisited("playground", PLAYGROUND);
handOff(el.href);
};

Expand Down
8 changes: 7 additions & 1 deletion docs/.vitepress/theme/playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ function loader() {
window.abap2ui5Embed
? resolve(window.abap2ui5Embed)
: reject(new Error('The playground loader did not install itself.')));
script.addEventListener('error', () => reject(new Error(`${LOADER} could not be loaded.`)));
script.addEventListener('error', () => {
/* Forgotten, not kept: a load that failed once (offline for a moment)
* must not answer every later press on the page with the same error. */
loading = undefined;
script.remove();
reject(new Error(`${LOADER} could not be loaded.`));
});
document.head.append(script);
});
return loading;
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/theme/search-engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ export function grouped(hits, { perGroup = 8 } = {}) {
return groups.map(({ label, hits: rows, total }) => ({ label, hits: rows, total }));
}

/** The index, fetched once. Callers await this on the first keystroke, never
/** The index, fetched once. Callers await this when the box is opened, never
* at load: a reader who does not search pays nothing for the box. */
let pending = null;
export function loadIndex(url = INDEX_URL, { fetchFn = globalThis.fetch } = {}) {
Expand Down
5 changes: 4 additions & 1 deletion docs/.vitepress/theme/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2366,7 +2366,10 @@
.Layout a:focus-visible,
.Layout button:focus-visible,
.Layout summary:focus-visible,
.Layout input:focus-visible,
.Layout input:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}

/* ---- a number beside every line, and an address for it ----
*
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced/extensibility/frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Open VS Code (or your editor of choice), then run in the terminal:
git clone https://github.com/abap2UI5/abap2UI5
cd abap2UI5/app
```
Next replace `<<system>>` in the three config files — `ui5.yaml`, `ui5-local.yaml` and `ui5-mock.yaml` — so they all point to your ABAP backend system:
Next replace `<<system>>` in the two config files — `ui5.yaml` and `ui5-local.yaml` — so both point to your ABAP backend system:

<img width="400" height="319" alt="Frontend config files where the backend system URL must be replaced" src="https://github.com/user-attachments/assets/155c9a3f-8a0a-494b-8fc4-a4bba2bf0e90">

Expand Down
4 changes: 2 additions & 2 deletions docs/advanced/extensibility/user_exits.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ outline: [2, 4]
abap2UI5 offers predefined user exits for tweaking the standard behavior. The interface [`Z2UI5_IF_UI5_EXIT`](https://github.com/abap2UI5/abap2UI5/blob/main/src/02/z2ui5_if_ui5_exit.intf.abap) exposes the user exits. To use them on your system, build a class that implements the interface and its methods. The abap2UI5 class [`Z2UI5_CL_UI5_USER_EXIT`](https://github.com/abap2UI5/abap2UI5/blob/main/src/01/04/z2ui5_cl_ui5_user_exit.clas.abap) calls them dynamically. Put your class in a custom package — **not** in the abap2UI5 packages.

The interface exposes two exit methods:
- **`set_config_http_get`** — called on the initial HTTP GET request (page load). Use it to set frontend properties like the UI5 theme, the UI5 version, or the inline CSS.
- **`set_config_http_get`** — called on the initial HTTP GET request (page load), and again per response for its `t_security_header`, which every response carries. Use it to set frontend properties like the UI5 theme, the UI5 version, or the inline CSS.
- **`set_config_http_post`** — called on every later HTTP POST request (each roundtrip). Use it to set backend behavior like the draft expiration time.

Both methods take a `cs_config` changing parameter whose fields you can set as needed. The example below changes the theme and how long the backend keeps drafts:
Expand All @@ -23,7 +23,7 @@ CLASS zcl_a2ui5_user_exit IMPLEMENTATION.

METHOD z2ui5_if_ui5_exit~set_config_http_get.

cs_config-theme = `sap_belize`.
cs_config-theme = `sap_horizon_dark`.

ENDMETHOD.

Expand Down
10 changes: 6 additions & 4 deletions docs/advanced/insights/12-where-your-own-javascript-goes.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ load. Write it as a backtick literal rather than a string template: a template
has to escape every brace, and one missed escape is a parser error on the whole
statement instead of a wrong string. Ask how we know.

**Everything else is a system decision, not an app decision.** Extra JavaScript
for the initial page is `custom_js` in the HTTP GET configuration, set in the
[user exit](/advanced/extensibility/user_exits) through `z2ui5_if_ui5_exit` —
one place, reviewable, and the same for every app in the system.
**Everything else is a system decision, not an app decision.** Frontend code
of your own is a custom control in the customer frontend BSP (`z2ui5_ccc`),
and what the initial page loads is the bootstrap the
[user exit](/advanced/extensibility/user_exits) sets through
`z2ui5_if_ui5_exit` — one place, reviewable, and the same for every app in
the system.

None of them lets an app change the framework, and none requires the framework
to change for an app. No plugin registry to learn, and no pull request to wait
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced/insights/22-who-may-start-which-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ with the refusal branch and what it should say, is on
[Authorization](/configuration/authorization).

What the framework itself brings to the table is the part a web application
needs and a report never did. A CSRF token on every POST, on by default. A
needs and a report never did. A CSRF check on every POST, on by default. A
Content Security Policy on the first page, restricting what the browser will
load, tightened in the user exit when an old UI5 release no longer has to be
served. Error details off in production, as
Expand Down
15 changes: 8 additions & 7 deletions docs/advanced/renaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ alone would let them collide; the segment is what keeps them apart.
| `smpc` | [samples-controls](https://github.com/abap2UI5/samples-controls) — control samples |
| `popup` | [popups](https://github.com/abap2UI5-addons/popups) — the popups [add-on](/resources/addons) |
| `cci` | [custom-controls](https://github.com/abap2UI5-addons/custom-controls) — custom controls shared with the community |
| `ccc` | [custom-controls-customer](https://github.com/abap2UI5/custom-controls-customer) — the template for **your own** frontend artifacts |
| `ccc` | [custom-controls-customer](https://github.com/abap2UI5-addons/custom-controls-customer) — the template for **your own** frontend artifacts |

The two tables share one rule and one exception. The rule: an object's segment
tells you which repository it was pulled from. The exception: frozen code keeps
Expand Down Expand Up @@ -106,7 +106,7 @@ hierarchy in your system. For the framework repository that is:
|---|---|
| `src/00/` | External libraries — ajson, S-RTTI and the vendored context/HTTP helpers |
| `src/01/` | Internal use only — draft persistence, request handling, event routing, binding, and the generated frontend |
| `src/02/` | The released API — the six objects above |
| `src/02/` | The released API — the five objects above |
| `src/99/` | Frozen — see [Deprecations](/resources/deprecations) |

The package a class sits in is the honest answer to "may I use this?": `src/02`
Expand Down Expand Up @@ -191,13 +191,13 @@ instead of `z2ui5_if_app`.
}
```

The renamed copy is a complete, installable abapGit project under your own namespace — install it side by side with the original, pin it to a release, or ship it inside your product. The abap2UI5 CI runs this transformation on every change (`npm run rename`, workflow `test_rename.yaml`) to guarantee the codebase stays renameable.
The renamed copy is a complete, installable abapGit project under your own namespace — install it side by side with the original, pin it to a release, or ship it inside your product. The abap2UI5 CI runs this transformation on every change (`npm run rename`, workflow `build-rename.yaml`) to guarantee the codebase stays renameable.

### Step-by-Step Guide
Everything is already set up in the main repository: the on-demand GitHub Action `build_rename` renames all artifacts to a namespace of your choice and pushes the result as a ready-to-install branch. Renaming abap2UI5 takes just two steps:
Everything is already set up in the main repository: the on-demand GitHub Action `build-rename` renames all artifacts to a namespace of your choice and pushes the result as a ready-to-install branch. Renaming abap2UI5 takes just two steps:

1. **Fork** the [abap2UI5 repository](https://github.com/abap2UI5/abap2UI5)
2. **Run the Action** — in your fork, open the *Actions* tab (enable workflows when asked), select the **build_rename** workflow, and start it with your new namespace (a letter followed by letters, digits or underscores, max. 10 characters, e.g., `ZMYUI5`)
2. **Run the Action** — in your fork, open the *Actions* tab (enable workflows when asked), select the **build-rename** workflow, and start it with your new namespace (a letter followed by letters, digits or underscores, max. 10 characters, e.g., `ZMYUI5`)

The workflow runs `abaplint --rename` with the checked-in configuration `.github/abaplint/rename.jsonc` and pushes the renamed sources to the branch `rename_<name>` (e.g., `rename_zmyui5`). The branch contains the complete renamed `src` tree together with a matching `.abapgit.xml` — **pull it with abapGit** into your ABAP system for a parallel installation next to the original.

Expand All @@ -220,9 +220,10 @@ in `CATCH cx_root`, so nothing is reported: the installation just runs with the
default configuration and your [user exit](/advanced/extensibility/user_exits) is
never called — no custom theme, no bootstrap configuration, no CSP override.

::: warning Patch the two literals after renaming
::: warning Patch the literals after renaming
In your renamed branch, change the literals in `get_user_exit_class( )` to your
own namespace (`` `ZMYUI5_IF_EXIT` `` and `` `ZMYUI5_CL_UI5_USER_EXIT` ``). Only
own namespace (`` `ZMYUI5_IF_UI5_EXIT` ``, `` `ZMYUI5_CL_UI5_USER_EXIT` `` and
the `` `ZMYUI5_IF_EXIT` `` fallback beside them). Only
apps that use a user exit are affected — everything else in the renamed
installation works without a change.
:::
Expand Down
Loading
Loading