Install
$ agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-syntax-controllers ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
nextcloud-syntax-controllers
Quick Reference
Controller Types
| Class | Base Path | Use Case | |-------|-----------|----------| | OCP\AppFramework\Controller | /index.php/apps/{appid}/ | Page rendering, internal endpoints | | OCP\AppFramework\OCSController | /ocs/v2.php/apps/{appid}/ | OCS API with JSON/XML envelope | | OCP\AppFramework\ApiController | /index.php/apps/{appid}/ | REST API (extends Controller, adds CORS) |
Route Definition Arrays (appinfo/routes.php)
| Array | URL Prefix | Controller Base | |-------|-----------|-----------------| | routes | /index.php/apps/{appid}/ | Controller or ApiController | | ocs | /ocs/v2.php/apps/{appid}/ | OCSController | | resources | /index.php/apps/{appid}/ | Auto-generates 5 CRUD routes |
Route Name Resolution
| Route Name | Controller Class | Method | |------------|-----------------|--------| | page#index | PageController | index() | | author_api#some_method | AuthorApiController | someMethod() | | api#getData | ApiController | getData() |
Parameter Extraction
| Source | Example | Extraction | |--------|---------|------------| | URL path | /authors/{id} | $id parameter | | Query string | ?name=john | $name parameter | | JSON body | {"title": "Test"} | $title parameter | | Form data | name=john | $name parameter | | Default value | $page = 1 | Falls back if not in request |
Type Casting (via type hints or @param)
| Type | PHP Hint | Notes | |------|----------|-------| | int / integer | int $id | Numeric string to int | | float | float $value | Numeric string to float | | bool / boolean | bool $flag | "true"/"1" to true | | string | string $name | Default, no conversion | | array | array $items | From JSON body |
Security Attributes (NC 27+)
| Attribute | Effect | |-----------|--------| | #[NoAdminRequired] | Allow non-admin authenticated users | | #[PublicPage] | No login required | | #[NoCSRFRequired] | Skip CSRF token validation | | #[NoTwoFactorRequired] | Bypass 2FA requirement | | #[UserRateLimit(limit: 5, period: 100)] | Rate limit for logged-in users | | #[AnonRateLimit(limit: 1, period: 100)] | Rate limit for anonymous users | | #[BruteForceProtection(action: 'login')] | Enable brute force throttling |
Default security posture (no attributes): admin-only, authenticated, 2FA required, CSRF validated.
Response Types
| Class | Use Case | |-------|----------| | TemplateResponse | Render server-side PHP template | | PublicTemplateResponse | Public page with header actions | | JSONResponse | JSON data with HTTP status | | DataResponse | Generic data (format via responder) | | RedirectResponse | HTTP redirect | | DownloadResponse | File download | | StreamResponse | Stream file contents |
Critical Warnings
NEVER create controller methods without security attributes and assume they are public -- the default is admin-only. Explicitly add #[NoAdminRequired] for regular user access or #[PublicPage] for anonymous access.
NEVER use #[NoCSRFRequired] on state-changing endpoints without alternative protection -- either require the OCS-APIRequest: true header or use token-based authentication.
NEVER use #[PublicPage] + #[NoCSRFRequired] on state-changing endpoints without additional authentication -- this leaves the endpoint completely unprotected.
NEVER return a response without the correct HTTP status code -- use the Http::STATUS_* constants for clarity.
ALWAYS extend OCSController for OCS API endpoints -- using Controller with OCS routes will break the response envelope.
ALWAYS define routes in appinfo/routes.php -- controllers without routes are unreachable.
ALWAYS use constructor injection for dependencies -- Nextcloud auto-wires by type hint.
ALWAYS call $response->throttle() only on failure conditions when using #[BruteForceProtection] -- never on success.
Essential Patterns
Pattern 1: Page Controller with Template
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest;
class PageController extends Controller {
public function __construct(
string $appName,
IRequest $request,
private MyService $service,
) {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
#[NoCSRFRequired]
public function index(): TemplateResponse {
return new TemplateResponse($this->appName, 'main');
}
}
// appinfo/routes.php
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
],
];
Pattern 2: OCS API Controller
namespace OCA\MyApp\Controller;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
class ItemApiController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
private ItemService $service,
private ?string $userId,
) {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
public function index(): DataResponse {
return new DataResponse($this->service->findAll($this->userId));
}
#[NoAdminRequired]
public function show(int $id): DataResponse {
return new DataResponse($this->service->find($id, $this->userId));
}
#[NoAdminRequired]
public function create(string $title, string $content = ''): DataResponse {
return new DataResponse(
$this->service->create($title, $content, $this->userId)
);
}
}
// appinfo/routes.php
return [
'ocs' => [
['name' => 'item_api#index', 'url' => '/api/v1/items', 'verb' => 'GET'],
['name' => 'item_api#show', 'url' => '/api/v1/items/{id}', 'verb' => 'GET'],
['name' => 'item_api#create', 'url' => '/api/v1/items', 'verb' => 'POST'],
],
];
Pattern 3: Resource Routes (Auto-CRUD)
// appinfo/routes.php
return [
'resources' => [
'author' => ['url' => '/authors'],
],
];
This auto-generates five routes mapping to AuthorController:
| Verb | URL | Method | |------|-----|--------| | GET | /authors | index() | | GET | /authors/{id} | show(int $id) | | POST | /authors | create() | | PUT | /authors/{id} | update(int $id) | | DELETE | /authors/{id} | destroy(int $id) |
Pattern 4: Attribute-Based Routing (NC 29+)
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\ApiRoute;
class PageController extends Controller {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'GET', url: '/')]
public function index(): TemplateResponse {
return new TemplateResponse($this->appName, 'main');
}
}
class ItemApiController extends OCSController {
#[NoAdminRequired]
#[ApiRoute(verb: 'GET', url: '/api/v1/items')]
public function index(): DataResponse {
return new DataResponse($this->service->findAll());
}
}
Pattern 5: Error Handling with Status Codes
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
#[NoAdminRequired]
public function show(int $id): JSONResponse {
try {
return new JSONResponse($this->service->find($id, $this->userId));
} catch (NotFoundException $e) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
} catch (ForbiddenException $e) {
return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_FORBIDDEN);
}
}
Pattern 6: Complete routes.php with All Three Arrays
[
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#settings', 'url' => '/settings', 'verb' => 'GET'],
[
'name' => 'file#download',
'url' => '/files/{path}',
'verb' => 'GET',
'requirements' => ['path' => '.+'], // match slashes in path
],
[
'name' => 'page#list',
'url' => '/list',
'verb' => 'GET',
'defaults' => ['page' => 1, 'limit' => 20],
],
],
'ocs' => [
['name' => 'api#getData', 'url' => '/api/v1/data', 'verb' => 'GET'],
['name' => 'api#updateData', 'url' => '/api/v1/data/{id}', 'verb' => 'PUT'],
],
'resources' => [
'author' => ['url' => '/authors'],
],
];
Responder System (Format Negotiation)
Priority order for response format:
?format=xmlor?format=jsonquery parameter (highest)AcceptHTTP header- Default:
json
OCSController handles this automatically -- DataResponse is rendered as JSON or XML based on negotiation.
For custom formats, register a responder:
class MyController extends Controller {
public function __construct(string $appName, IRequest $request) {
parent::__construct($appName, $request);
$this->registerResponder('xml', function ($data) {
return new XMLResponse($data);
});
}
}
URL Generation
use OCP\IURLGenerator;
// Route URL: /index.php/apps/myapp/authors/3
$url = $urlGenerator->linkToRoute('myapp.author.show', ['id' => 3]);
// OCS route URL: /ocs/v2.php/apps/myapp/api/v1/data
$url = $urlGenerator->linkToOCSRoute('myapp.api.getData');
Route name format: {appid}.{controller}.{method} where hashes become dots and underscores are preserved.
Reference Links
- [references/methods.md](references/methods.md) -- Controller types, route syntax, response types, parameter extraction
- [references/examples.md](references/examples.md) -- Controller patterns, routing patterns
- [references/anti-patterns.md](references/anti-patterns.md) -- Controller mistakes
Official Sources
- https://docs.nextcloud.com/server/latest/developermanual/diggingdeeper/controllers.html
- https://docs.nextcloud.com/server/latest/developer_manual/basics/routing.html
- https://docs.nextcloud.com/server/latest/developermanual/diggingdeeper/middleware.html
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Nextcloud-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.