Skip to content

feat(apps): copy an app, a page or a registration form - #1026

Merged
rubenvdlinde merged 7 commits into
developmentfrom
feat/copy-app-and-page
Oct 2, 2026
Merged

rubenvdlinde merged 7 commits into
developmentfrom
feat/copy-app-and-page

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Change apps-copy-app-and-page (not archived: T03's Playwright copy has not run against a live stack yet). A maker can copy a whole app, one page, or one registration form, and the copy is independent of its source from the moment it exists.

What is built:

  • T01, REQ-BQCP-001: lib/Service/AppTemplateCapture.php ports the de-namespace step of src/services/templateCapture.js to PHP, so a template array can be built from an app's current version on the server. Its PHPUnit cases are the ones of templateCapture.spec.js, so both sides agree.
  • T02, REQ-BQCP-001/002: POST /api/applications/{slug}/copy in ApplicationsController. A signed-in administrator who owns or edits the source app; rate limited like from-template; a taken slug is the install seam's 409. It installs through installFromTemplateArray(), so the copy gets its own register, the source's schemas and its current manifest, and no records.
  • T03, REQ-BQCP-001: "Copy app" in ApplicationDetailActions.vue, with a name and slug dialog in src/modals/CopyAppDialog.vue; the copy opens once it is made. Design correction: not on the app cards. A card is one link, and a button inside a link is announced wrongly by screen readers and swallows the card's click.
  • T04, REQ-BQCP-003: "Copy page" on each row of the page list (src/services/pageCopy.js): a unique id and route, "Copy of" the title, inserted below the source.
  • T05, REQ-BQCP-004: "Copy form" on each registration form (src/services/formCopy.js): a draft for the same type value, never the default. The body is validated against the real registrationForm schema fragment (Ajv in Vitest; Opis in the lane: valid, and a wrong-typed control invalid).
  • T06: English and Dutch strings; a section "Copy instead of templating" in docs/tutorials/admin/02-template-catalogue.md.

Still open, and why the change is not archived: T03's Playwright test tests/e2e/copy-app-and-page.spec.ts (copies the seeded Hello World app at API level, then a second copy on the same slug must be refused) has not run against a live stack. The parity row app-duplicate stays building until it has; form-copy is built (its requirements are covered by Vitest and their e2e exclusions carry reasons).

Tests, each red before its code (logs kept in the lane):

  • T01: AppTemplateCaptureTest (testDeNamespacesSchemasAndEveryManifestReference, testCaptureThenCloneIsACleanRename, testAnUnprefixedSchemaIsCapturedUnchanged, testACollisionNamesBothSchemas, testNoRowsAndNoMutation).
  • T02: CopyApplicationTest (testAnEditorCopiesTheApp, testAViewerCannotCopyTheApp, testANonAdminOwnerIsRefused, testATakenSlugIsRefused, testCopyIsRateLimitedLikeFromTemplate).
  • T03 to T05: tests/modals/CopyAppDialog.spec.js, tests/services/pageCopy.spec.js, tests/services/formCopy.spec.js.

Live check: as an admin, POST /index.php/apps/buildiq/api/applications/hello-world/copy with {"name": "Copy", "slug": "copy-1"} answers 201; GET .../applications/copy-1/manifest lists the same page ids as hello-world; the same POST again answers 409. In the UI: an app's detail page, Actions, Copy app.

Inherited, not fixed here: the vendored hydra-gates in composer check:strict fail gate-101 on the five buildiq schemas that carry reasoned no-demo skips since #853; CI's required gate run (hydra-gates@main) honours the reasons.

🤖 Generated with Claude Code

POST /api/applications/{slug}/copy captures the app's companion schemas
and current manifest in memory, de-namespaced exactly as Save as template
does (AppTemplateCapture, the PHP twin of templateCapture.js), and installs
them through the template seam. Same gates as cloning a template, plus
owner or editor of the source; no records are copied.
A dialog asks a name and a slug, suggests 'Copy of <name>', and opens the
copy once the server made it. Offered to an owner or editor who is an
administrator, the people the server lets copy.
Each page row gets a copy button that inserts a deep copy below it, with
a unique id and route (-copy, then -copy-2) and the title 'Copy of ...'.
The copy is not added to the menu.
The copy keeps the fields, steps, rules, sections and presets for the same
type, is named 'Copy of ...', is a draft and never the default, and opens
in the editor. The body is checked against the real registrationForm
schema with Ajv in the test and with Opis in the lane.
…te built

Strings in English and Dutch, a docs section on copying, an API-level
e2e for Copy app. The spec folds into openspec/specs/copy-app-page-and-form
with one design correction: Copy app is on the detail page, not on the app
cards, because a card is a single link.
…y has run

T03's e2e has not run against a live stack, so the change goes back
under openspec/changes, @SPEC tags point at the change's spec, and the
app-duplicate row stays building. The e2e file is prettier-formatted.
…and-page

# Conflicts:
#	l10n/en.js
#	l10n/en.json
#	l10n/nl.js
#	l10n/nl.json
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/buildiq @ 056d30a

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
test-l10n ✅
check-gitignore ✅
check-nc-floor ✅
format ✅
check-l10n-js ✅
check-schema-l10n ✅
composer ✅ ✅ 106/106
npm ✅ ✅ 752/752
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅
Newman ✅
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ✅

Quality workflow — 2026-10-02 05:15 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit d040ba3 into development Oct 2, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant