Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wopi Bundle Laravel Package

champs-libres/wopi-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Bundle

    composer require champs-libres/wopi-bundle
    

    Add to config/bundles.php:

    return [
        // ...
        ChampsLibres\WopiBundle\WopiBundle::class => ['all' => true],
    ];
    
  2. Configure WOPI Endpoints Define routes in config/routes.yaml:

    wopi:
        resource: "@WopiBundle/Resources/config/routing.yaml"
        prefix: /wopi
    
  3. Implement WOPI Handler Create a custom handler extending ChampsLibres\WopiBundle\Handler\WopiHandlerInterface:

    use ChampsLibres\WopiBundle\Handler\WopiHandlerInterface;
    use ChampsLibres\WopiBundle\Wopi\WopiRequest;
    
    class CustomWopiHandler implements WopiHandlerInterface
    {
        public function checkLock(WopiRequest $request): bool { /* ... */ }
        public function getFileInfo(WopiRequest $request): array { /* ... */ }
        public function getFile(WopiRequest $request): string { /* ... */ }
        // ... other required methods
    }
    
  4. Register Handler in Services

    # config/services.yaml
    services:
        ChampsLibres\WopiBundle\Handler\WopiHandlerInterface:
            class: App\Handler\CustomWopiHandler
    
  5. Test with Collabora Online Configure Collabora’s wopi.json to point to your /wopi endpoint and test file operations.


First Use Case: File Previews

Use the getFileInfo method to return metadata (e.g., file size, MIME type) for Collabora to render previews:

public function getFileInfo(WopiRequest $request): array
{
    $file = $this->storage->find($request->getFileId());
    return [
        'BaseFileName' => $file->getName(),
        'Size' => $file->getSize(),
        'Url' => $this->generateUrl('wopi_download', ['id' => $file->getId()]),
        'UserId' => $request->getUserId(),
        'UserCanWrite' => $request->getUserCanWrite(),
    ];
}

Implementation Patterns

Core Workflow: WOPI Request Handling

  1. Incoming Requests The bundle routes WOPI requests (e.g., GET /wopi/files/{fileId}) to your handler via the WopiHandlerInterface. Example request object:

    $request = new WopiRequest(
        $fileId,
        $userId,
        $accessToken,
        $userCanWrite,
        $request->query->all()
    );
    
  2. Locking Mechanism Implement checkLock() to validate concurrent edits:

    public function checkLock(WopiRequest $request): bool
    {
        $lock = $this->lockService->getLock($request->getFileId());
        return $lock->isActive() && $lock->getUserId() === $request->getUserId();
    }
    
  3. File Operations

    • Download: Return raw file content via getFile().
      public function getFile(WopiRequest $request): string
      {
          return $this->storage->read($request->getFileId());
      }
      
    • Upload: Handle PUT requests in putFile().
      public function putFile(WopiRequest $request, string $content): void
      {
          $this->storage->save($request->getFileId(), $content);
      }
      
  4. Permissions Use getUserCanWrite() to enforce edit restrictions:

    if (!$request->getUserCanWrite()) {
        throw new \RuntimeException('User lacks write permissions.');
    }
    

Integration Tips

  1. Storage Abstraction Decouple storage logic by injecting a service (e.g., Filesystem, Doctrine ORM, or S3):

    $this->storage = $container->get('your.storage.service');
    
  2. Security

    • Validate access_token against your auth system (e.g., JWT).
    • Sanitize fileId to prevent path traversal:
      if (!preg_match('/^[a-f0-9]{32}$/', $fileId)) {
          throw new \InvalidArgumentException('Invalid file ID.');
      }
      
  3. CORS Configuration Ensure Collabora’s domain is whitelisted in Symfony’s CORS settings:

    # config/packages/nelmio_cors.yaml
    nelmio_cors:
        defaults:
            allow_origin: ["https://your-collabora-domain.com"]
    
  4. Logging Log WOPI actions for debugging:

    $this->logger->info('WOPI action', [
        'file_id' => $request->getFileId(),
        'action' => $request->getAction(),
        'user_id' => $request->getUserId(),
    ]);
    
  5. Testing Use WopiRequest in unit tests:

    $request = new WopiRequest('file123', 'user456', 'token789', true, ['action' => 'getfileinfo']);
    $this->assertEquals(['Size' => 1024], $handler->getFileInfo($request));
    

Gotchas and Tips

Pitfalls

  1. Missing Required Methods The WopiHandlerInterface requires all methods (e.g., getLock, refreshLock). Omitting any will throw BadMethodCallException. Fix: Implement a skeleton class:

    class BaseWopiHandler implements WopiHandlerInterface {
        public function getLock(WopiRequest $request) { /* ... */ }
        // ... stub all methods
    }
    
  2. Incorrect File IDs WOPI expects fileId to be a URL-encoded string. Decode it before use:

    $fileId = rawurldecode($request->getFileId());
    
  3. CORS Preflight Issues Collabora sends OPTIONS requests before POST/PUT. Ensure your CORS middleware handles them:

    nelmio_cors:
        paths:
            '^/wopi/':
                methods: [GET, POST, PUT, OPTIONS]
                allow_origin: ["*"] # Adjust in production!
    
  4. Lock Expiry Collabora expects locks to expire after inactivity. Implement refreshLock() to extend lock duration:

    public function refreshLock(WopiRequest $request): void
    {
        $lock = $this->lockService->getLock($request->getFileId());
        $lock->extend(300); // 5-minute extension
    }
    
  5. Large File Handling For files >100MB, use chunked uploads (PUT with Content-Range headers). Validate ranges in putFile():

    if (isset($_SERVER['HTTP_CONTENT_RANGE'])) {
        $range = explode('-', $_SERVER['HTTP_CONTENT_RANGE']);
        $this->storage->appendChunk($request->getFileId(), $range[0], $content);
    }
    

Debugging Tips

  1. Enable WOPI Debugging Set WOPI_DEBUG in .env to log raw requests/responses:

    WOPI_DEBUG=true
    

    Logs appear in var/log/dev.log.

  2. Validate WOPI Responses Use WOPI Validator to test endpoints:

    wopi-validator validate http://your-app/wopi/files/123 --action=getfileinfo
    
  3. Common Errors

    • 403 Forbidden: Missing access_token or invalid permissions. Debug: Check getUserCanWrite() logic.
    • 500 Internal Server Error: Unhandled exceptions in your handler. Debug: Enable APP_DEBUG=true and inspect logs.
    • 404 Not Found: Incorrect route configuration. Debug: Verify routing.yaml and prefix in config/routes.yaml.

Extension Points

  1. Custom Actions Extend the bundle by adding new WOPI actions (e.g., copy, rename):

    public function copyFile(WopiRequest $request, string $newFileId): void
    {
        $this->storage->copy($request->getFileId(), $newFileId);
    }
    

    Register the action in routing.yaml:

    wopi_copy:
        path: /files/{fileId}/copy
        methods: [POST]
        defaults: { _controller: 'App\Controller\WopiController::copy' }
    
  2. Middleware Integration Add Symfony middleware to pre-process

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours