AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Nextcloud Syntax File Api

skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-syntax-file-api · by Impertio-Studio

>

No reviews yet
0 installs
14 views
0.0% view→install

Install

$ agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-syntax-file-api

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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 Used
  • 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-syntax-file-api)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

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 →
Are you the author of Nextcloud Syntax File Api? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

nextcloud-syntax-file-api

Quick Reference

Entry Points

| Interface | Namespace | How to Get | |-----------|-----------|------------| | IRootFolder | OCP\Files\IRootFolder | Constructor injection (DI) | | IUserFolder | OCP\Files\Folder | $rootFolder->getUserFolder($userId) |

ALWAYS inject IRootFolder via constructor -- NEVER instantiate it directly.

ALWAYS use getUserFolder($userId) to access a user's files -- NEVER construct paths manually to /userid/files/.

Node Hierarchy

| Interface | Namespace | Represents | |-----------|-----------|------------| | Node | OCP\Files\Node | Base: any filesystem entry | | File | OCP\Files\File | A file (extends Node) | | Folder | OCP\Files\Folder | A folder (extends Node) | | FileInfo | OCP\Files\FileInfo | Metadata-only interface |

Key Folder Methods

| Method | Return | Description | |--------|--------|-------------| | get(string $path) | Node | Get node by relative path | | getById(int $id) | Node[] | Get nodes by file ID (returns ARRAY) | | newFile(string $path, ?string $content = null) | File | Create new file | | newFolder(string $path) | Folder | Create new folder | | nodeExists(string $path) | bool | Check if path exists | | getDirectoryListing() | Node[] | List immediate children | | search(ISearchQuery $query) | Node[] | Search within folder | | getById(int $id) | Node[] | Lookup by filecache ID |

Key File Methods

| Method | Return | Description | |--------|--------|-------------| | getContent() | string | Read entire file content | | putContent(string $data) | void | Write/overwrite file content | | fopen(string $mode) | resource\|false | Open file as stream | | getMimeType() | string | Get MIME type | | getSize() | int\|float | Get file size in bytes |

Common Node Methods (File and Folder)

| Method | Return | Description | |--------|--------|-------------| | getName() | string | Filename or folder name | | getPath() | string | Full internal path | | getInternalPath() | string | Path relative to storage | | getId() | int | Filecache ID | | getEtag() | string | ETag for caching | | getMTime() | int | Last modified timestamp | | getStorage() | IStorage | Underlying storage backend | | getParent() | Folder | Parent folder | | delete() | void | Delete this node | | move(string $targetPath) | Node | Move/rename node | | copy(string $targetPath) | Node | Copy node | | lock(int $type) | void | Acquire lock | | unlock(int $type) | void | Release lock |

File Events (OCP\Files\Events\Node\)

| Event | Trigger | Available Data | |-------|---------|----------------| | BeforeNodeCreatedEvent | Before file/folder creation | getNode() | | NodeCreatedEvent | After file/folder creation | getNode() | | BeforeNodeWrittenEvent | Before content write | getNode() | | NodeWrittenEvent | After content write | getNode() | | BeforeNodeDeletedEvent | Before deletion | getNode() | | NodeDeletedEvent | After deletion | getNode() | | BeforeNodeRenamedEvent | Before rename/move | getSource(), getTarget() | | NodeRenamedEvent | After rename/move | getSource(), getTarget() | | BeforeNodeCopiedEvent | Before copy | getSource(), getTarget() | | NodeCopiedEvent | After copy | getSource(), getTarget() | | BeforeNodeTouchedEvent | Before mtime update | getNode() | | NodeTouchedEvent | After mtime update | getNode() |

Critical Warnings

NEVER assume getById() returns a single node -- it returns an array because a file can appear in multiple mount points (external storage, group folders). ALWAYS access $nodes[0] after checking the array is not empty.

NEVER access files without wrapping in try/catch for NotFoundException -- the file may have been deleted between check and access.

NEVER use hardcoded absolute paths like /admin/files/Documents/file.txt -- ALWAYS use getUserFolder() which returns the user's files root.

NEVER call getContent() on large files -- use fopen() with streaming for files larger than a few MB.

ALWAYS check instanceof File or instanceof Folder after get() -- the return type is Node which could be either.

ALWAYS inject IRootFolder and call getUserFolder() -- NEVER try to inject IUserFolder directly (it is not a registered DI service).

ALWAYS register file event listeners in lib/AppInfo/Application.php register() method using $context->registerEventListener().


Decision Tree: File Access

Need to access files?
├── Which user's files? → getUserFolder($userId)
│   ├── Know the path? → $userFolder->get('path/to/file')
│   │   ├── Is it a File? → instanceof \OCP\Files\File → getContent() / putContent()
│   │   └── Is it a Folder? → instanceof \OCP\Files\Folder → getDirectoryListing()
│   ├── Know the file ID? → $userFolder->getById($id)
│   │   └── ALWAYS check: if (empty($nodes)) { throw ... }
│   │   └── Use $nodes[0] for first match
│   ├── Need to create? → Does it exist?
│   │   ├── nodeExists() returns true → get() then putContent()
│   │   └── nodeExists() returns false → newFile() or newFolder()
│   └── Need to search? → $userFolder->search(ISearchQuery)
└── System-level access? → Use IRootFolder directly (admin only)

Essential Patterns

Pattern 1: Service with File Access

namespace OCA\MyApp\Service;

use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;

class FileService {
    public function __construct(
        private IRootFolder $rootFolder,
    ) {}

    public function readFile(string $userId, string $path): string {
        $userFolder = $this->rootFolder->getUserFolder($userId);
        try {
            $node = $userFolder->get($path);
            if (!($node instanceof File)) {
                throw new \RuntimeException('Not a file');
            }
            return $node->getContent();
        } catch (NotFoundException $e) {
            throw new NotFoundException('File not found: ' . $path);
        }
    }

    public function writeFile(string $userId, string $path, string $content): void {
        $userFolder = $this->rootFolder->getUserFolder($userId);
        try {
            $file = $userFolder->get($path);
            if (!($file instanceof File)) {
                throw new \RuntimeException('Not a file');
            }
            $file->putContent($content);
        } catch (NotFoundException $e) {
            $userFolder->newFile($path, $content);
        }
    }
}

Pattern 2: Safe getById() Usage

public function getFileById(string $userId, int $fileId): File {
    $userFolder = $this->rootFolder->getUserFolder($userId);
    $nodes = $userFolder->getById($fileId);

    if (empty($nodes)) {
        throw new NotFoundException('File not found for ID: ' . $fileId);
    }

    $node = $nodes[0];
    if (!($node instanceof File)) {
        throw new \RuntimeException('Node is not a file');
    }

    return $node;
}

Pattern 3: Folder Listing with Type Filtering

public function listFiles(string $userId, string $folderPath): array {
    $userFolder = $this->rootFolder->getUserFolder($userId);
    try {
        $folder = $userFolder->get($folderPath);
        if (!($folder instanceof Folder)) {
            throw new \RuntimeException('Not a folder');
        }

        $result = [];
        foreach ($folder->getDirectoryListing() as $node) {
            $result[] = [
                'name' => $node->getName(),
                'type' => $node instanceof File ? 'file' : 'folder',
                'size' => $node->getSize(),
                'mtime' => $node->getMTime(),
                'mimetype' => $node instanceof File ? $node->getMimeType() : 'httpd/unix-directory',
            ];
        }
        return $result;
    } catch (NotFoundException $e) {
        throw new NotFoundException('Folder not found: ' . $folderPath);
    }
}

Pattern 4: File Event Listener Registration

// lib/AppInfo/Application.php
namespace OCA\MyApp\AppInfo;

use OCA\MyApp\Listener\FileCreatedListener;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Files\Events\Node\NodeCreatedEvent;

class Application extends App implements IBootstrap {
    public const APP_ID = 'myapp';

    public function __construct() {
        parent::__construct(self::APP_ID);
    }

    public function register(IRegistrationContext $context): void {
        $context->registerEventListener(
            NodeCreatedEvent::class,
            FileCreatedListener::class
        );
    }

    public function boot(IBootContext $context): void {}
}
// lib/Listener/FileCreatedListener.php
namespace OCA\MyApp\Listener;

use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\Files\File;
use Psr\Log\LoggerInterface;

/** @template-implements IEventListener */
class FileCreatedListener implements IEventListener {
    public function __construct(
        private LoggerInterface $logger,
    ) {}

    public function handle(Event $event): void {
        if (!($event instanceof NodeCreatedEvent)) {
            return;
        }

        $node = $event->getNode();
        if (!($node instanceof File)) {
            return;
        }

        $this->logger->info('File created: ' . $node->getPath());
    }
}

Pattern 5: Streaming Large Files

public function streamFile(string $userId, string $path): void {
    $userFolder = $this->rootFolder->getUserFolder($userId);
    $file = $userFolder->get($path);

    if (!($file instanceof File)) {
        throw new \RuntimeException('Not a file');
    }

    $handle = $file->fopen('r');
    if ($handle === false) {
        throw new \RuntimeException('Cannot open file');
    }

    try {
        while (!feof($handle)) {
            echo fread($handle, 8192);
            flush();
        }
    } finally {
        fclose($handle);
    }
}

Pattern 6: Create or Update (Upsert)

public function upsertFile(string $userId, string $path, string $content): File {
    $userFolder = $this->rootFolder->getUserFolder($userId);

    if ($userFolder->nodeExists($path)) {
        $file = $userFolder->get($path);
        if (!($file instanceof File)) {
            throw new \RuntimeException('Path exists but is not a file');
        }
        $file->putContent($content);
        return $file;
    }

    return $userFolder->newFile($path, $content);
}

Reference Links

  • [references/methods.md](references/methods.md) -- Complete IRootFolder, File, Folder, Node method tables
  • [references/examples.md](references/examples.md) -- File CRUD, events, storage access examples
  • [references/anti-patterns.md](references/anti-patterns.md) -- File handling mistakes and corrections

Official Sources

  • https://docs.nextcloud.com/server/latest/developermanual/diggingdeeper/files.html
  • https://docs.nextcloud.com/server/latest/developermanual/diggingdeeper/events.html
  • https://docs.nextcloud.com/server/latest/developer_manual/basics/storage.html

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.