champs-libres / wopi-bundle
提供在 Symfony 和 WOPI 连接器之间路由和粘合代码的包。
Requires
- php: >= 7.4
- ext-json: *
- ext-mbstring: *
- champs-libres/wopi-lib: dev-master
- loophp/psr-http-message-bridge-bundle: ^1
- symfony/expression-language: >= 4.4
- symfony/framework-bundle: >= 4.4
- symfony/http-client: >= 4.4
- symfony/security-core: >= 4.4
Requires (Dev)
- drupol/php-conventions: ^5.0
- friends-of-phpspec/phpspec-code-coverage: ^6.1
- nyholm/psr7: ^1.4
- phpspec/phpspec: ^7.1
- phpspec/prophecy: ^1.16
- phpspec/prophecy-phpunit: ^2.0
- phpunit/phpunit: ^9.5
- symfony/phpunit-bridge: ^6.2
This package is auto-updated.
Last update: 2024-09-19 17:18:47 UTC
README
WOPI 包
一个用于简化 WOPI 端点和协议实现的 Symfony 扩展包。
描述
Web 应用程序开放平台接口(WOPI)协议让您可以将 Office for the web 与您的应用程序集成,还可以与其他软件如 Collabora Online 集成。
目前,此包的目标是集成 Collabora Online。
将来,此包可能能够通过 验证,以便与 Office for the Web 一起使用。
Collabora Online 集成
WOPI 协议概述
Web 级 Office 平台
安装
composer require champs-libres/wopi-bundle
使用方法
此包为 Symfony 提供了协议的基本实现。但是,有许多方法可以
- 在应用程序中存储文档;
- 确保协议的安全性
- 并管理权限,根据您自己的业务逻辑。
因此,此包不提供通过 基本接口 从 champs-libres/wopi-lib 包中描述的 WOPI 协议的具体实现。
因此,此包提供
一些术语
- Wopi 服务器:实现此包的应用程序;
- Wopi 客户端:Collabora Online(或 Office 365),它将使用您的应用程序(服务器)提供的端点;
- 编辑器:Collabora Online(或 office 365)。Wopi 客户端的同义词。
以下是集成 wopi 包到您的应用程序的步骤
启动编辑器/您的 wopi 客户端进行开发
您将在 CODE 项目中找到一个免费的 collabora online: CODE。
⚠️编辑器必须可以访问您的应用程序,与浏览器打开您的应用程序的域名相同。
如果您使用 docker 和 docker-compose,您可以通过修改您的 /etc/hosts
文件来实现这一点
# docker-compose.yaml services: app: # your php / symfony application # we assume that your app listen **inside the container** on the port 8001 (no port mapping required between inside and # outside of the container) # ... collabora: image: collabora/code:latest environment: - SLEEPFORDEBUGGER=0 - DONT_GEN_SSL_CERT="True" - extra_params=--o:ssl.enable=false --o:ssl.termination=false - username=admin - password=admin - dictionaries=en_US - aliasgroup1=http://nginx:8001 ports: - "127.0.0.1:9980:9980" cap_add: - MKNOD links: - app
# /etc/hosts
127.0.0.1 app collabora
使用此配置,您应该可以通过 http://collabora:9980 访问 collabora,并通过 http://app:8001 访问您的应用程序。您必须在调试 collabora 功能时使用后者访问您的应用程序。
配置此包
# app/config/package/wopi.yaml wopi: # this is the path to your server. # note: the wopi client (Collabora) must be able to your app **using the same domains as your browser** server: http://collabora:9980
创建您的文档实体
每个编辑的文档都应该是一个实现 Document
的实体。
创建您的文档管理器
您的经理将实现 DocumentManagerInterface
。
这个DocumentManager将处理您应用程序中的文档逻辑。它提供写入文档和从中提取一些信息的方法。
您可以在这里找到一个实现。
创建您的访问令牌逻辑
access_token
由您的应用程序创建,当它打开编辑页面时(剧透:编辑页面将是一个iframe)。Wopi主机(您的应用程序)将在每次客户端发起请求时接收此访问令牌。每个令牌的有效期为10小时。
您可以选择自己的逻辑。但JWT可以简化您的任务。
使用LexikJWT的一些工作配置
使用JWT (Json Web Token)轻松验证您的请求。这可以通过LexikJWTAuthenticationBundle轻松实现。
创建一个防火墙并配置以/wopi
开头的url的访问控制
# config/package/security.yaml security: firewalls: wopi: pattern: ^/wopi stateless: true guard: authenticators: - lexik_jwt_authentication.jwt_token_authenticator access_control: # ... - { path: ^/wopi, roles: IS_AUTHENTICATED_FULLY } # ...
配置lexik
# config/package/lexik_jwt_authentication.yaml lexik_jwt_authentication: # required for wopi - recommended duration for token ttl token_ttl: 36000 # required for wopi: the token is in query, with `?access_token=<your_token>` token_extractors: query_parameter: enabled: true name: access_token
查看一个工作实现:https://gitea.champs-libres.be/Chill-project/chill-skeleton-basic
提供有关您的用户的信息
实现UserManagerInterface
以提供有关用户的信息。
这些信息应通过访问令牌提取。
提供有关权限/授权的信息
实现AuthorizationManagerInterface
以提供有关给定文档的权限信息。
绑定所有服务
此捆绑包将需要实现按照接口命名。
一些示例
namespace Symfony\Component\DependencyInjection\Loader\Configurator; use ChampsLibres\WopiBundle\Contracts\AuthorizationManagerInterface; use ChampsLibres\WopiBundle\Contracts\UserManagerInterface; use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; use Chill\WopiBundle\Service\Wopi\AuthorizationManager; use Chill\WopiBundle\Service\Wopi\ChillDocumentManager; use Chill\WopiBundle\Service\Wopi\UserManager; return static function (ContainerConfigurator $container) { $services = $container ->services(); $services ->defaults() ->autowire() ->autoconfigure(); $services ->set(ChillDocumentManager::class); $services ->alias(DocumentManagerInterface::class, ChillDocumentManager::class); $services ->set(AuthorizationManager::class); $services->alias(AuthorizationManagerInterface::class, AuthorizationManager::class); $services ->set(UserManager::class); $services->alias(UserManagerInterface::class, UserManager::class); };
创建一个编辑页面
编辑页面将是一个通过iframe加载编辑器的页面。
这里是一个控制器
<?php declare(strict_types=1); namespace App\Controller; use ChampsLibres\WopiLib\Contract\Service\Configuration\ConfigurationInterface; use ChampsLibres\WopiLib\Contract\Service\Discovery\DiscoveryInterface; use ChampsLibres\WopiLib\Contract\Service\DocumentManagerInterface; use Chill\DocStoreBundle\Entity\StoredObject; use Chill\MainBundle\Entity\User; use Chill\WopiBundle\Service\Controller\ResponderInterface; use Exception; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use loophp\psr17\Psr17Interface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Security; final class Editor { private DocumentManagerInterface $documentManager; private JWTTokenManagerInterface $JWTTokenManager; private Psr17Interface $psr17; private ResponderInterface $responder; private RouterInterface $router; private Security $security; private ConfigurationInterface $wopiConfiguration; private DiscoveryInterface $wopiDiscovery; public function __construct( ConfigurationInterface $wopiConfiguration, DiscoveryInterface $wopiDiscovery, DocumentManagerInterface $documentManager, JWTTokenManagerInterface $JWTTokenManager, ResponderInterface $responder, Security $security, Psr17Interface $psr17, RouterInterface $router ) { $this->documentManager = $documentManager; $this->JWTTokenManager = $JWTTokenManager; $this->wopiConfiguration = $wopiConfiguration; $this->wopiDiscovery = $wopiDiscovery; $this->responder = $responder; $this->security = $security; $this->psr17 = $psr17; $this->router = $router; } public function __invoke(string $fileId): Response { if (null === $user = $this->security->getUser()) { throw new AccessDeniedHttpException('Please authenticate to access this feature'); } $configuration = $this->wopiConfiguration->jsonSerialize(); $storedObject = $this->documentManager->findByDocumentId($fileId); if (null === $storedObject) { throw new NotFoundHttpException(sprintf('Unable to find object %s', $fileId)); } if ([] === $discoverExtension = $this->wopiDiscovery->discoverMimeType($storedObject->getType())) { throw new Exception(sprintf('Unable to find mime type %s', $storedObject->getType())); } $configuration['favIconUrl'] = ''; $configuration['access_token'] = $this->JWTTokenManager->createFromPayload($user, [ 'UserCanWrite' => true, 'UserCanAttend' => true, 'UserCanPresent' => true, 'fileId' => $fileId, ]); // we parse the jwt to get the access_token_ttl // reminder: access_token_ttl is a javascript epoch, not a number of seconds; it is the // time when the token will expire, not the time to live: // https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/concepts#the-access_token_ttl-property $jwt = $this->JWTTokenManager->parse($configuration['access_token']); $configuration['access_token_ttl'] = $jwt['exp'] * 1000; $configuration['server'] = $this ->psr17 ->createUri($discoverExtension[0]['urlsrc']) ->withQuery( http_build_query( [ 'WOPISrc' => $this ->router ->generate( 'checkFileInfo', [ 'fileId' => $this->documentManager->getDocumentId($storedObject), ], UrlGeneratorInterface::ABSOLUTE_URL ), 'closebutton' => 1, ] ) ); return $this ->responder ->render( '@Wopi/Editor/page.html.twig', $configuration ); } }
故障排除
- 检查您的collabora / CODE的日志。它们提供了WOPI调用中的错误信息;
- 使用分析器来调试wopi客户端在幕后对WOPI端点的调用。
文档
代码质量、测试、基准
每次向库引入更改时,Github都会运行测试。
库使用PHPUNIT编写了测试。
在每次提交之前,使用GrumPHP执行一些检查;运行composer grumphp
以手动检查。
使用Infection(PHP突变测试框架)测试测试的质量,运行composer infection
以尝试。
静态分析器也正在控制代码。已启用PHPStan和PSalm的最大级别。
贡献
请随时通过在Github上提交拉取请求来为此项目做出贡献。
更改日志
请参阅CHANGELOG.md以获取基于git提交的更改日志。
如需查看更详细的更新日志,请访问发布更新日志。