Skip to content

Commit

Permalink
Merge pull request #68 from CollaboraOnline/issue-56-refactor-CoolReq…
Browse files Browse the repository at this point in the history
…uest

Issue #56: Refactor CoolRequest
  • Loading branch information
donquixote authored Dec 3, 2024
2 parents bbfa056 + e663b8d commit b2f0a1e
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 235 deletions.
10 changes: 10 additions & 0 deletions collabora_online.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
_defaults:
autowire: true
logger.channel.collabora_online:
parent: logger.channel_base
arguments: ['cool']
Drupal\collabora_online\Cool\CollaboraDiscoveryFetcherInterface:
class: Drupal\collabora_online\Cool\CollaboraDiscoveryFetcher
Drupal\collabora_online\Cool\CollaboraDiscoveryInterface:
class: Drupal\collabora_online\Cool\CollaboraDiscovery
3 changes: 0 additions & 3 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,5 @@
<exclude name="Drupal.NamingConventions.ValidFunctionName.InvalidName"/>
<exclude name="Drupal.NamingConventions.ValidVariableName.LowerCamelName"/>
<exclude name="Drupal.NamingConventions.ValidFunctionName.ScopeNotCamelCaps"/>

<!-- Fix translatable strings later. -->
<exclude name="Drupal.Semantics.FunctionT.WhiteSpace"/>
</rule>
</ruleset>
75 changes: 45 additions & 30 deletions src/Controller/ViewerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,80 +10,95 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\collabora_online\Controller;

use Drupal\collabora_online\Cool\CollaboraDiscoveryInterface;
use Drupal\collabora_online\Cool\CoolUtils;
use Drupal\collabora_online\Exception\CollaboraNotAvailableException;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\RendererInterface;
use Drupal\media\Entity\Media;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Utility\Error;
use Drupal\media\MediaInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* Provides route responses for the Collabora module.
*/
class ViewerController extends ControllerBase {

/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
private $renderer;

/**
* The controller constructor.
*
* @param \Drupal\collabora_online\Cool\CollaboraDiscoveryInterface $discovery
* Service to fetch a WOPI client URL.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
*/
public function __construct(RendererInterface $renderer) {
$this->renderer = $renderer;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): self {
return new self(
$container->get('renderer'),
);
}
public function __construct(
protected readonly CollaboraDiscoveryInterface $discovery,
protected readonly RendererInterface $renderer,
) {}

/**
* Returns a raw page for the iframe embed.
*
* @param \Drupal\media\Entity\Media $media
* @param \Drupal\media\MediaInterface $media
* Media entity.
* @param \Symfony\Component\HttpFoundation\Request $request
* The incoming request.
* @param bool $edit
* TRUE to open Collabora Online in edit mode.
* FALSE to open Collabora Online in readonly mode.
*
* @return \Symfony\Component\HttpFoundation\Response
* Response suitable for iframe, without the usual page decorations.
*/
public function editor(Media $media, $edit = FALSE) {
public function editor(MediaInterface $media, Request $request, $edit = FALSE) {
$options = [
'closebutton' => 'true',
];

$render_array = CoolUtils::getViewerRender($media, $edit, $options);
try {
$wopi_client_url = $this->discovery->getWopiClientURL();
}
catch (CollaboraNotAvailableException $e) {
$this->getLogger('cool')->warning(
"Collabora Online is not available.<br>\n" . Error::DEFAULT_ERROR_MESSAGE,
Error::decodeException($e) + [],
);
return new Response(
(string) $this->t('The Collabora Online editor/viewer is not available.'),
Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain'],
);
}

if (!$render_array || array_key_exists('error', $render_array)) {
$error_msg = 'Viewer error: ' . ($render_array ? $render_array['error'] : 'NULL');
\Drupal::logger('cool')->error($error_msg);
$current_request_scheme = $request->getScheme();
if (!str_starts_with($wopi_client_url, $current_request_scheme . '://')) {
$this->getLogger('cool')->error($this->t(
"The current request uses '@current_request_scheme' url scheme, but the Collabora client url is '@wopi_client_url'.",
[
'@current_request_scheme' => $current_request_scheme,
'@wopi_client_url' => $wopi_client_url,
],
));
return new Response(
$error_msg,
(string) $this->t('Viewer error: Protocol mismatch.'),
Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']
['content-type' => 'text/plain'],
);
}

$render_array = CoolUtils::getViewerRender($media, $wopi_client_url, $edit, $options);

$render_array['#theme'] = 'collabora_online_full';
$render_array['#attached']['library'][] = 'collabora_online/cool.frame';

$response = new Response();
$response->setContent($this->renderer->renderRoot($render_array));
$response->setContent((string) $this->renderer->renderRoot($render_array));

return $response;
}
Expand Down
53 changes: 53 additions & 0 deletions src/Cool/CollaboraDiscovery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\collabora_online\Cool;

use Drupal\collabora_online\Exception\CollaboraNotAvailableException;

/**
* Service to get a WOPI client url for a given MIME type.
*/
class CollaboraDiscovery implements CollaboraDiscoveryInterface {

/**
* Constructor.
*
* @param \Drupal\collabora_online\Cool\CollaboraDiscoveryFetcherInterface $discoveryFetcher
* Service to load the discovery.xml from the Collabora server.
*/
public function __construct(
protected readonly CollaboraDiscoveryFetcherInterface $discoveryFetcher,
) {}

/**
* {@inheritdoc}
*/
public function getWopiClientURL(string $mimetype = 'text/plain'): string {
$xml = $this->discoveryFetcher->getDiscoveryXml();

$discovery_parsed = simplexml_load_string($xml);
if (!$discovery_parsed) {
throw new CollaboraNotAvailableException('The retrieved discovery.xml file is not a valid XML file.');
}

$result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype));
if (empty($result[0]['urlsrc'][0])) {
throw new CollaboraNotAvailableException('The requested mime type is not handled.');
}

return (string) $result[0]['urlsrc'][0];
}

}
122 changes: 122 additions & 0 deletions src/Cool/CollaboraDiscoveryFetcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\collabora_online\Cool;

use Drupal\collabora_online\Exception\CollaboraNotAvailableException;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\RequestOptions;
use Psr\Http\Client\ClientExceptionInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/**
* Service to load the discovery.xml from the Collabora server.
*/
class CollaboraDiscoveryFetcher implements CollaboraDiscoveryFetcherInterface {

/**
* Constructor.
*
* @param \Drupal\Core\Logger\LoggerChannelInterface $logger
* Logger channel.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* Config factory.
* @param \GuzzleHttp\ClientInterface $httpClient
* Http client.
*/
public function __construct(
#[Autowire(service: 'logger.channel.collabora_online')]
protected readonly LoggerChannelInterface $logger,
protected readonly ConfigFactoryInterface $configFactory,
protected readonly ClientInterface $httpClient,
) {}

/**
* {@inheritdoc}
*/
public function getDiscoveryXml(): string {
$discovery_url = $this->getWopiClientServerBaseUrl() . '/hosting/discovery';

$cool_settings = $this->loadSettings();
$disable_checks = !empty($cool_settings['disable_cert_check']);

try {
$response = $this->httpClient->get($discovery_url, [
RequestOptions::VERIFY => !$disable_checks,
]);
$xml = $response->getBody()->getContents();
}
catch (ClientExceptionInterface $e) {
// The backtrace of a client exception is typically not very
// interesting. Just log the message.
$this->logger->error("Failed to fetch from '@url': @message.", [
'@url' => $discovery_url,
'@message' => $e->getMessage(),
]);
throw new CollaboraNotAvailableException(
'Not able to retrieve the discovery.xml file from the Collabora Online server.',
previous: $e,
);
}
return $xml;
}

/**
* Loads the WOPI server url from configuration.
*
* @return string
* Base URL to access the WOPI server from Drupal.
*
* @throws \Drupal\collabora_online\Exception\CollaboraNotAvailableException
* The WOPI server url is misconfigured, or the protocol does not match
* that of the current Drupal request.
*/
protected function getWopiClientServerBaseUrl(): string {
$cool_settings = $this->loadSettings();
$wopi_client_server = $cool_settings['server'] ?? NULL;
if (!$wopi_client_server) {
throw new CollaboraNotAvailableException('The configured Collabora Online server address is empty.');
}
$wopi_client_server = trim($wopi_client_server);

if (!str_starts_with($wopi_client_server, 'http://') && !str_starts_with($wopi_client_server, 'https://')) {
throw new CollaboraNotAvailableException(sprintf(
"The configured Collabora Online server address must begin with 'http://' or 'https://'. Found '%s'.",
$wopi_client_server,
));
}

return $wopi_client_server;
}

/**
* Loads the relevant configuration.
*
* @return array
* Configuration.
*
* @throws \Drupal\collabora_online\Exception\CollaboraNotAvailableException
* The module is not configured.
*/
protected function loadSettings(): array {
$cool_settings = $this->configFactory->get('collabora_online.settings')->get('cool');
if (!$cool_settings) {
throw new CollaboraNotAvailableException('The Collabora Online connection is not configured.');
}
return $cool_settings;
}

}
33 changes: 33 additions & 0 deletions src/Cool/CollaboraDiscoveryFetcherInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\collabora_online\Cool;

/**
* Service to load the discovery.xml from the Collabora server.
*/
interface CollaboraDiscoveryFetcherInterface {

/**
* Gets the contents of discovery.xml from the Collabora server.
*
* @return string
* The full contents of discovery.xml.
*
* @throws \Drupal\collabora_online\Exception\CollaboraNotAvailableException
* The client url cannot be retrieved.
*/
public function getDiscoveryXml(): string;

}
37 changes: 37 additions & 0 deletions src/Cool/CollaboraDiscoveryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\collabora_online\Cool;

/**
* Service to get a WOPI client url for a given MIME type.
*/
interface CollaboraDiscoveryInterface {

/**
* Gets the URL for the WOPI client.
*
* @param string $mimetype
* Mime type for which to get the WOPI client url.
* This refers to config entries in the discovery.xml file.
*
* @return string
* The WOPI client url.
*
* @throws \Drupal\collabora_online\Exception\CollaboraNotAvailableException
* The client url cannot be retrieved.
*/
public function getWopiClientURL(string $mimetype = 'text/plain'): string;

}
Loading

0 comments on commit b2f0a1e

Please sign in to comment.