Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
fca9d6d
chore(sync): carry beta back into development
github-actions[bot] Sep 6, 2026
f6dd2db
Merge pull request #570 from ConductionNL/sync/beta-to-development-20…
rubenvdlinde Sep 6, 2026
1c3d338
chore(deps): refresh the shared Conduction locks (#574)
rubenvdlinde Sep 7, 2026
8a443c9
fix(tests): cap PHPUnit memory and only boot an installed Nextcloud i…
rubenvdlinde Sep 8, 2026
dd16c6d
fix(tests): a mid-boot Nextcloud failure warns, it does not abort the…
rubenvdlinde Sep 8, 2026
fff28db
chore(deps): pinia 4 is exports-only, so alias its entry file (#580)
rubenvdlinde Sep 8, 2026
85d1f6c
fix: point launchpad's cross-app lookups at names that exist (#582)
rubenvdlinde Sep 9, 2026
3d4dea8
chore(gate-115): record the two spend-narrative bindings as answered …
rubenvdlinde Sep 10, 2026
647d08f
The admin menu entries render for every account, and the routes serve…
rubenvdlinde Sep 10, 2026
caad753
chore(release): 1.1.2-unstable.20260910103156 (#588)
rubenvdlinde Sep 10, 2026
fc8708e
test(e2e): do not anchor the landing path, CI serves index.php routin…
rubenvdlinde Sep 10, 2026
a3eb22a
chore(release): 1.1.3-unstable.20260910114308 (#590)
rubenvdlinde Sep 10, 2026
25f776c
chore(deps-dev): bump @typescript-eslint/parser from 8.69.0 to 8.70.0…
dependabot[bot] Sep 10, 2026
dcedd67
chore(deps-dev): bump conduction/hydra-gates from 1.16.0 to 1.18.0 (#…
dependabot[bot] Sep 10, 2026
d34c8a5
chore(deps-dev): bump phpstan/phpstan from 2.2.12 to 2.2.13 (#595)
dependabot[bot] Sep 10, 2026
bf67298
chore(deps-dev): bump @vitest/ui from 4.1.11 to 5.0.0 (#602)
dependabot[bot] Sep 10, 2026
af3fd45
chore(deps-dev): bump eslint from 10.9.1 to 10.10.0 (#601)
dependabot[bot] Sep 10, 2026
5e86058
chore(deps-dev): bump stylelint from 17.14.1 to 17.15.0 (#600)
dependabot[bot] Sep 10, 2026
df7c53b
chore(deps): bump dompurify from 3.4.14 to 3.4.15 (#599)
dependabot[bot] Sep 10, 2026
9299d41
chore(deps-dev): bump @playwright/test from 1.62.1 to 1.63.0 (#598)
dependabot[bot] Sep 10, 2026
710b081
chore(deps): bump @conduction/nextcloud-vue from 2.37.0 to 2.41.1 (#597)
dependabot[bot] Sep 10, 2026
d1cd632
chore(deps): bump marked from 18.0.11 to 18.0.12 (#592)
dependabot[bot] Sep 10, 2026
3c4d578
fix(deps): restore the dependency graph I broke on development (#603)
rubenvdlinde Sep 10, 2026
ecf4965
chore(release): 1.1.4-unstable.20260910141110 (#604)
rubenvdlinde Sep 10, 2026
3797821
feat(showcases): a case handler dashboard, and the settings PUT that …
rubenvdlinde Sep 11, 2026
9149845
feat(store): install a dashboard template from another instance (#607)
rubenvdlinde Sep 11, 2026
e365850
fix(showcases): install the widgets LaunchPad renders itself, with th…
rubenvdlinde Sep 11, 2026
a502fa3
fix(import): an import lands at all, then keeps its configuration (#612)
rubenvdlinde Sep 11, 2026
05f7f1b
fix(sharing): delete a dashboard's shares with it, and make the shari…
rubenvdlinde Sep 11, 2026
3b91e5c
test(export-import): give every scenario its own verdict, and fix thr…
rubenvdlinde Sep 11, 2026
71b47fc
docs(openspec): plan an e2e verdict for every LaunchPad scenario (#619)
rubenvdlinde Sep 11, 2026
78907c5
test(db): run database tests in CI, cover four write paths, and fix t…
rubenvdlinde Sep 11, 2026
233065e
ci: run the fast tier on pull requests (path filter, one PHPUnit leg)…
rubenvdlinde Sep 12, 2026
7258c8e
feat(e2e): refuse the shared instance unless the run names it (#625)
rubenvdlinde Sep 12, 2026
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
42 changes: 42 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,45 @@ jobs:
# deliberate). Floating both halves keeps caller and callee in step,
# which is the only configuration in which a pinned path cannot expire.
enable-hydra-gates: true
# ── Cost controls (see ConductionNL/.github#596, #599) ───────────────
# Run PHPUnit and Playwright only when the diff could change their
# verdict. Fails safe TOWARDS running: an unreadable diff, a force-push,
# a branch creation or a dispatch all run everything, and a filtered
# skip is a declared state in the Quality Report, not a missing one.
enable-path-filtering: true
# PR-time PHPUnit runs the primary PHP against the newest declared
# Nextcloud; the full matrix still runs on every push to a default
# branch and on the release PR into beta. Breadth moves from
# per-commit to per-merge, it is not dropped.
reduce-pr-matrix: true

# ── l10n parity, as a WARNING ─────────────────────────────────────────────
# tests/l10n/check-l10n-parity.js ran in no CI lane, so #559 left 35 of the
# 36 required locales without "Admin settings" and nothing noticed for six
# days: gate-102 only guards nl.json, and only for manifest strings.
#
# It lives HERE, in this repo's own workflow, and not in the shared
# ConductionNL/.github quality.yml, whose changes reach the whole fleet the
# minute they merge. And it ships as a warning first, the way every new check
# does: a failure raises an annotation and marks this step, but
# `continue-on-error` keeps the job and the run green. Make it blocking once
# it has held for a while, by deleting `continue-on-error`.
#
# Dependency-free Node, so it needs no `npm ci`.
l10n-parity:
name: l10n parity (warning only)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Every required locale carries every source key
continue-on-error: true
run: |
if ! npm run --silent check:l10n-parity; then
echo "::warning title=l10n parity::A required locale is missing a translation. Warning only, not blocking: see this step's log for the locale and the key."
exit 1
fi
60 changes: 30 additions & 30 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,49 @@
<name lang="nl">LaunchPad</name>
<summary lang="en">Drag-and-drop dashboards with templates, tiles, and smart visibility</summary>
<summary lang="nl">Drag-and-drop dashboards met templates, tegels en slimme zichtbaarheid</summary>
<description lang="en"><![CDATA[LaunchPad supercharges the Nextcloud dashboard. Create personalized workspaces with drag-and-drop widgets, custom shortcut tiles, and smart visibility rules — then let admins roll out templates to entire teams.

**Key Features**

- **Drag-and-drop grid layout** Position and resize widgets freely on a flexible grid
- **Multiple dashboards** Create as many dashboards as you need and switch between them
- **Custom tiles** Add shortcut cards with icons, colors, and links to your most-used tools
- **Admin templates** Pre-configure dashboards and distribute them to user groups with one click
- **Permission levels** Choose view-only, add-only, or full customization per template
- **Conditional visibility** Show or hide widgets based on group membership, time of day, or date
- **Widget styling** Customize colors, borders, and titles for each individual widget
- **Compulsory widgets** Admins can pin important widgets that users cannot remove
- **Full Nextcloud compatibility** Works with every existing Nextcloud dashboard widget out of the box
- **Role-based widget access** Restrict which widget types a group of users may add, resolved from Nextcloud group membership
- **Dashboard sharing** Share a dashboard with specific users or groups, or publish a brute-force-protected read-only public link
- **Group dashboards** One shared dashboard per group, in addition to personal dashboards
<description lang="en"><![CDATA[LaunchPad supercharges the Nextcloud dashboard. Create personalized workspaces with drag-and-drop widgets, custom shortcut tiles, and smart visibility rules. Admins roll out templates to entire teams.

**Key features**

- **Drag-and-drop grid layout**: Position and resize widgets freely on a flexible grid
- **Multiple dashboards**: Create as many dashboards as you need and switch between them
- **Custom tiles**: Add shortcut cards with icons, colors, and links to your most-used tools
- **Admin templates**: Pre-configure dashboards and distribute them to user groups with one click
- **Permission levels**: Choose view-only, add-only, or full customization per template
- **Conditional visibility**: Show or hide widgets based on group membership, time of day, or date
- **Widget styling**: Customize colors, borders, and titles for each individual widget
- **Compulsory widgets**: Admins can pin important widgets that users cannot remove
- **Full Nextcloud compatibility**: Works with every existing Nextcloud dashboard widget out of the box
- **Role-based widget access**: Restrict which widget types a group of users may add, resolved from Nextcloud group membership
- **Dashboard sharing**: Share a dashboard with specific users or groups, or publish a brute-force-protected read-only public link
- **Group dashboards**: One shared dashboard per group, in addition to personal dashboards

Perfect for organizations that want consistent, curated dashboards for their teams while still giving users freedom to personalize.

Free and open source under the EUPL-1.2 license.
]]></description>
<description lang="nl"><![CDATA[LaunchPad geeft het Nextcloud-dashboard superkrachten. Maak persoonlijke werkruimtes met drag-and-drop widgets, eigen snelkoppelingstegels en slimme zichtbaarheidsregels — en laat beheerders templates uitrollen naar hele teams.
<description lang="nl"><![CDATA[LaunchPad geeft het Nextcloud-dashboard superkrachten. Maak persoonlijke werkruimtes met drag-and-drop widgets, eigen snelkoppelingstegels en slimme zichtbaarheidsregels. Beheerders rollen templates uit naar hele teams.

**Belangrijkste functies**

- **Drag-and-drop grid layout** Positioneer en schaal widgets vrij op een flexibel raster
- **Meerdere dashboards** Maak zoveel dashboards als je nodig hebt en wissel ertussen
- **Eigen tegels** Voeg snelkoppelingskaarten toe met iconen, kleuren en links naar je meestgebruikte tools
- **Beheerdertemplates** Configureer dashboards vooraf en distribueer ze met een klik naar gebruikersgroepen
- **Rechtenniveaus** Kies tussen alleen bekijken, toevoegen, of volledige aanpassing per template
- **Conditionele zichtbaarheid** Toon of verberg widgets op basis van groepslidmaatschap, tijdstip of datum
- **Widget-styling** Pas kleuren, randen en titels aan voor elke individuele widget
- **Verplichte widgets** Beheerders kunnen belangrijke widgets vastzetten die gebruikers niet kunnen verwijderen
- **Volledige Nextcloud-compatibiliteit** Werkt direct met elke bestaande Nextcloud dashboard-widget
- **Rolgebaseerde widget-toegang** Beperk welke widget-types een gebruikersgroep mag toevoegen, op basis van Nextcloud-groepslidmaatschap
- **Dashboards delen** Deel een dashboard met specifieke gebruikers of groepen, of publiceer een tegen brute-force beveiligde alleen-lezen publieke link
- **Groepsdashboards** Eén gedeeld dashboard per groep, naast persoonlijke dashboards
- **Drag-and-drop grid layout**: Positioneer en schaal widgets vrij op een flexibel raster
- **Meerdere dashboards**: Maak zoveel dashboards als je nodig hebt en wissel ertussen
- **Eigen tegels**: Voeg snelkoppelingskaarten toe met iconen, kleuren en links naar je meestgebruikte tools
- **Beheerdertemplates**: Configureer dashboards vooraf en distribueer ze met een klik naar gebruikersgroepen
- **Rechtenniveaus**: Kies tussen alleen bekijken, toevoegen, of volledige aanpassing per template
- **Conditionele zichtbaarheid**: Toon of verberg widgets op basis van groepslidmaatschap, tijdstip of datum
- **Widget-styling**: Pas kleuren, randen en titels aan voor elke individuele widget
- **Verplichte widgets**: Beheerders kunnen belangrijke widgets vastzetten die gebruikers niet kunnen verwijderen
- **Volledige Nextcloud-compatibiliteit**: Werkt direct met elke bestaande Nextcloud dashboard-widget
- **Rolgebaseerde widget-toegang**: Beperk welke widget-types een gebruikersgroep mag toevoegen, op basis van Nextcloud-groepslidmaatschap
- **Dashboards delen**: Deel een dashboard met specifieke gebruikers of groepen, of publiceer een tegen brute-force beveiligde alleen-lezen publieke link
- **Groepsdashboards**: Eén gedeeld dashboard per groep, naast persoonlijke dashboards

Ideaal voor organisaties die consistente, samengestelde dashboards willen voor hun teams, terwijl gebruikers de vrijheid houden om te personaliseren.

Vrij en open source onder de EUPL-1.2-licentie.
]]></description>
<version>1.0.25-unstable.20260831161142</version>
<version>1.1.4-unstable.20260910141110</version>
<licence>EUPL-1.2</licence>
<author mail="info@conduction.nl" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>LaunchPad</namespace>
Expand Down
18 changes: 18 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,24 @@
['name' => 'metrics#index', 'url' => '/api/metrics', 'verb' => 'GET'],
['name' => 'health#index', 'url' => '/api/health', 'verb' => 'GET'],

// Store plane (ADR-080). LaunchPad declares a `type: "store"` page, which
// renders CnStorePage, which calls the item routes on mount and on an install
// click. Without them that page is in the navigation and 404s.
//
// Discovery is OpenRegister's `GenericStoreService`; install is ours,
// because a dashboard lives in LaunchPad's tables and no engine install
// op can write one. `store#*` therefore resolves to
// `OCA\LaunchPad\Controller\StoreController`, which is also what keeps
// the engine's alias off this app (see that class).
//
// The slug requirement matches the engine's, so a malformed slug fails
// at the router rather than inside a registry URL.
['name' => 'store#search', 'url' => '/api/store/items', 'verb' => 'GET'],
['name' => 'store#getConfig', 'url' => '/api/store/config', 'verb' => 'GET'],
['name' => 'store#updateConfig', 'url' => '/api/store/config', 'verb' => 'PUT'],
['name' => 'store#install', 'url' => '/api/store/items/{slug}/install', 'verb' => 'POST',
'requirements' => ['slug' => '[a-z0-9][a-z0-9\-]*[a-z0-9]']],

// Generic per-user preferences (used by shared nextcloud-vue widgets, e.g. CnSupportDialog).
['name' => 'preferences#getPreference', 'url' => '/api/preferences/{key}', 'verb' => 'GET'],
['name' => 'preferences#setPreference', 'url' => '/api/preferences/{key}', 'verb' => 'PUT'],
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
"phpcs": "./vendor/bin/phpcs --standard=phpcs.xml",
"phpcs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml",
"phpcs:output": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ 2>/dev/null | tail -1 > phpcs-output.json",
"phpmd": "E=0; ./vendor/bin/phpmd lib text phpmd.xml || E=$?; ./vendor/bin/phpmd lib text vendor/conduction/hydra-gates/quality-config/phpmd-unusedparams.xml --baseline-file phpmd.baseline.xml || E=$?; exit $E",
"phpmd": "E=0; php -d memory_limit=2G ./vendor/bin/phpmd lib text phpmd.xml || E=$?; php -d memory_limit=2G ./vendor/bin/phpmd lib text vendor/conduction/hydra-gates/quality-config/phpmd-unusedparams.xml --baseline-file phpmd.baseline.xml || E=$?; exit $E",
"phpmetrics": "./vendor/bin/phpmetrics --report-html=phpmetrics lib/",
"phpmetrics:violations": "./vendor/bin/phpmetrics --violations-xml=phpmetrics/violations.xml lib/",
"psalm": "./vendor/bin/psalm --threads=1 --no-cache",
"psalm": "./vendor/bin/psalm --threads=1 --no-cache --memory-limit=2G",
"phpstan": "./vendor/bin/phpstan analyse --memory-limit=1G",
"test": "phpunit --configuration phpunit.xml",
"test:unit": "./vendor/bin/phpunit --configuration phpunit.xml --colors=always",
Expand Down
20 changes: 10 additions & 10 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file added img/showcases/case-handler.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions l10n/be.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
OC.L10N.register(
"launchpad",
{
"Admin settings" : "Налады адміністравання",
"Features & roadmap" : "Функцыі і дарожная карта",
"Maximum dashboards per user" : "Максімальная колькасць дашбордаў на карыстальніка",
"Maximum widgets per dashboard" : "Максімальная колькасць віджэтаў на дашборд",
Expand Down Expand Up @@ -1113,7 +1114,6 @@ OC.L10N.register(
"Kind": "Від",
"Version": "Версія",
"How many dashboards there are, and who they belong to.": "Колькі ёсць панэляў і каму яны належаць.",
"Store": "Крама",
"Install dashboard configurations that other organisations have published: a set of tiles, a layout, or the flows behind them.": "Усталюйце рэестры, схемы і патокі, апублікаваныя іншымі арганізацыямі."
"Store": "Крама"
},
"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
4 changes: 2 additions & 2 deletions l10n/be.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"translations": {
"Admin settings": "Налады адміністравання",
"Maximum dashboards per user": "Максімальная колькасць дашбордаў на карыстальніка",
"Maximum widgets per dashboard": "Максімальная колькасць віджэтаў на дашборд",
"0 = unlimited. Lowering a limit never deletes existing dashboards; it only blocks new ones until users are back under the limit.": "0 = без абмежаванняў. Зніжэнне ліміту ніколі не выдаляе існуючыя дашборды; яно толькі блакуе стварэнне новых, пакуль карыстальнікі не вернуцца пад ліміт.",
Expand Down Expand Up @@ -1090,7 +1091,6 @@
"Kind": "Від",
"Version": "Версія",
"How many dashboards there are, and who they belong to.": "Колькі ёсць панэляў і каму яны належаць.",
"Store": "Крама",
"Install dashboard configurations that other organisations have published: a set of tiles, a layout, or the flows behind them.": "Усталюйце рэестры, схемы і патокі, апублікаваныя іншымі арганізацыямі."
"Store": "Крама"
}
}
4 changes: 2 additions & 2 deletions l10n/bg.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
OC.L10N.register(
"launchpad",
{
"Admin settings" : "Административни настройки",
"Features & roadmap" : "Функции и пътна карта",
"Maximum dashboards per user" : "Максимален брой табла на потребител",
"Maximum widgets per dashboard" : "Максимален брой джаджи на табло",
Expand Down Expand Up @@ -1122,8 +1123,7 @@ OC.L10N.register(
"Kind": "Вид",
"Version": "Версия",
"How many dashboards there are, and who they belong to.": "Колко табла има и на кого принадлежат.",
"Store": "Магазин",
"Install dashboard configurations that other organisations have published: a set of tiles, a layout, or the flows behind them.": "Инсталирайте регистри, схеми и потоци, публикувани от други организации."
"Store": "Магазин"
},
"nplurals=2; plural=(n != 1);"
);
4 changes: 2 additions & 2 deletions l10n/bg.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"translations": {
"Admin settings": "Административни настройки",
"Maximum dashboards per user": "Максимален брой табла на потребител",
"Maximum widgets per dashboard": "Максимален брой джаджи на табло",
"0 = unlimited. Lowering a limit never deletes existing dashboards; it only blocks new ones until users are back under the limit.": "0 = неограничено. Намаляването на ограничение никога не изтрива съществуващи табла; то само блокира новите, докато потребителите не се върнат под ограничението.",
Expand Down Expand Up @@ -1089,7 +1090,6 @@
"Kind": "Вид",
"Version": "Версия",
"How many dashboards there are, and who they belong to.": "Колко табла има и на кого принадлежат.",
"Store": "Магазин",
"Install dashboard configurations that other organisations have published: a set of tiles, a layout, or the flows behind them.": "Инсталирайте регистри, схеми и потоци, публикувани от други организации."
"Store": "Магазин"
}
}
Loading
Loading