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
8 changes: 8 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ jobs:
if: ${{ !cancelled() }}
run: npm run check:examples

# every `client->` name on the site - in the prose and in the snippets
# check:examples cannot compile because they are not whole classes -
# against z2ui5_if_client at the release this site names. Four pages
# taught API that 1.143.0 had deleted, and nothing here was red.
- name: API names
if: ${{ !cancelled() }}
run: npm run check:api-names

# the Working Samples blocks: still generated from what the catalogue says
# today, still pointing at classes that exist, and still pointed back at
# from those classes. A link into another repository is the one kind that
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ jobs:
- name: ABAP examples
if: ${{ !cancelled() }}
run: npm run check:examples
- name: API names
if: ${{ !cancelled() }}
run: npm run check:api-names
- name: sample links
if: ${{ !cancelled() }}
run: npm run check:samples
Expand Down
13 changes: 7 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ person reads the page. Do not put "as an AI, …" prose back into `docs/`.
## Build & verify — run before every commit

```bash
npm run check # test + check:version + docs:build + check:examples + check:samples
npm run check # test + check:version + docs:build + check:examples + check:api-names + check:samples
```

A documentation repository has no compiler for its prose, but five things in it
are decidable, and all five are decided before a merge:
A documentation repository has no compiler for its prose, but six things in it
are decidable, and all six are decided before a merge:

| | |
|---|---|
| `test` | the sample-catalogue parser in `scripts/lib/`, against a row of every shape the three sample repositories generate |
| `check:version` | the release number in the nav bar, the deprecations page and the changelog, against the newest release tag of the framework — this one goes stale without anybody touching this repository |
| `docs:build` | a page that does not build is a page nobody can read |
| `check:examples` | the ABAP in the fenced blocks, against the real framework: does it compile, and does the view it builds name controls and properties that exist on the UI5 floor this documentation targets |
| `check:api-names` | every `client->` name on the site — method, parameter, `cs_*` constant — against `z2ui5_if_client` at the release this site names, plus every `blob/main/` link into the framework's tree. `check:examples` compiles the fenced blocks that are whole CLASSES; this is the rest of the page: the sentence, the two-line snippet, the constant block a page reproduces, the source link. Four pages taught API that 1.143.0 had deleted and nothing was red |
| `check:samples` | the **Working Samples** blocks, against [abap2UI5/samples](https://github.com/abap2UI5/samples) |

`.github/workflows/check.yml` runs the same five, in the same order. Keep the
`.github/workflows/check.yml` runs the same six, in the same order. Keep the
two in step: a step that exists only in `package.json` is a step no pull
request has to pass, which is how `npm test` — the pin added *because* the
catalogue parser broke twice in silence — went a release without CI.
Expand All @@ -48,7 +49,7 @@ clone it as a sibling. Without one it *skips* rather than fails, so verify the
output says what you think it says. CI checks out `abap2UI5/samples@main`
explicitly for this reason.

There used to be a sixth, `check:counts`, holding four figures on a
There used to be one more, `check:counts`, holding four figures on a
`resources/samples.md` page against the catalogues themselves. That page is
gone — the home page opens [the samples page](https://abap2ui5.github.io/samples/)
directly and each catalogue introduces itself — and with it the only prose
Expand Down Expand Up @@ -112,7 +113,7 @@ The code travels in the playground's URL fragment, read out of the rendered
block at click time — so nothing is hosted here, and the example that runs is
the text on the page rather than a copy of it.

**This is the sixth decidable thing in this repository and the only one CI
**This is the seventh decidable thing in this repository and the only one CI
cannot decide.** Whether an example runs is a question only a playground can
answer, and a playground is a three-minute build of another repository. So the
rules in `docs/.vitepress/playground.mjs` are an approximation, they fail
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md

All project guidance lives in **[AGENTS.md](AGENTS.md)** — the single source of
truth for this repository (how the site is built, the five gates, the playground rule engine, and what may be written where).
truth for this repository (how the site is built, the six gates, the playground rule engine, and what may be written where).

Read `AGENTS.md` before making any change.
14 changes: 9 additions & 5 deletions docs/advanced/extensibility/user_exits.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ outline: [2, 4]
---
# User Exits

abap2UI5 offers predefined user exits for tweaking the standard behavior. The interface [`Z2UI5_IF_EXIT`](https://github.com/abap2UI5/abap2UI5/blob/main/src/02/z2ui5_if_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.
abap2UI5 offers predefined user exits for tweaking the standard behavior. The interface [`Z2UI5_IF_EXIT`](https://github.com/abap2UI5/abap2UI5/blob/main/src/99/z2ui5_if_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.
Expand Down Expand Up @@ -41,10 +41,14 @@ ENDCLASS.
```

::: tip The interface is being renamed
`z2ui5_if_exit` becomes `z2ui5_if_ui5_exit`, following the framework's naming.
Both work — abap2UI5 looks up both interfaces, so an existing exit keeps
running — and the examples here move to the new name once it is in a release.
See [Deprecations](/resources/deprecations).
`z2ui5_if_exit` becomes
[`z2ui5_if_ui5_exit`](https://github.com/abap2UI5/abap2UI5/blob/main/src/02/z2ui5_if_ui5_exit.intf.abap),
following the framework's naming. Both work — abap2UI5 looks up both
interfaces, so an existing exit keeps running — and the examples here move to
the new name once it is in a release. On `main` the old interface has already
moved to the frozen `src/99` package, which is why the link above points
there; it still ships and is still called. See
[Deprecations](/resources/deprecations).
:::

::: warning The tab title is not set here
Expand Down
4 changes: 2 additions & 2 deletions docs/configuration/authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ CLASS z2ui5_cl_my_http_handler IMPLEMENTATION.
ENDIF.

" Call the abap2UI5 handler
z2ui5_cl_http_handler=>run( server ).
z2ui5_cl_ui5_http_handler=>run( server ).
ENDMETHOD.

ENDCLASS.
Expand Down Expand Up @@ -61,7 +61,7 @@ CLASS z2ui5_cl_my_http_handler IMPLEMENTATION.
ENDIF.

" Call the abap2UI5 handler if authorized
z2ui5_cl_http_handler=>run( server ).
z2ui5_cl_ui5_http_handler=>run( server ).
ENDMETHOD.
ENDCLASS.
```
Expand Down
6 changes: 3 additions & 3 deletions docs/configuration/logout.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ A logout button isn't a single action. Depending on how an abap2UI5 app was star
## The Logout Event
Fire the event from any controller method to log the user off:
```abap
client->_event_client( client->cs_event-system_logout ).
client->follow_up_action( client->cs_event-system_logout ).
```

Optionally pass a same-origin URL as the first argument to control where the user lands afterwards. The default is `/sap/public/bc/icf/logoff`:
```abap
client->_event_client(
client->follow_up_action(
val = client->cs_event-system_logout
t_arg = VALUE #( ( `/sap/public/bsp/sap/system/logoff.htm` ) ) ).
```
Expand All @@ -25,7 +25,7 @@ abap2UI5 can be launched in three ways. Each one creates a different combination
| Context | Typical URL | Fiori Launchpad shell? |
|---|---|---|
| Fiori Launchpad tile | `…/sap/bc/ui2/flp#Z2UI5-display` | yes |
| ICF HTTP handler (e.g. `Z2UI5_CL_HTTP_HANDLER`) | `…/sap/bc/<your-service>` | no |
| ICF HTTP handler (e.g. `Z2UI5_CL_UI5_HTTP_HANDLER`) | `…/sap/bc/<your-service>` | no |
| BSP application (`Z2UI5`, `Z2UI5_V2`) | `…/sap/bc/bsp/sap/z2ui5/index.html` | no |

### Session Layers
Expand Down
34 changes: 31 additions & 3 deletions docs/configuration/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The frontend is a Single-Page Application (SPA) built with SAPUI5 or OpenUI5. Th
abap2UI5 never sends the app's business logic to the client. All business processes stay safely on the server, and sensitive data never reaches the frontend.

## Content-Security-Policy
To strengthen security, abap2UI5 uses a Content Security Policy (CSP) by default. CSP blocks attacks like cross-site scripting (XSS) and data injection by restricting which resources the browser can load. The default policy allows a fixed set of trusted sources — the SAP and OpenUI5 CDNs plus jsDelivr and cdnjs; the complete policy is shown below. It also carries hardening directives (`object-src 'none'`, `base-uri 'self'`, `frame-ancestors 'self'`) that block plugin content, pin `<base>` to the app origin and forbid cross-origin framing.
To strengthen security, abap2UI5 uses a Content Security Policy (CSP) by default. CSP blocks attacks like cross-site scripting (XSS) and data injection by restricting which resources the browser can load. The default policy allows a fixed set of trusted sources — the SAP and OpenUI5 CDNs plus jsDelivr and cdnjs; the complete policy is shown below. It also carries two hardening directives `object-src 'none'` and `base-uri 'self'`that block plugin content and pin `<base>` to the app origin. It deliberately carries **no** `frame-ancestors`: browsers ignore that directive in a `<meta>` CSP (and log a console warning about it), so cross-origin framing is forbidden by the real `X-Frame-Options` response header instead — see [Response headers](#response-headers) below.

The default **does** contain `'unsafe-eval'`: the ui5loader of OpenUI5 `1.71` — the oldest supported release — still evaluates module source as a string, and without `'unsafe-eval'` a `1.71` bootstrap fails with a CSP `EvalError`. Modern UI5 releases load all modules without `eval()`, so if you pin a modern release you can tighten the policy — see [Hardening: Dropping `'unsafe-eval'`](#hardening-dropping-unsafe-eval) below.

Expand All @@ -37,7 +37,7 @@ By default, abap2UI5 uses the CSP below (defined in `z2ui5_cl_ui5_user_exit`):
openui5.hana.ondemand.com *.openui5.hana.ondemand.com sdk.openui5.org *.sdk.openui5.org
cdn.jsdelivr.net *.cdn.jsdelivr.net cdnjs.cloudflare.com *.cdnjs.cloudflare.com;
worker-src 'self' blob:;
object-src 'none'; base-uri 'self'; frame-ancestors 'self';"/>
object-src 'none'; base-uri 'self';"/>
```

### Customizing the CSP
Expand Down Expand Up @@ -78,7 +78,7 @@ METHOD z2ui5_if_exit~set_config_http_get.
| cdn.jsdelivr.net *.cdn.jsdelivr.net | &&
| cdnjs.cloudflare.com *.cdnjs.cloudflare.com; | &&
|worker-src 'self' blob:; | &&
|object-src 'none'; base-uri 'self'; frame-ancestors 'self'; "/>|.
|object-src 'none'; base-uri 'self'; "/>|.

ENDMETHOD.
```
Expand All @@ -95,6 +95,34 @@ not an allowed source of script: default-src 'self' 'unsafe-inline' data: ui5.sa
Only tighten the policy when every system you deploy to bootstraps a modern release.
:::

## Response headers
Not everything hardening can be done in a `<meta>` CSP is done there — a page
served over HTTP carries headers too, and some directives only work as one.
abap2UI5 sets these on every response, out of the box:

| Header | Value | What it does |
|---|---|---|
| `X-Frame-Options` | `SAMEORIGIN` | forbids cross-origin framing. This, not the CSP, is where clickjacking protection lives: `frame-ancestors` in a `<meta>` CSP is ignored by browsers |
| `X-Content-Type-Options` | `nosniff` | the browser honours the declared content type instead of guessing one |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | a cross-origin request leaks the origin, never the path or query |
| `Permissions-Policy` | `geolocation=(self), microphone=(self), camera=(self), payment=(), usb=()` | the device APIs abap2UI5 offers stay available to the app itself; payment and USB are off |
| `cache-control` / `Pragma` / `Expires` | `no-cache, no-store, must-revalidate` / `no-cache` / `0` | the roundtrip responses carry application state — nothing about them may be cached |

They live in `cs_config-t_security_header` and are set in the same
[user exit](/advanced/extensibility/user_exits) as the CSP, so an installation
behind a proxy that already sets one of them can drop or change it:

```abap
METHOD z2ui5_if_exit~set_config_http_get.

" keep everything the framework set, override one entry
DELETE cs_config-t_security_header WHERE n = `Referrer-Policy`.
APPEND VALUE #( n = `Referrer-Policy` v = `no-referrer` )
TO cs_config-t_security_header.

ENDMETHOD.
```

## Cross-Site Request Forgery (CSRF)
Every state-changing request in abap2UI5 is a POST, so the framework ships its own CSRF defense instead of relying on a fronting SAP ICF/CSRF layer that may or may not be there. The check compares the host authority of the request's `Origin` (or `Referer`) header against the `Host` header — a cross-origin POST is rejected with an error response before any app logic runs.

Expand Down
12 changes: 9 additions & 3 deletions docs/cookbook/browser_interaction/url_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ client->follow_up_action(
```

### Browser History
Two client methods control the browser history from the backend:
One client method writes the browser history from the backend:

Push a new history entry — the value is appended to the URL hash, so app state becomes bookmarkable and the browser back button steps through your pushed states:
```abap
Expand All @@ -36,9 +36,15 @@ client->set_push_state( `&my-app-state=detail` ).
There is no method that presses the browser's back button for you — the
pushed states above are what the back button walks through, and leaving an
app is [`nav_app_leave( )`](/cookbook/event_navigation/navigation), which
returns to the calling app rather than to the previous URL.
returns to the calling app rather than to the previous URL. To step back
through the pushed states from ABAP, hand the raw expression to
`follow_up_action( )`:

For a complete example, see sample `Z2UI5_CL_DEMO_APP_139`.
```abap
client->follow_up_action( |history.back()| ).
```

For a complete example, see sample `Z2UI5_CL_SMP_APP_322`.

::: tip Hash-based app routing
For app-to-app navigation, the framework can own the URL hash itself: with [Routing](/cookbook/event_navigation/routing) enabled, each app gets a bookmarkable route and the browser Back/Forward buttons navigate the app stack — no manual push states needed.
Expand Down
4 changes: 2 additions & 2 deletions docs/cookbook/cheat_sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ A one-page recap of the rules that decide whether an abap2UI5 app works or misbe
| Implement `z2ui5_if_app` and put everything in the single `main` method | It is the only entry point the framework calls — on the initial load *and* on every user interaction → [Life Cycle](/cookbook/event_navigation/life_cycle) |
| Dispatch with one `IF` / `ELSEIF` chain over `check_on_init( )`, `check_on_navigated( )` and `check_on_event( )` | Each check answers for its own phase only; separate `IF` blocks let two branches run in the same roundtrip → [Life Cycle](/cookbook/event_navigation/life_cycle) |
| Always call `view_display( )` in the `check_on_navigated( )` branch | After a called app returns via `nav_app_leave( )`, the browser still shows *its* view — without a re-display the user is left on a stale or blank screen → [Navigation](/cookbook/event_navigation/navigation) |
| Declare every attribute you bind in the `PUBLIC SECTION` | Binding works via dynamic `ASSIGN`; `PROTECTED` and `PRIVATE` attributes are silently ignored → [Binding](/cookbook/model/binding) |
| Keep state in public attributes, not in local variables | Between two events the controller is serialized to the client and back — locals, `DATA(...)` declarations, open cursors and locks do not survive → [Statefulness](/cookbook/expert_more/statefulness) |
| Declare every attribute you bind in the `PUBLIC SECTION` | Binding works via dynamic `ASSIGN` and cannot reach `PROTECTED` / `PRIVATE`; the roundtrip fails with `BINDING_ERROR` → [Binding](/cookbook/model/binding) |
| Keep state in attributes, not in local variables | Between two events the app instance is serialized into a draft on the SERVER and read back — attributes survive at any visibility; locals, `DATA(...)` declarations, open cursors and locks do not → [Statefulness](/cookbook/expert_more/statefulness) |
| Respect the UI5 aggregation rules even though the builder does not enforce them | The builder lets you nest anything inside anything; UI5 does not, and the mismatch surfaces as broken rendering rather than a syntax error → [Definition](/cookbook/view/definition) |

| Never use a deprecated UI5 control | It renders today and vanishes on the next UI5 upgrade → [Deprecated Controls](/cookbook/view/deprecated_controls) |
Expand Down
1 change: 0 additions & 1 deletion docs/cookbook/device_capabilities/barcode_scanning.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ Since UI5 version 1.102, the `sap.ndc.BarcodeScannerButton` control is part of t

ENDMETHOD.
```
To see barcode scanning in action, check the `Z2UI5_CL_DEMO_APP_124` sample app.

::: tip **UI5 Versions**
This feature works only with the UI5 framework, not with OpenUI5.
Expand Down
8 changes: 8 additions & 0 deletions docs/cookbook/device_capabilities/pdf.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ Render a PDF directly in your app — for printouts from Adobe Forms, SmartForms

The simplest path is the built-in popup `Z2UI5_CL_POP_PDF`. It expects the PDF as a `data:application/pdf;base64,...` URI and embeds it in an iframe:

::: warning The built-in popups are frozen
`Z2UI5_CL_POP_PDF` and its siblings live in the framework's frozen `src/99/02` package:
they still run, so existing apps keep working, but they are not maintained and
are on the [removal list](/resources/deprecations).
New code should take its popups from the separate
[popups addon](https://github.com/abap2UI5-addons/popups).
:::

```abap
METHOD z2ui5_if_app~main.

Expand Down
8 changes: 8 additions & 0 deletions docs/cookbook/event_navigation/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ samples:

Beyond plain messages, abap2UI5 ships dedicated popups and fallbacks for handling exceptions and unexpected failures.

::: warning The built-in popups are frozen
`Z2UI5_CL_POP_ERROR` and its siblings live in the framework's frozen `src/99/02` package:
they still run, so existing apps keep working, but they are not maintained and
are on the [removal list](/resources/deprecations).
New code should take its popups from the separate
[popups addon](https://github.com/abap2UI5-addons/popups).
:::

### Error Popup
To display full details of your exception:
```abap
Expand Down
Loading