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 .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main, development, feature/**, bugfix/**, hotfix/**]
pull_request:
branches: [main, master, development]
branches: [main, master, development, beta]
workflow_dispatch:

jobs:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pull-request-lint-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches:
- development
- main
- beta

jobs:
lint-check:
Expand Down
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Makefile for nextcloud-app-template development

# Create a relative symlink in the parent directory so Nextcloud can find the
# app by its ID (app-template) even though the repo is cloned as nextcloud-app-template.
# Nextcloud requires the directory name to match the <id> in appinfo/info.xml.
dev-link:
@if [ -L ../app-template ]; then \
echo "Symlink ../app-template already exists."; \
else \
ln -s nextcloud-app-template ../app-template && \
echo "Created symlink: apps-extra/app-template -> nextcloud-app-template"; \
fi

dev-unlink:
@if [ -L ../app-template ]; then \
rm ../app-template && echo "Removed symlink ../app-template"; \
else \
echo "No symlink found at ../app-template."; \
fi

.PHONY: dev-link dev-unlink
78 changes: 56 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@

A starting point for building Nextcloud apps following ConductionNL conventions.

> **Requires:** [OpenRegister](https://github.com/ConductionNL/openregister) — all data is stored as OpenRegister objects.
> **Pre-wired for [OpenRegister](https://github.com/ConductionNL/openregister)** — all data is stored as OpenRegister objects. If your app needs OpenRegister, install it first. If not, remove the dependency from `appinfo/info.xml` and `openspec/app-config.json`.

## Screenshots

_Add screenshots here once the app has a UI._

## Features

Features are defined in [`appspec/features/`](appspec/features/). See the [roadmap](openspec/ROADMAP.md) for planned work.
Features are defined in [`openspec/specs/`](openspec/specs/). See the [roadmap](openspec/ROADMAP.md) for planned work.

### Core
- **Dashboard** — Personal overview page with key information at a glance
Expand Down Expand Up @@ -54,37 +54,40 @@ _Update this diagram during `/app-explore` sessions as the architecture evolves.
|--------|-------------|
| _(define your data objects here)_ | — |

_Data model is defined using OpenRegister schemas. See [`appspec/features/`](appspec/features/) for feature-level design decisions and [`appspec/adr/`](appspec/adr/) for architectural decisions._
_Data model is defined using OpenRegister schemas. See [`openspec/specs/`](openspec/specs/) for feature-level design decisions and [`openspec/architecture/`](openspec/architecture/) for architectural decisions._

### Directory Structure

```
app-template/
├── appinfo/ # Nextcloud app manifest, routes, navigation
├── lib/ # PHP backend — controllers, settings
├── lib/ # PHP backend
│ ├── AppInfo/Application.php
│ ├── Controller/DashboardController.php
│ ├── Settings/AdminSettings.php
│ └── Sections/SettingsSection.php
│ ├── Controller/ # DashboardController, SettingsController
│ ├── Service/SettingsService.php
│ ├── Listener/DeepLinkRegistrationListener.php
│ ├── Repair/InitializeSettings.php
│ └── Settings/ # AdminSettings, app_template_register.json
├── templates/ # PHP templates (SPA shells)
├── src/ # Vue 2 frontend
│ ├── main.js # App entry point
│ ├── settings.js # Admin settings entry
│ ├── App.vue # Root component
│ ├── navigation/MainMenu.vue # App navigation sidebar
│ ├── router/ # Vue Router
│ ├── store/ # Pinia stores
│ └── views/ # Route-level views
├── appspec/ # App configuration and specification
│ └── views/ # Route-level views + UserSettings.vue
├── openspec/ # Specifications, decisions, and roadmap
│ ├── app-config.json # Canonical app config (id, goal, dependencies, CI)
│ ├── features/ # High-level feature definitions
── adr/ # Architectural Decision Records
├── openspec/ # Implementation specifications and roadmap
│ ├── config.yaml # OpenSpec CLI configuration
── specs/ # Feature specs (input for OpenSpec changes)
├── architecture/ # App-specific Architectural Decision Records
│ ├── ROADMAP.md # Product roadmap
│ └── changes/ # OpenSpec change directories
│ └── changes/ # OpenSpec change directories (created on first change)
├── tests/ # Unit and integration tests
├── l10n/ # Translations (en, nl)
├── .github/workflows/ # CI/CD pipelines
├── phpcs-custom-sniffs/ # Named parameters enforcement
├── img/ # App icons and screenshots
└── l10n/ # Translations (en, nl)
├── Makefile # Dev helpers (make dev-link)
└── img/ # App icons and screenshots
```

## Requirements
Expand Down Expand Up @@ -121,7 +124,7 @@ php occ app:enable app-template
### Start the environment

```bash
docker compose -f openregister/docker-compose.yml up -d
docker compose -f ../openregister/docker-compose.yml up -d
```

### Frontend development
Expand All @@ -148,7 +151,14 @@ npm run stylelint # CSS linting

### Enable locally

Nextcloud requires the app directory name to match the `<id>` in `appinfo/info.xml` (`app-template`).
When this repo is cloned as `nextcloud-app-template`, create a relative symlink first.

> **Note:** The `js/` build output is not committed. You must build the frontend before enabling the app, or the UI will be blank.

```bash
make dev-link
npm install && npm run build
docker exec nextcloud php occ app:enable app-template
```

Expand All @@ -175,11 +185,11 @@ docker exec nextcloud php occ app:enable app-template

| Resource | Description |
|----------|-------------|
| [`appspec/`](appspec/) | App configuration, features, and architectural decisions |
| [`appspec/features/`](appspec/features/) | Feature definitions and lifecycle status |
| [`appspec/adr/`](appspec/adr/) | Architectural Decision Records |
| [`openspec/app-config.json`](openspec/app-config.json) | App identity, goals, dependencies, and CI configuration |
| [`openspec/specs/`](openspec/specs/) | Feature specs — what the app should do |
| [`openspec/architecture/`](openspec/architecture/) | App-specific Architectural Decision Records |
| [`openspec/ROADMAP.md`](openspec/ROADMAP.md) | Product roadmap |
| [`openspec/`](openspec/) | Implementation specifications |
| [`openspec/`](openspec/) | Implementation specifications and changes |

## Standards & Compliance

Expand All @@ -194,6 +204,30 @@ docker exec nextcloud php occ app:enable app-template

_Add related apps here as integrations are built._

## Troubleshooting

### App UI is blank after enabling

The `js/` build output is not committed to the repo. Run the frontend build before enabling the app:

```bash
npm install && npm run build
```

### "Could not download app app-template" when running `occ app:enable`

Nextcloud requires the app directory name to exactly match the `<id>` in `appinfo/info.xml`. When this repo is cloned as `nextcloud-app-template`, create a symlink first:

```bash
make dev-link # creates apps-extra/app-template -> nextcloud-app-template
```

Then enable the app again:

```bash
docker exec nextcloud php occ app:enable app-template
```

## Support

For support, contact us at [support@conduction.nl](mailto:support@conduction.nl).
Expand Down
2 changes: 1 addition & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Vrij en open source onder de EUPL-1.2-licentie.
<navigation>
<id>app-template</id>
<name>App Template</name>
<route>app_template.dashboard.page</route>
<route>app-template.dashboard.page</route>
<icon>app.svg</icon>
</navigation>
</navigations>
Expand Down
5 changes: 3 additions & 2 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
// Health check endpoint.
['name' => 'health#index', 'url' => '/api/health', 'verb' => 'GET'],

// SPA catch-all — serves the Vue app for any frontend route (history mode)
['name' => 'dashboard#page', 'url' => '/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']],
// SPA catch-all — same controller as the index route; must use a distinct route name
// (duplicate names replace the earlier route in Symfony, which breaks GET /).
['name' => 'dashboard#catchAll', 'url' => '/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+'], 'defaults' => ['path' => '']],
],
];
30 changes: 0 additions & 30 deletions appspec/README.md

This file was deleted.

34 changes: 34 additions & 0 deletions l10n/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"translations": {
"App Template settings": "App Template settings",
"Configure the app settings": "Configure the app settings",
"Configuration": "Configuration",
"Completed": "Completed",
"Dashboard": "Dashboard",
"Due this week": "Due this week",
"Open items": "Open items",
"Placeholder: comment added": "Placeholder: comment added",
"Placeholder: status changed to Review": "Placeholder: status changed to Review",
"Placeholder: user opened a record": "Placeholder: user opened a record",
"Quick actions": "Quick actions",
"Recent activity": "Recent activity",
"Starter overview with sample KPIs and activity placeholders. Replace this view with your own data.": "Starter overview with sample KPIs and activity placeholders. Replace this view with your own data.",
"Team members": "Team members",
"Wire buttons here to create records, open lists, or deep links. Use the sidebar for Settings and Documentation.": "Wire buttons here to create records, open lists, or deep links. Use the sidebar for Settings and Documentation.",
"sample": "sample",
"Documentation": "Documentation",
"General": "General",
"Install OpenRegister": "Install OpenRegister",
"No settings available yet": "No settings available yet",
"OpenRegister is required": "OpenRegister is required",
"OpenRegister register ID": "OpenRegister register ID",
"Register": "Register",
"Save": "Save",
"Settings": "Settings",
"Settings saved successfully": "Settings saved successfully",
"Saving...": "Saving...",
"This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.": "This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.",
"User settings will appear here in a future update.": "User settings will appear here in a future update."
},
"plurals": ""
}
34 changes: 34 additions & 0 deletions l10n/nl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"translations": {
"App Template settings": "App Template instellingen",
"Configure the app settings": "Configureer de app-instellingen",
"Configuration": "Configuratie",
"Completed": "Afgerond",
"Dashboard": "Dashboard",
"Due this week": "Deze week vervallen",
"Open items": "Openstaande items",
"Placeholder: comment added": "Placeholder: reactie toegevoegd",
"Placeholder: status changed to Review": "Placeholder: status gewijzigd naar Review",
"Placeholder: user opened a record": "Placeholder: gebruiker opende een record",
"Quick actions": "Snelle acties",
"Recent activity": "Recente activiteit",
"Starter overview with sample KPIs and activity placeholders. Replace this view with your own data.": "Startoverzicht met voorbeeld-KPI's en activiteitsplaceholders. Vervang dit scherm door je eigen gegevens.",
"Team members": "Teamleden",
"Wire buttons here to create records, open lists, or deep links. Use the sidebar for Settings and Documentation.": "Koppel hier knoppen aan het aanmaken van records, lijsten of deep links. Gebruik de zijbalk voor Instellingen en Documentatie.",
"sample": "voorbeeld",
"Documentation": "Documentatie",
"General": "Algemeen",
"Install OpenRegister": "OpenRegister installeren",
"No settings available yet": "Nog geen instellingen beschikbaar",
"OpenRegister is required": "OpenRegister is vereist",
"OpenRegister register ID": "OpenRegister register-ID",
"Register": "Register",
"Save": "Opslaan",
"Settings": "Instellingen",
"Settings saved successfully": "Instellingen succesvol opgeslagen",
"Saving...": "Opslaan...",
"This app needs OpenRegister to store and manage data. Please install OpenRegister from the app store to get started.": "Deze app heeft OpenRegister nodig om gegevens op te slaan en te beheren. Installeer OpenRegister via de app store om te beginnen.",
"User settings will appear here in a future update.": "Gebruikersinstellingen verschijnen hier in een toekomstige update."
},
"plurals": ""
}
20 changes: 12 additions & 8 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

namespace OCA\AppTemplate\AppInfo;

use OCA\AppTemplate\Listener\DeepLinkRegistrationListener;
use OCA\AppTemplate\Repair\InitializeSettings;
use OCA\OpenRegister\Event\DeepLinkRegistrationEvent;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
Expand Down Expand Up @@ -54,14 +57,15 @@ public function __construct()
*/
public function register(IRegistrationContext $context): void
{
/*
* Register your event listeners and services here.
* Example:
* $context->registerEventListener(
* event: SomeEvent::class,
* listener: SomeListener::class
* ).
*/
// Register deep link patterns with OpenRegister's unified search provider.
// Only fires when OpenRegister is installed and dispatches the event.
$context->registerEventListener(
event: DeepLinkRegistrationEvent::class,
listener: DeepLinkRegistrationListener::class
);

// Initialize register and schemas on install/upgrade.
$context->registerRepairStep(InitializeSettings::class);

}//end register()

Expand Down
13 changes: 13 additions & 0 deletions lib/Controller/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,17 @@ public function page(): TemplateResponse
{
return new TemplateResponse(Application::APP_ID, 'index');
}//end page()

/**
* Serve the SPA for deep links (Vue history mode). Delegates to {@see page()}.
*
* @NoAdminRequired
* @NoCSRFRequired
*
* @return TemplateResponse
*/
public function catchAll(): TemplateResponse
{
return $this->page();
}//end catchAll()
}//end class
Loading
Loading