From 06055dc344ac5b2d3ad5d8bb97f641b41c1cffbd Mon Sep 17 00:00:00 2001 From: Ahmed Nagi Date: Sun, 21 Jul 2024 11:06:35 +0300 Subject: [PATCH] wip --- config/wopi.php | 13 +++- src/Contracts/AbstractDocumentManager.php | 92 ++++++++++++++++++----- 2 files changed, 82 insertions(+), 23 deletions(-) diff --git a/config/wopi.php b/config/wopi.php index cd60d84..0715f3b 100644 --- a/config/wopi.php +++ b/config/wopi.php @@ -1,14 +1,14 @@ null, - /* + /* * Default UI langauge. */ 'ui_language' => 'en-US', @@ -29,7 +29,7 @@ * not have any validation logic. It's a great place to implement * custom validation for the access_token and access_token_ttl. */ - 'wopi_request' => Nagi\LaravelWopi\Http\Requests\WopiRequest::class, + 'wopi_request' => Nagi\LaravelWopi\Http\Requests\WopiRequest::class, /* * Here's you can define your middleware pipeline that every @@ -37,7 +37,7 @@ */ 'middleware' => [Nagi\LaravelWopi\Http\Middleware\ValidateProof::class], - /* + /* * Collabora or Microsoft Office 365 or any WOPI client url. */ 'client_url' => env('WOPI_CLIENT_URL', ''), @@ -111,4 +111,9 @@ */ 'support_user_info' => false, + /* + * @see https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/discovery#placeholder-values + */ + 'microsoft_365_url_placeholder_value_map' => [], + ]; diff --git a/src/Contracts/AbstractDocumentManager.php b/src/Contracts/AbstractDocumentManager.php index d5ebfe7..aab1b2d 100644 --- a/src/Contracts/AbstractDocumentManager.php +++ b/src/Contracts/AbstractDocumentManager.php @@ -88,7 +88,7 @@ abstract class AbstractDocumentManager /** * Preform look up for the file/document. * - * @param string $fileId unique ID, Represent a single file and URL safe. + * @param string $fileId unique ID, Represent a single file and URL safe. * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ @@ -271,46 +271,100 @@ public function generateUrl(string $lang = 'en-Us'): string public function getUrlForAction(string $action, string $lang = 'en-US'): string { - /** @var ConfigRepositoryInterface */ - $config = app(ConfigRepositoryInterface::class); - - $lang = empty($lang) ? $config->getDefaultUiLang() : $lang; - $extension = method_exists($this, 'extension') ? Str::replaceFirst('.', '', $this->extension()) : pathinfo($this->basename(), PATHINFO_EXTENSION); + $actionUrl = optional(Discovery::discoverAction($extension, $action)); + $url = route('wopi.checkFileInfo', [ 'file_id' => $this->id(), ]); - // todo handle microsoft office 365 <> placeholders - // example https://FFC-onenote.officeapps.live.com/hosting/WopiTestFrame.aspx? + /** @var ConfigRepositoryInterface */ + $config = app(ConfigRepositoryInterface::class); - $actionUrl = optional(Discovery::discoverAction($extension, $action)); + $lang = empty($lang) ? $config->getDefaultUiLang() : $lang; if (is_null($actionUrl['urlsrc'])) { throw new Exception("Unsupported action \"{$action}\" for \"{$extension}\" extension."); } + if (str($actionUrl['urlsrc'])->contains('officeapps.live.com')) { + return $this->processMicrosoftOffice365Url($actionUrl['urlsrc'], $url); + } + return "{$actionUrl['urlsrc']}lang={$lang}&WOPISrc={$url}"; } + protected function processMicrosoftOffice365Url(string $url, string $wopiSrc): string + { + /** @var ConfigRepositoryInterface */ + $config = app(ConfigRepositoryInterface::class); + + $lang = empty($lang) ? $config->getDefaultUiLang() : $lang; + + $url = str($url); + + // extract all placeholders or + // https://excel.officeapps.live.com/x/_layouts/xlviewerinternal.aspx? + + $reqiredReplaceMap = [ + 'UI_LLCC' => $lang, + 'DC_LLCC' => $lang, + 'WOPI_SOURCE' => $wopiSrc, + ]; + + // extract it form the url and remove the required from them + $otherReplaceMap = config('wopi.microsoft_365_url_placeholder_value_map', []); + + preg_match_all('/<([^>]*)>/', $url, $matches); + + collect($matches[1]) + // filter out nulls and falsy values + ->filter() + ->each(function (string $queryParamWithPlaceholder) use (&$url, &$reqiredReplaceMap, &$otherReplaceMap) { + + foreach ($reqiredReplaceMap as $placeholder => $value) { + if (str($queryParamWithPlaceholder)->contains($placeholder)) { + $url = str($url)->replace($placeholder, $value); + + return; + } + } + + foreach ($otherReplaceMap as $placeholder => $value) { + if (str($queryParamWithPlaceholder)->contains($placeholder)) { + $url = str($url)->replace($placeholder, $value); + + return; + } + } + + // remove the rest of if not found + $url = str($url)->replace('<'.$queryParamWithPlaceholder.'>', ''); + }); + + return $url->replace(['<', '>'], '') + ->replaceLast('&', '') + ->toString(); + } + /** * Get CheckfileInfo response proprites based * on implemented interfaces/features. */ public function getResponseProprties(): array { - return collect(static::$propertyMethodMapping) - ->flatMap(function (string $methodName, string $propertyName) { - if (method_exists($this, $methodName)) { - return [ - $propertyName => $this->$methodName(), - ]; - } - }) - ->filter(fn ($value) => $value !== null) - ->toArray(); + return collect(static::$propertyMethodMapping) + ->flatMap(function (string $methodName, string $propertyName) { + if (method_exists($this, $methodName)) { + return [ + $propertyName => $this->$methodName(), + ]; + } + }) + ->filter(fn ($value) => $value !== null) + ->toArray(); } }