Repository navigation
feat(announcements): targeted news and notices with preview, likes, comments and category follows - #788
Open
rubenvdlinde wants to merge 56 commits into
Open
feat(announcements): targeted news and notices with preview, likes, comments and category follows#788rubenvdlinde wants to merge 56 commits into
rubenvdlinde wants to merge 56 commits into
Conversation
…ly, by most or last used, or at random Most and last used read counts kept in the viewer's own browser, never sent to the server; a viewer can forget them. The order is applied when the page is viewed and never written back, and random holds for the page load. REQ-TSO-001..004, REQ-CONT-007 (launcher-tile-sorting)
…ile-sort are built
…ends a query straight to a site
search_shortcuts holds up to 30 entries {prefix, name, urlTemplate}; a prefix is !
and 1 to 10 letters or digits, unique; the address passes the https {query} check
the quick-search fallback already uses. The list reaches every page through the
workspace initial state.
REQ-QSP-001, REQ-QSP-002 (search-ai-prefix-shortcuts)
…d ? lists the shortcuts In the search widget a query whose first word is a shortcut prefix shows one result, Search TOPdesk for printer, and Enter opens the site in a new tab; an unknown prefix filters tiles as before; ! or ? lists the shortcuts. Administrators edit the list on a new admin tab. REQ-SPX-001..003 (search-ai-prefix-shortcuts)
…d-fields' into feat/launcher-tile-sorting
…ncher-tile-sorting
…ming a group of tiles The browser reads the file (Firefox, Chrome and Edge exports), shows folders and loose bookmarks to pick from, and refuses files over 5 MB or 2,000 bookmarks. The server keeps only http and https addresses and lists the rest as skipped, checks the widget quota once for the whole import (409 names the room left), and creates a container of link tiles per folder plus loose tiles at the bottom in one transaction. The entry sits in the active dashboard's menu for people who may add widgets. REQ-BMI-001..004 (launcher-bookmark-import)
…carry an address for inside the office office_networks holds IPv4 and IPv6 ranges, validated with Nextcloud's IP factory; the server decides from the request address and tells the page onOfficeNetwork. A tile's content.internalUrl must be an http or https address or a path on this Nextcloud, or the update is refused. REQ-TIA-001, REQ-TIA-002 (launcher-tile-internal-address)
…nd administrators set the office networks The tile editor gets "Address on the office network" and says which address the author gets right now; the tile opens the internal address only when the server says the request comes from an office network. A new admin tab lists the ranges and names the administrator's own address. REQ-TIA-001..003 (launcher-tile-internal-address)
…ncher-tile-sorting
… feat/search-ai-prefix-shortcuts
… into feat/launcher-bookmark-import
…e tile wrapper is not a fake control (gate-32)
… feat/search-ai-prefix-shortcuts
…ncher-tile-sorting
…job and red service test
…ort endpoint couples to 13 types (phpmd)
…ress check under the phpmd limits
…ncher-tile-sorting
…admin, refusals and an invalid list
…rows building until the live pass
…ess' into feat/engagement-announcements
…groups through the shared resolver, prettier and lint
…itor binds its selects directly (gates 16, 25)
rubenvdlinde
requested review from
Rem-Dam,
SudoThijn,
WilcoLouwerse,
bbrands02,
remko48 and
rjzondervan
as code owners
October 5, 2026 17:56
Contributor
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| format | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 104/104 | |||
| npm | ✅ | ✅ 636/636 | |||
| 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-05 18:19 UTC
Download the full PDF report from the workflow artifacts.
…tile-sorting # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json
…shortcuts # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # lib/Controller/PageController.php # lib/Db/AdminSettingKey.php # lib/Service/InitialStateBuilder.php # src/components/admin/AdminSettings.vue # src/utils/loadInitialState.js
Assisted-by: ClaudeCode:claude-opus-5-5
…kmark-import # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # src/views/Views.vue
…internal-address # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # lib/Controller/PageController.php # lib/Db/AdminSettingKey.php # lib/Service/InitialStateBuilder.php # src/components/admin/AdminSettings.vue # src/utils/loadInitialState.js
…nt-announcements # Conflicts: # l10n/be.js # l10n/be.json # l10n/bg.js # l10n/bg.json # l10n/bs.js # l10n/bs.json # l10n/ca.js # l10n/ca.json # l10n/cs.js # l10n/cs.json # l10n/da.js # l10n/da.json # l10n/de.js # l10n/de.json # l10n/el.js # l10n/el.json # l10n/en.js # l10n/en.json # l10n/es.js # l10n/es.json # l10n/et.js # l10n/et.json # l10n/fi.js # l10n/fi.json # l10n/fr.js # l10n/fr.json # l10n/ga.js # l10n/ga.json # l10n/hr.js # l10n/hr.json # l10n/hu.js # l10n/hu.json # l10n/is.js # l10n/is.json # l10n/it.js # l10n/it.json # l10n/lb.js # l10n/lb.json # l10n/lt.js # l10n/lt.json # l10n/lv.js # l10n/lv.json # l10n/mk.js # l10n/mk.json # l10n/mt.js # l10n/mt.json # l10n/nb.js # l10n/nb.json # l10n/nl.js # l10n/nl.json # l10n/pl.js # l10n/pl.json # l10n/pt.js # l10n/pt.json # l10n/rm.js # l10n/rm.json # l10n/ro.js # l10n/ro.json # l10n/ru.js # l10n/ru.json # l10n/sk.js # l10n/sk.json # l10n/sl.js # l10n/sl.json # l10n/sq.js # l10n/sq.json # l10n/sr.js # l10n/sr.json # l10n/sv.js # l10n/sv.json # l10n/tr.js # l10n/tr.json # l10n/uk.js # l10n/uk.json # lib/Db/AdminSettingKey.php # src/components/admin/AdminSettings.vue
Contributor
Quality Report — ConductionNL/launchpad @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| format | ❌ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 107/107 | |||
| npm | ❌ | ✅ 636/636 | |||
| 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-23 05:24 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Change
engagement-announcements(rows e-announcements, e-maintenance, d-news-preview). Stacked on #787 (feat/launcher-tile-internal-address): this branch contains every PR below it (#743, #784 to #787). Land the stack in order, or land the top PR with a merge commit and the ones below close with it.launchpad_announcement); LaunchPad keeps no comment rows.AnnouncementNotifyJob(every 5 minutes), never twice (notified_at).launchpad_announcementsandlaunchpad_ann_follows(migrationVersion002013Date20261005190000, info.xml 1.1.7-unstable.20261005200000). A deleted user's follows go with them. Demo installs get the three demo announcements from the design.openspec/specs/announcements/spec.md; the design records what the build changed (short table name, likes as comments with verblike, the reach line).The rows move to
building, notbuilt: tasks 8 (axe check) and 12 (Playwright, written intests/e2e/announcements.spec.ts) need the live instance. The change is archived after the live pass.Tests (red first)
tests/Unit/Service/AnnouncementServiceTest.php(12): targeting, drafts, the window, author rights, the notice end time, comment refusals, follower notified once, "Preview as". Red run without the service: 12 errors (lane5/red-announcement-service.log).tests/Unit/Controller/AnnouncementControllerTest.php(6) through the real service: 403 for a reader who posts, 404 by id for a non-member, 400 for a notice without an end time, the action per endpoint, settings admin-only.tests/Unit/BackgroundJob/AnnouncementNotifyJobTest.php,tests/Unit/Notification/AnnouncementNotifierTest.php, the listener's follow cleanup inUserDeletedListenerTest, the demo seeding throughDemoDataService::install()(red before the wiring: 1 failure), the action baseline.tests/Unit/Database/AnnouncementsDatabaseTest.php: real database and Nextcloud's real comments manager (publish, like twice is one like, comment, unlike, follow once, follows removed by the real UserDeletedEvent). Skipped without a live Nextcloud; runs in CI.AnnouncementsWidget.spec.js,NoticeRegion.spec.js,AnnouncementEditorModal.spec.js(13), with axios mocked so the URLs and bodies are checked against the routes. Red run without the components: 3 files failed.Verified
composer check:strictparts on 30bd319: phpcs, phpmd, psalm, phpstan 0, PHPUnit 1987 tests OK (17 skipped database tests that run in CI); AnnouncementControllerTest re-run on 375ab2e.--require-full-coverage) on 375ab2e: 85 of 86 applicable gates pass; gate 23 fails with the one match development has (PermissionService,RoleFeaturePermissionService), inherited and waiting on a decision. The first run found two untagged computed setters (gate 16) and the comments list without a contract test (gate 25), both fixed.Live check
As admin, Admin settings, LaunchPad, "Announcements": add group "Redactie". As Karin (in Redactie), on a dashboard with the Announcements widget: "New announcement", kind "Notice above the dashboard", level Warning, title "Onderhoud zaaksysteem zaterdag 08:00 tot 12:00", target group "Burgerzaken", no end time, "Preview": banner and "People reached: N"; "Publish": "A notice needs an end time". Set an end time, Preview, Publish. As Pieter (in Burgerzaken): the banner shows above every dashboard, with no close button. As Sanne: no banner. Publish a news item in "Facilitair" for everyone; Pieter follows "Privacy", Karin publishes in "Privacy": Pieter gets one notification. Pieter likes and comments: the card shows 1 and 1.