diff --git a/INSTALL.txt b/INSTALL.txt index 057be79..0e6eef9 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -55,16 +55,18 @@ If this is true then Stage File Proxy will not transfer the remote file to the local machine, it will just serve a 301 to the remote file and let the origin webserver handle it. -$config['stage_file_proxy.settings']['origin_dir'] = 'sites/default/files'; +$config['stage_file_proxy.settings']['origin_path'][*scheme*] = + 'sites/default/files'; // Drush variable set -drush config-set stage_file_proxy.settings origin_dir sites/default/files +drush config-set stage_file_proxy.settings origin_path.public sites/default/files -Default is 'sites/default/files'; +The *scheme* is the stream wrappers scheme, (ex: public, private). +Defaults are used public: 'sites/default/files'; private: 'system/files' If this is set then Stage File Proxy will use a different path for the remote files. This is useful for multisite installations where the sites directory contains different names for each url. If this is not set, it defaults to the -same path as the local site (sites/default/files). +same path as the local site. DRUSH USERS =========== diff --git a/README.md b/README.md index 1d56dc4..1e9b983 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,10 @@ $ drush en --yes stage_file_proxy 2. Configure connection to the source. This is available via the UI, at Configuration > Stage File Proxy Settings (admin/config/system/stage_file_proxy) +3. Set the preferred proxy operation mode in settings.php. The default is +EXPLICIT_STREAM that proxies only the core / known stream wrappers +(public, private). + As this module should only be used on non-production sites, it is preferable to configure this within your settings.php or settings.local.php file. Detailed descriptions of each setting, and syntax for defining the configuration in code diff --git a/config/install/stage_file_proxy.settings.yml b/config/install/stage_file_proxy.settings.yml index 843355d..c6cc6ec 100644 --- a/config/install/stage_file_proxy.settings.yml +++ b/config/install/stage_file_proxy.settings.yml @@ -1,5 +1,5 @@ hotlink: false origin: '' -origin_dir: false +origin_path: {} use_imagecache_root: true verify: true diff --git a/config/schema/stage_file_proxy.schema.yml b/config/schema/stage_file_proxy.schema.yml index 6ca997c..2dce779 100644 --- a/config/schema/stage_file_proxy.schema.yml +++ b/config/schema/stage_file_proxy.schema.yml @@ -8,9 +8,9 @@ stage_file_proxy.settings: origin: type: string label: "Origin" - origin_dir: - type: string - label: "Origin dir" + origin_path: + type: mapping + label: "Origin path" use_imagecache_root: type: boolean label: "Use imagecache root" diff --git a/src/EventSubscriber/ProxySubscriber.php b/src/EventSubscriber/ProxySubscriber.php index fb2d68d..9a29f47 100644 --- a/src/EventSubscriber/ProxySubscriber.php +++ b/src/EventSubscriber/ProxySubscriber.php @@ -30,9 +30,13 @@ class ProxySubscriber implements EventSubscriberInterface { */ protected $logger; + /** + * The fetch info service. + * + * @var \Drupal\stage_file_proxy\FetchInfoService + */ protected $fetchInfoService; - /** * Construct the FetchManager. * @@ -57,7 +61,7 @@ class ProxySubscriber implements EventSubscriberInterface { */ public function checkFileOrigin(GetResponseEvent $event) { $request_path = $event->getRequest()->getPathInfo(); - $fetch_info =$this->fetchInfoService->getOriginFetchInfo($request_path); + $fetch_info = $this->fetchInfoService->getOriginFetchInfo($request_path); if (!$fetch_info) { return; @@ -73,11 +77,12 @@ class ProxySubscriber implements EventSubscriberInterface { ]; $fetch_info->setQueryParams($query_parameters); - $fetch_info->setDomain($server); if ($config->get('hotlink')) { - $location = Url::fromUri($server . "/" . $fetch_info->getFetchPath(), [ + // Note that for hotlink we want to use the path to the originally + // requested file and not the one from which it was generated. + $location = Url::fromUri($server . "/" . $fetch_info->getOriginalFetch()->getFetchPath(), [ 'query' => $query_parameters, 'absolute' => TRUE, ])->toString(); @@ -94,7 +99,7 @@ class ProxySubscriber implements EventSubscriberInterface { } else { $this->logger->error('Stage File Proxy encountered an unknown error by retrieving file @file', [ - '@file' => $server . '/' . UrlHelper::encodePath($this->fetchInfoService->getOriginSchemePath($fetch_info->getScheme()) . '/' . $fetch_info->getFetchPath()) + '@file' => $fetch_info->getUrlPath(), ]); } diff --git a/src/FetchInfoService.php b/src/FetchInfoService.php index 58d99fd..ace3687 100644 --- a/src/FetchInfoService.php +++ b/src/FetchInfoService.php @@ -4,13 +4,18 @@ namespace Drupal\stage_file_proxy; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\File\FileSystemInterface; -use Drupal\Core\PathProcessor\PathProcessorManager; use Drupal\stage_file_proxy\EventDispatcher\AlterExcludedPathsEvent; +use Drupal\stage_file_proxy\Model\FileFetchInfo; use Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +/** + * The fetch info service. + * + * Combines all handles stream wrapper services and servers their + * data combined. + */ class FetchInfoService implements FetchInfoServiceInterface { /** @@ -21,7 +26,7 @@ class FetchInfoService implements FetchInfoServiceInterface { /** * Proxy schemes handled by stage file proxy. * - * @var SfpStreamInterface[] + * @var \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface[] */ protected $proxySchemes = []; @@ -35,59 +40,67 @@ class FetchInfoService implements FetchInfoServiceInterface { /** * The event dispatcher. * - * @var EventDispatcherInterface + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $eventDispatcher; /** * The current request. * - * @var Request + * @var \Symfony\Component\HttpFoundation\Request */ protected $currentRequest; /** * The file system service. * - * @var FileSystemInterface + * @var \Drupal\Core\File\FileSystemInterface */ protected $fileSystem; /** - * The path processor service. - * - * @var PathProcessorManager - */ - protected $pathProcessor; - - /** * FetchInfoService constructor. * - * @param ConfigFactoryInterface $configFactory - * @param EventDispatcherInterface $eventDispatcher - * @param RequestStack $requestStack - * @param FileSystemInterface $fileSystem - * @param PathProcessorManager $pathProcessorManager + * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory + * The config factory. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher + * The event dispatcher. + * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack + * The request stack. + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The filesystem. */ public function __construct(ConfigFactoryInterface $configFactory, EventDispatcherInterface $eventDispatcher, RequestStack $requestStack, - FileSystemInterface $fileSystem, - PathProcessorManager $pathProcessorManager) { + FileSystemInterface $fileSystem) { $this->config = $configFactory->get('stage_file_proxy.settings'); $this->eventDispatcher = $eventDispatcher; $this->currentRequest = $requestStack->getCurrentRequest(); $this->fileSystem = $fileSystem; - $this->pathProcessor = $pathProcessorManager; } + /** + * {@inheritdoc} + */ public function addProxyScheme(SfpStreamInterface $service) { + if (!($service instanceof SfpStreamInterface)) { + throw new \LogicException('Incorrect stream handler added to fetch info service.'); + } + $this->proxySchemes[$service::getScheme()] = $service; } /** * {@inheritdoc} */ + public function getProxySchemes() { + return $this->proxySchemes; + } + + /** + * {@inheritdoc} + */ public function handlesScheme($scheme) { return isset($this->proxySchemes[$scheme]); } @@ -104,7 +117,7 @@ class FetchInfoService implements FetchInfoServiceInterface { } } - return null; + return NULL; } /** @@ -128,7 +141,7 @@ class FetchInfoService implements FetchInfoServiceInterface { */ public function getOriginSchemePath($scheme) { if (!$this->handlesScheme($scheme)) { - return null; + return NULL; } $originPath = $this->config->get("origin_scheme_path.$scheme"); @@ -144,7 +157,7 @@ class FetchInfoService implements FetchInfoServiceInterface { */ public function getLocalSchemePath($scheme) { if (!$this->handlesScheme($scheme)) { - return null; + return NULL; } return $this->proxySchemes[$scheme]->getUrlBasePath(); @@ -186,7 +199,7 @@ class FetchInfoService implements FetchInfoServiceInterface { /** * {@inheritdoc} */ - public function shouldFetchFromHost($localHost = null) { + public function shouldFetchFromHost($localHost = NULL) { $localHost = $localHost ? $localHost : $this->currentRequest->getHost(); // Get the origin server. @@ -236,7 +249,13 @@ class FetchInfoService implements FetchInfoServiceInterface { continue; } - return $service->getFetchInfo($urlPath); + $fetchInfo = $service->getFetchInfo($urlPath); + + if ($fetchInfo instanceof FileFetchInfo) { + $fetchInfo->setDomain($this->config->get('origin')); + } + + return $fetchInfo; } return FALSE; diff --git a/src/FetchInfoServiceInterface.php b/src/FetchInfoServiceInterface.php index 9275036..aae0d50 100644 --- a/src/FetchInfoServiceInterface.php +++ b/src/FetchInfoServiceInterface.php @@ -2,12 +2,33 @@ namespace Drupal\stage_file_proxy; +use Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface; + +/** + * Fetch info service interface. + */ interface FetchInfoServiceInterface { /** + * Adds a stream wrapper handler to be handled by SFP. + * + * @param \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface $service + * The stream wrapper service. + */ + public function addProxyScheme(SfpStreamInterface $service); + + /** + * Gets the handled proxy schemes. + * + * @return \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface[] + * The scheme handlers. + */ + public function getProxySchemes(); + + /** * Whether given scheme is proxied. * - * @param $scheme + * @param string $scheme * The scheme. * * @return string|null @@ -18,7 +39,7 @@ interface FetchInfoServiceInterface { /** * Gets scheme base path if is handled. * - * @param $path + * @param string $path * The path. * * @return string|null @@ -40,7 +61,7 @@ interface FetchInfoServiceInterface { /** * Gets the origin scheme for path if is handled. * - * @param $path + * @param string $path * The path. * * @return string|null @@ -51,7 +72,7 @@ interface FetchInfoServiceInterface { /** * Gets the origin path for the scheme if handled. * - * @param $scheme + * @param string $scheme * The scheme. * * @return string|null @@ -90,7 +111,7 @@ interface FetchInfoServiceInterface { * @return bool * Whether fetch is allowed relative to host. */ - public function shouldFetchFromHost($localHost = null); + public function shouldFetchFromHost($localHost = NULL); /** * Determines if fetching should be done for path. @@ -107,7 +128,7 @@ interface FetchInfoServiceInterface { * Gets file fetch info if allowed for path. * * @param string $urlPath - * The url path to the file. + * The url path to the file or the URI. * * @return bool|\Drupal\stage_file_proxy\Model\FileFetchInfo * The fetch info or false if should not fetch. diff --git a/src/FetchManager.php b/src/FetchManager.php index b5089a5..ef26e16 100644 --- a/src/FetchManager.php +++ b/src/FetchManager.php @@ -3,7 +3,7 @@ namespace Drupal\stage_file_proxy; use Drupal\Core\File\FileSystemInterface; -use Drupal\stage_file_proxy\Model\FileFetchInfo; +use Drupal\stage_file_proxy\Model\FileFetchInfoInterface; use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; @@ -26,6 +26,11 @@ class FetchManager implements FetchManagerInterface { */ protected $fileSystem; + /** + * The fetch info. + * + * @var \Drupal\stage_file_proxy\FetchInfoServiceInterface + */ protected $fetchInfoService; /** @@ -42,7 +47,7 @@ class FetchManager implements FetchManagerInterface { /** * {@inheritdoc} */ - public function fetch(FileFetchInfo $fetchInfo, array $options = []) { + public function fetch(FileFetchInfoInterface $fetchInfo, array $options = []) { try { $url = $fetchInfo->getUrlPath(); $response = $this->getResponse($fetchInfo, $options); @@ -58,13 +63,13 @@ class FetchManager implements FetchManagerInterface { // Prepare local target directory and save downloaded file. $file_dir = $this->fetchInfoService->getLocalSchemePath($fetchInfo->getScheme()); - $destination = $file_dir . '/' . dirname($fetchInfo->getFileUri()); + $destination = $file_dir . '/' . dirname($fetchInfo->getFilePath()); if (!file_prepare_directory($destination, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { \Drupal::logger('stage_file_proxy')->error('Unable to prepare local directory @path.', ['@path' => $destination]); return FALSE; } - $destination = str_replace('///', '//', "$destination/") . $this->fileSystem->basename($fetchInfo->getFileUri()); + $destination = str_replace('///', '//', "$destination/") . $this->fileSystem->basename($fetchInfo->getUri()); $response_headers = $response->getHeaders(); $response_data = $response->getBody()->getContents(); @@ -88,7 +93,18 @@ class FetchManager implements FetchManagerInterface { return FALSE; } - protected function getResponse(FileFetchInfo $fetchInfo, array $options) { + /** + * Gets response for fetch information. + * + * @param \Drupal\stage_file_proxy\Model\FileFetchInfoInterface $fetchInfo + * The fetch info. + * @param array $options + * The fetch options. + * + * @return \Psr\Http\Message\ResponseInterface + * The response. + */ + protected function getResponse(FileFetchInfoInterface $fetchInfo, array $options) { // Create request for server. $url = $fetchInfo->getUrlPath(); $options['Connection'] = 'close'; @@ -96,7 +112,6 @@ class FetchManager implements FetchManagerInterface { return $this->client->get($url, $options); } - /** * Use write & rename instead of write. * diff --git a/src/FetchManagerInterface.php b/src/FetchManagerInterface.php index 6baff86..1a1b716 100644 --- a/src/FetchManagerInterface.php +++ b/src/FetchManagerInterface.php @@ -3,7 +3,7 @@ namespace Drupal\stage_file_proxy; use Drupal\Core\File\FileSystemInterface; -use Drupal\stage_file_proxy\Model\FileFetchInfo; +use Drupal\stage_file_proxy\Model\FileFetchInfoInterface; use GuzzleHttp\Client; /** @@ -18,6 +18,8 @@ interface FetchManagerInterface { * The HTTP client. * @param \Drupal\Core\File\FileSystemInterface $file_system * The file system. + * @param \Drupal\stage_file_proxy\FetchInfoServiceInterface $fetchInfoService + * The fetch info service. */ public function __construct(Client $client, FileSystemInterface $file_system, @@ -26,7 +28,7 @@ interface FetchManagerInterface { /** * Downloads a remote file and saves it to the local files directory. * - * @param \Drupal\stage_file_proxy\Model\FileFetchInfo $fetchInfo + * @param \Drupal\stage_file_proxy\Model\FileFetchInfoInterface $fetchInfo * The file fetch info object. * @param array $options * Options for the request. @@ -34,6 +36,6 @@ interface FetchManagerInterface { * @return bool * Returns true if the content was downloaded, otherwise false. */ - public function fetch(FileFetchInfo $fetchInfo, array $options); + public function fetch(FileFetchInfoInterface $fetchInfo, array $options); } diff --git a/src/Form/SettingsForm.php b/src/Form/SettingsForm.php index 027969e..f62adfe 100644 --- a/src/Form/SettingsForm.php +++ b/src/Form/SettingsForm.php @@ -2,12 +2,15 @@ namespace Drupal\stage_file_proxy\Form; +use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Component\Utility\Unicode; use Drupal\Core\Site\Settings; use Drupal\stage_file_proxy\FetchInfoService; +use Drupal\stage_file_proxy\FetchInfoServiceInterface; use Drupal\stage_file_proxy\SfpProxyMode; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provide settings for Stage File Proxy. @@ -15,6 +18,13 @@ use Drupal\stage_file_proxy\SfpProxyMode; class SettingsForm extends ConfigFormBase { /** + * The fetch info service. + * + * @var \Drupal\stage_file_proxy\FetchInfoServiceInterface + */ + protected $fetchInfo; + + /** * {@inheritdoc} */ public function getFormId() { @@ -31,6 +41,29 @@ class SettingsForm extends ConfigFormBase { } /** + * SettingsForm constructor. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + * @param \Drupal\stage_file_proxy\FetchInfoServiceInterface $infoService + * The fetch info service. + */ + public function __construct(ConfigFactoryInterface $config_factory, FetchInfoServiceInterface $infoService) { + parent::__construct($config_factory); + $this->fetchInfo = $infoService; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('stage_file_proxy.fetch_info_service') + ); + } + + /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state, $field_type = NULL) { @@ -52,21 +85,6 @@ class SettingsForm extends ConfigFormBase { '#required' => FALSE, ]; - $stage_file_proxy_origin_dir = $config->get('origin_dir'); - if (!$stage_file_proxy_origin_dir) { - $stage_file_proxy_origin_dir = $config->get('file_public_path'); - if (empty($stage_file_proxy_origin_dir)) { - $stage_file_proxy_origin_dir = \Drupal::service('site.path') . '/files'; - } - } - $form['origin_dir'] = [ - '#type' => 'textfield', - '#title' => $this->t('The origin directory.'), - '#default_value' => $stage_file_proxy_origin_dir, - '#description' => $this->t('If this is set then Stage File Proxy will use a different path for the remote files. This is useful for multisite installations where the sites directory contains different names for each url. If this is not set, it defaults to the same path as the local site.'), - '#required' => FALSE, - ]; - $form['use_imagecache_root'] = [ '#type' => 'checkbox', '#title' => $this->t('Imagecache Root.'), @@ -79,28 +97,51 @@ class SettingsForm extends ConfigFormBase { '#type' => 'checkbox', '#title' => $this->t('Hotlink.'), '#default_value' => $config->get('hotlink'), - '#description' => $this->t("If this is true then Stage File Proxy will not transfer the remote file to the local machine, it will just serve a 301 to the remote file and let the origin webserver handle it."), + '#description' => $this->t("If this is true then Stage File Proxy will not transfer the remote file to the local machine, it will just serve a 301 to the remote file and let the origin webserver handle it. This feature does not work with streams, in that case the files will be fetched."), '#required' => FALSE, ]; + $form['origin_path'] = [ + '#title' => $this->t('Origin base paths'), + '#type' => 'details', + '#description' => $this->t('Base access paths for schemes can be different on origin. If these are set then Stage File Proxy will use a different path for the remote files. This is useful for multisite installations where the sites directory contains different names for each url. If this is not set, it defaults to the same path as the local site.'), + '#open' => TRUE, + '#tree' => TRUE, + ]; + + foreach ($this->fetchInfo->getProxySchemes() as $scheme => $handler) { + $form['origin_path'][$scheme] = [ + '#type' => 'textfield', + '#title' => $this->t('%scheme scheme path.', ['%scheme' => ucfirst($scheme)]), + '#default_value' => $handler->getUrlOriginBasePath(), + '#required' => FALSE, + ]; + } + $settings = Settings::get('stage_file_proxy', []); - $form['core_streams'] = [ + $form['proxy_mode'] = [ + '#title' => $this->t('Proxy operation mode'), + '#type' => 'details', + '#description' => $this->t('To set proxy operation mode got to settings.php and set ["stage_file_proxy"]["proxy_mode"].'), + ]; + + $form['proxy_mode']['core_streams'] = [ '#type' => 'checkbox', '#title' => $this->t('Use core stream wrappers'), '#default_value' => isset($settings['proxy_mode']) ? - in_array($settings['proxy_mode'], [SfpProxyMode::CORE_STREAM, SfpProxyMode::BRUTE_STREAM]) : - FALSE, + in_array($settings['proxy_mode'], [SfpProxyMode::EXPLICIT_STREAM, SfpProxyMode::BRUTE_STREAM]) : + FALSE, '#description' => $this->t('Enables the usage of stream wrappers. Extends functionality of the Stage File Proxy to the file system level. This provides support for core stream wrappers (public, private).'), '#disabled' => TRUE, ]; - $form['brute_streams'] = [ + $form['proxy_mode']['brute_streams'] = [ '#type' => 'checkbox', '#title' => $this->t('Use all stream wrappers'), '#default_value' => isset($settings['proxy_mode']) ? - $settings['proxy_mode'] == SfpProxyMode::BRUTE_STREAM : - FALSE, + $settings['proxy_mode'] == SfpProxyMode::BRUTE_STREAM : + FALSE, '#description' => $this->t('Extends functionality of the above mode to use all registered local stream wrappers. Note that this option might be incompatible with certain systems as it uses eval.'), '#disabled' => TRUE, ]; @@ -133,7 +174,7 @@ class SettingsForm extends ConfigFormBase { $keys = [ 'origin', - 'origin_dir', + 'origin_path', 'use_imagecache_root', 'hotlink', 'verify', diff --git a/src/Model/FileFetchInfo.php b/src/Model/FileFetchInfo.php index f487fbb..d0c061b 100644 --- a/src/Model/FileFetchInfo.php +++ b/src/Model/FileFetchInfo.php @@ -4,142 +4,208 @@ namespace Drupal\stage_file_proxy\Model; use Drupal\Component\Utility\UrlHelper; -class FileFetchInfo { - - const ACCESS_PUBLIC = "public"; - const ACCESS_PRIVATE = "private"; +/** + * Model representing information for fetching files. + */ +class FileFetchInfo implements FileFetchInfoInterface { + /** + * The fetch path. + * + * @var string + */ protected $fetchPath; - protected $scheme; - protected $fileUri; - protected $queryParams; - protected $type; + + /** + * The file URI. + * + * @var string + */ + protected $uri; + + /** + * The fetch query params. + * + * @var array + */ + protected $queryParams = []; + + /** + * The access type. + * + * @var string + */ + protected $accessType; + + /** + * The domain. + * + * @var string + */ protected $domain; + + /** + * The port. + * + * @var int + */ protected $port = 80; - public function __construct($fetchPath, $fileUri, $scheme, $type = FileFetchInfo::ACCESS_PUBLIC) { + /** + * Original fetch info. + * + * @var \Drupal\stage_file_proxy\Model\FileFetchInfoInterface + */ + protected $originalFetch; + + /** + * FileFetchInfo constructor. + * + * @param string $fetchPath + * The fetch URL path. + * @param string $fileUri + * The file URI. + * @param string $accessType + * The file access type. + */ + public function __construct($fetchPath, $fileUri, $accessType = FileFetchInfoInterface::ACCESS_PUBLIC) { $this->fetchPath = $fetchPath; - $this->scheme = $scheme; - $this->fileUri = $fileUri; - $this->type = $type; + $this->uri = $fileUri; + $this->accessType = $accessType; $this->queryParams = []; } /** - * @return mixed + * {@inheritdoc} */ - public function getFetchPath() - { + public function getFetchPath() { return $this->fetchPath; } /** - * @return mixed + * {@inheritdoc} */ - public function getScheme() - { - return $this->scheme; + public function getScheme() { + list($scheme) = explode('://', $this->getUri(), 2); + return $scheme; } /** - * @return mixed + * {@inheritdoc} */ - public function getFileUri() - { - return $this->fileUri; + public function getUri() { + return $this->uri; } /** - * @param mixed $fetchPath + * {@inheritdoc} */ - public function setFetchPath($fetchPath): void - { + public function setFetchPath($fetchPath): void { $this->fetchPath = $fetchPath; } /** - * @param mixed $scheme - */ - public function setScheme($scheme): void - { - $this->scheme = $scheme; - } - - /** - * @param mixed $fileUri + * {@inheritdoc} */ - public function setFileUri($fileUri): void - { - $this->fileUri = $fileUri; + public function setUri($fileUri): void { + $this->uri = $fileUri; } /** - * @return mixed + * {@inheritdoc} */ - public function getQueryParams() - { + public function getQueryParams() { return $this->queryParams; } /** - * @param mixed $queryParams + * {@inheritdoc} */ - public function setQueryParams($queryParams): void - { + public function setQueryParams(array $queryParams): void { $this->queryParams = $queryParams; + if ($this->originalFetch instanceof FileFetchInfoInterface && !$this->originalFetch->getQueryParams()) { + $this->originalFetch->setQueryParams($queryParams); + } } /** - * @return string + * {@inheritdoc} */ - public function getType(): string - { - return $this->type; + public function getAccessType(): string { + return $this->accessType; } /** - * @param string $type + * {@inheritdoc} */ - public function setType(string $type): void - { - $this->type = $type; + public function setAccessType(string $accessType): void { + $this->accessType = $accessType; } /** - * @return mixed + * {@inheritdoc} */ - public function getDomain() - { + public function getDomain() { return $this->domain; } /** - * @param mixed $domain + * {@inheritdoc} */ - public function setDomain($domain): void - { + public function setDomain($domain): void { $this->domain = $domain; + if ($this->originalFetch instanceof FileFetchInfoInterface && !$this->originalFetch->getDomain()) { + $this->originalFetch->setDomain($domain); + } } /** - * @return int + * {@inheritdoc} */ - public function getPort(): int - { + public function getPort(): int { return $this->port; } /** - * @param int $port + * {@inheritdoc} */ - public function setPort(int $port): void - { + public function setPort(int $port): void { $this->port = $port; + if ($this->originalFetch instanceof FileFetchInfoInterface && $this->originalFetch->getPort()) { + $this->originalFetch->setPort($port); + } + } + + /** + * {@inheritdoc} + */ + public function getOriginalFetch() { + return $this->originalFetch instanceof FileFetchInfoInterface ? + $this->originalFetch->getOriginalFetch() : + $this; } + /** + * {@inheritdoc} + */ + public function setOriginalFetch(FileFetchInfoInterface $originalFetch): void { + $this->originalFetch = $originalFetch; + } + + /** + * {@inheritdoc} + */ + public function getFilePath(): string { + list(, $filePath) = explode('://', $this->getUri(), 2); + return $filePath; + } + + /** + * {@inheritdoc} + */ public function getUrlPath(): string { return $this->getDomain() . ':' . $this->getPort() . '/' . - l(UrlHelper::encodePath($this->getFetchPath()), '/') + ltrim(UrlHelper::encodePath($this->getFetchPath()), '/') . '?' . UrlHelper::buildQuery($this->getQueryParams()); } diff --git a/src/SfpProxyMode.php b/src/SfpProxyMode.php index 5d3b91a..5850d9c 100644 --- a/src/SfpProxyMode.php +++ b/src/SfpProxyMode.php @@ -20,7 +20,7 @@ class SfpProxyMode { * mode non existent files that are attempted to read from disk * will be loaded by stage file proxy. */ - const CORE_STREAM = 1; + const EXPLICIT_STREAM = 1; /** * Forces replacement of all supported and relevant stream wrappers. diff --git a/src/StageFileProxyServiceProvider.php b/src/StageFileProxyServiceProvider.php index 3da37b7..fa0c768 100644 --- a/src/StageFileProxyServiceProvider.php +++ b/src/StageFileProxyServiceProvider.php @@ -12,6 +12,9 @@ use Drupal\Core\Site\Settings; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Definition; +/** + * Stage file proxy service provider. + */ class StageFileProxyServiceProvider extends ServiceProviderBase implements CompilerPassInterface { /** @@ -28,7 +31,7 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi // We run our registers just before the stream wrapper pass. $index = 0; $optPasses = $container->getCompilerPassConfig()->getBeforeOptimizationPasses(); - while($index < count($optPasses) && !($optPasses[$index] instanceof RegisterStreamWrappersPass)) { + while ($index < count($optPasses) && !($optPasses[$index] instanceof RegisterStreamWrappersPass)) { ++$index; } @@ -42,13 +45,13 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi public function process(SymfonyContainerBuilder $container) { // Initialize proxy operation mode. $sfpSettings = Settings::get('stage_file_proxy'); - $proxyMode = isset($sfpSettings['proxy_mode']) ? $sfpSettings['proxy_mode'] : SfpProxyMode::CORE_STREAM; + $proxyMode = isset($sfpSettings['proxy_mode']) ? $sfpSettings['proxy_mode'] : SfpProxyMode::EXPLICIT_STREAM; if ($proxyMode == SfpProxyMode::BRUTE_STREAM) { $processed = $this->processSfpStreams($container); $this->processRemainingStreams($container, $processed); } - elseif ($proxyMode == SfpProxyMode::CORE_STREAM) { + elseif ($proxyMode == SfpProxyMode::EXPLICIT_STREAM) { $this->processSfpStreams($container); } else { @@ -59,7 +62,7 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi /** * Replaces defined stage file proxy stream service classes. * - * @param SymfonyContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container builder. * * @return array @@ -76,7 +79,13 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi $streamDefinition = $container->getDefinition($streamWrapperService); $streamDefinition->setClass($sfpStreamDefinition->getClass()); - $processed[$streamDefinition->getTag('stream_wrapper')['scheme']] = $serviceId; + $processed[$streamDefinition->getTag('stream_wrapper')[0]['scheme']] = $serviceId; + } + else { + // If the original stream wrapper service does not exist we remove the + // stage file proxy wrapper service so that it's not added by the + // service collector to the fetch info service. + $container->removeDefinition($serviceId); } } @@ -86,33 +95,35 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi /** * Replaces streams that have not been defined stage file proxy. * - * @param SymfonyContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container builder. + * @param array $processed + * The processed stream services. */ protected function processRemainingStreams(SymfonyContainerBuilder $container, array $processed) { $interfaceClass = SfpStreamInterface::class; $namespace = '\\Drupal\\stage_file_proxy\\StreamWrapper'; foreach ($this->getNonProcessedStreams($container, $processed) as $serviceId) { - /** @var Definition $service */ + /** @var \Symfony\Component\DependencyInjection\Definition $service */ $service = $container->getDefinition($serviceId); $serviceClass = $service->getClass(); - $serviceClassName = substr($serviceClass, strrpos($serviceClass, '\\')+1); + $serviceClassName = substr($serviceClass, strrpos($serviceClass, '\\') + 1); $newClassName = "Sfp${serviceClassName}Evaluated"; + $scheme = $service->getTag('stream_wrapper')['scheme']; eval("namespace $namespace { class extends $serviceClass implements $interfaceClass { use trait SfpStreamTrait; + public static function getScheme() {return '$scheme';} } }"); - $scheme = $service->getTag('stream_wrapper')['scheme']; - // Replaces the class of the original stream wrapper. $service->setClass("$namespace\\$newClassName"); - // Add the stage file proxy stream definition so that the service collector can - // add it to the fetch info service. + // Add the stage file proxy stream definition so that the service + // collector can add it to the fetch info service. $sfpStreamDefinition = new Definition("$namespace\\$newClassName"); $sfpStreamDefinition->addTag('stage_file_proxy_stream', ['wraps' => $scheme]); $container->setDefinition("stage_file_proxy.stream.$scheme", $sfpStreamDefinition); @@ -122,8 +133,11 @@ class StageFileProxyServiceProvider extends ServiceProviderBase implements Compi /** * Gets modules provided stream wrapper services. * - * @param SymfonyContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container * The container builder. + * @param array $processed + * The processed services. + * * @return array * Array keyed with scheme containing an array with service and base * path getter. diff --git a/src/StreamWrapper/SfpPrivateStream.php b/src/StreamWrapper/SfpPrivateStream.php index 9765e4e..9454548 100644 --- a/src/StreamWrapper/SfpPrivateStream.php +++ b/src/StreamWrapper/SfpPrivateStream.php @@ -2,10 +2,15 @@ namespace Drupal\stage_file_proxy\StreamWrapper; +use Drupal\Core\PathProcessor\PathProcessorManager; use Drupal\Core\StreamWrapper\PrivateStream; +use Drupal\stage_file_proxy\FetchInfoServiceInterface; use Drupal\stage_file_proxy\Model\FileFetchInfo; use Symfony\Component\HttpFoundation\Request; +/** + * Private stream handler. + */ class SfpPrivateStream extends PrivateStream implements SfpStreamInterface { use SfpStreamTrait { @@ -13,23 +18,63 @@ class SfpPrivateStream extends PrivateStream implements SfpStreamInterface { } /** + * The patch processor. + * + * @var \Drupal\Core\PathProcessor\PathProcessorManager + */ + protected $pathProcessor; + + /** + * The fetch info service. + * + * @var \Drupal\stage_file_proxy\FetchInfoServiceInterface + */ + protected $fetchInfo; + + /** + * SfpPrivateStream constructor. + * + * @param \Drupal\Core\PathProcessor\PathProcessorManager|null $pathProcessorManager + * The patch processor. + * @param \Drupal\stage_file_proxy\FetchInfoServiceInterface|null $fetchInfoService + * The fetch info service. + */ + public function __construct( + PathProcessorManager $pathProcessorManager = NULL, + FetchInfoServiceInterface $fetchInfoService = NULL + ) { + $this->pathProcessor = $pathProcessorManager; + $this->fetchInfo = $fetchInfoService; + } + + /** + * {@inheritdoc} + */ + public static function getAccessType() { + return FileFetchInfo::ACCESS_PRIVATE; + } + + /** * {@inheritdoc} */ public static function getScheme() { return 'private'; } + /** + * {@inheritdoc} + */ public function getUrlBasePath() { return 'system/files/'; } - public static function getAccessType() { - return FileFetchInfo::ACCESS_PRIVATE; - } - + /** + * {@inheritdoc} + */ public function getFetchInfo($urlPath) { + $originalFetch = $this->traitGetFetchInfo($urlPath); if (strpos($urlPath, '/system/files/styles/') !== 0) { - return $this->traitGetFetchInfo($urlPath); + return $originalFetch; } $dummyRequest = new Request(); @@ -40,7 +85,7 @@ class SfpPrivateStream extends PrivateStream implements SfpStreamInterface { // @see FileDownloadController::download. if ($dummyRequest->query->has('file')) { $filePath = $dummyRequest->query->get('file'); - $uri = static::$scheme . "://$filePath"; + $uri = static::getScheme() . "://$filePath"; } else { return FALSE; @@ -50,10 +95,13 @@ class SfpPrivateStream extends PrivateStream implements SfpStreamInterface { return FALSE; } - return new FileFetchInfo( + $fileFetch = new FileFetchInfo( rtrim($this->getUrlBasePath()) . '/' . ltrim($filePath), - $uri, static::$scheme, static::getAccessType() + $uri, static::getAccessType() ); + $fileFetch->setOriginalFetch($originalFetch); + + return $fileFetch; } } diff --git a/src/StreamWrapper/SfpPublicStream.php b/src/StreamWrapper/SfpPublicStream.php index 0142b9d..06926cf 100644 --- a/src/StreamWrapper/SfpPublicStream.php +++ b/src/StreamWrapper/SfpPublicStream.php @@ -3,8 +3,12 @@ namespace Drupal\stage_file_proxy\StreamWrapper; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\stage_file_proxy\FetchInfoServiceInterface; use Drupal\stage_file_proxy\Model\FileFetchInfo; +/** + * Public stream handler. + */ class SfpPublicStream extends PublicStream implements SfpStreamInterface { use SfpStreamTrait { @@ -12,48 +16,60 @@ class SfpPublicStream extends PublicStream implements SfpStreamInterface { } /** - * {@inheritdoc} + * The fetch info service. + * + * @var \Drupal\stage_file_proxy\FetchInfoServiceInterface */ - public static function getScheme() { - return 'public'; + protected $fetchInfo; + + /** + * SfpPublicStream constructor. + * + * @param \Drupal\stage_file_proxy\FetchInfoServiceInterface|null $fetchInfoService + * The fetch info service. + */ + public function __construct( + FetchInfoServiceInterface $fetchInfoService = NULL + ) { + $this->fetchInfo = $fetchInfoService; } /** * {@inheritdoc} */ - public function getOriginPath($localPath) { - $originBasePath = $this->config->get('origin_path'); - if (!$originBasePath) { - return $localPath; - } - - $relativePath = $this->getRelativePath($localPath); - return rtrim($originBasePath) . '/' . $relativePath; + public static function getScheme() { + return 'public'; } /** * {@inheritdoc} */ public function getFetchInfo($urlPath) { + $urlPath = ltrim($urlPath, '/'); $basePath = $this->getUrlBasePath(); - // Handle the case of public styles files. + $originalFetch = $this->traitGetFetchInfo($urlPath); + if (strpos($urlPath, $basePath . '/styles') !== 0) { - return $this->traitGetFetchInfo($urlPath); + return $originalFetch; } - $urlPath = substr($urlPath, strlen($basePath)); + // Handle the case of public styles files. + $urlPath = substr($urlPath, strlen($basePath) + 1); $originalFileUri = preg_replace('/styles\/.*\/(.*)\/(.*)/U', '$1://$2', $urlPath); // The image styles can handle the generation. - if (file_exists($originalFileUri) || !$this->config->get('use_imagecache_root')) { + if (file_exists($originalFileUri) || !$this->fetchInfo->config()->get('use_imagecache_root')) { return FALSE; } - return new FileFetchInfo( - $this->getOriginPath($urlPath), - $originalFileUri, static::$scheme + $fileFetch = new FileFetchInfo( + $this->getUrlPathFromUri($originalFileUri, TRUE), + $originalFileUri ); + $fileFetch->setOriginalFetch($originalFetch); + + return $fileFetch; } } diff --git a/src/StreamWrapper/SfpStreamInterface.php b/src/StreamWrapper/SfpStreamInterface.php index 6ab4ec0..10aaa9e 100644 --- a/src/StreamWrapper/SfpStreamInterface.php +++ b/src/StreamWrapper/SfpStreamInterface.php @@ -2,13 +2,38 @@ namespace Drupal\stage_file_proxy\StreamWrapper; +/** + * Stage file proxy stream and stream service interface. + */ interface SfpStreamInterface { + /** + * Gets the file system base path for this stream. + * + * @return string + * The path. + */ public function getFileSystemBasePath(); + /** + * Gets the URL accessible base path. + * + * @return string + * The URL path. + */ public function getUrlBasePath(); /** + * Gets the origin URL base path. + * + * By default should return the local URL base path. + * + * @return string + * The URL path. + */ + public function getUrlOriginBasePath(); + + /** * Determines if this stream handles the given path. * * @param string $urlPath. @@ -19,18 +44,82 @@ interface SfpStreamInterface { */ public function handlesPath($urlPath); + /** + * Gets the relative path part from URL to file. + * + * Strips the base path from the full path. + * + * @param string $urlPath + * The URL path from which to get relative path. + * + * @return string + * The relative path. + */ public function getRelativePath($urlPath); - public function getUrlPathFromUri($urlPath); + /** + * Creates URL path to file from URI. + * + * @param string $uri + * The URI. + * @param bool $forOrigin + * Whether to generate for origin. + * + * @return string + * The path. + */ + public function getUrlPathFromUri($uri, $forOrigin = false); - public function getUriFromUrlPath($uri); + /** + * Creates URI for file from URL path. + * + * @param string $urlPath + * The URL path. + * + * @return string + * The URI. + */ + public function getUriFromUrlPath($urlPath); - public function getOriginPath($localPath); + /** + * Transforms local URL path to origin URL path. + * + * @param string $urlPath + * Local URL path. + * + * @return string + * Origin URL path. + */ + public function transformToOriginPath($urlPath); + /** + * Gets the stage file proxy fetch information. + * + * @param string $urlPath + * For what URL path to get. + * + * @return \Drupal\stage_file_proxy\Model\FileFetchInfoInterface + * The fetch info. + */ public function getFetchInfo($urlPath); + /** + * Gets the access type of this stream. + * + * @return string + * The access type. + * + * @see \Drupal\stage_file_proxy\Model\FileFetchInfoInterface + * For the types of access. + */ public static function getAccessType(); + /** + * Gets the scheme of this stream wrapper. + * + * @return string + * The scheme. + */ public static function getScheme(); } diff --git a/src/StreamWrapper/SfpStreamTrait.php b/src/StreamWrapper/SfpStreamTrait.php index 932c271..eb19ac8 100644 --- a/src/StreamWrapper/SfpStreamTrait.php +++ b/src/StreamWrapper/SfpStreamTrait.php @@ -2,16 +2,16 @@ namespace Drupal\stage_file_proxy\StreamWrapper; -use Drupal\stage_file_proxy\FetchManagerInterface; -use Drupal\stage_file_proxy\FetchInfoService; use Drupal\stage_file_proxy\Model\FileFetchInfo; +/** + * Stage file proxy stream trait. + */ trait SfpStreamTrait { - public static $scheme = ''; - /** * {@inheritdoc} + * * @see StreamWrapper::stream_open() */ public function stream_open($uri, $mode, $options, &$opened_path) { @@ -38,32 +38,36 @@ trait SfpStreamTrait { * * @param string $uri * The URI of the file. + * * @return bool|null * Whether the download was successful. NULL if fetching was aborted. */ protected function fetchFromOrigin($uri) { - /** @var FetchInfoService $fetchService */ - $fetchService = \Drupal::service('stage_file_proxy.fetch_service'); + /** @var \Drupal\stage_file_proxy\FetchInfoService $fetchService */ + $fetchService = \Drupal::service('stage_file_proxy.fetch_info_service'); $config = $fetchService->config(); $fetch_info = $fetchService->getOriginFetchInfo($uri); if (!$fetch_info) { - return null; + return NULL; } - $origin = $config->get('origin'); - $options = ['verify' => $config->get('verify'),]; + $options = ['verify' => $config->get('verify')]; - /** @var FetchManagerInterface $fetchManager */ + /** @var \Drupal\stage_file_proxy\FetchManagerInterface $fetchManager */ $fetchManager = \Drupal::service('stage_file_proxy.fetch_manager'); - $success = $fetchManager->fetch($origin, $fetch_info, $options); + + // Note that we are sending the original fetch information. This is + // because when using streams directly we have no way of generating + // the requested file from the original one. + $success = $fetchManager->fetch($fetch_info->getOriginalFetch(), $options); if (!$success) { \Drupal::service('logger.channel.stage_file_proxy') ->error('Stage File Proxy encountered an unknown error by retrieving file with stream @scheme @file', [ '@file' => $fetch_info->getUrlPath(), - '@scheme' => $fetch_info->getScheme() + '@scheme' => $fetch_info->getScheme(), ]); } @@ -72,6 +76,7 @@ trait SfpStreamTrait { /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function getFileSystemBasePath() { @@ -80,6 +85,7 @@ trait SfpStreamTrait { /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function getUrlBasePath() { @@ -89,57 +95,89 @@ trait SfpStreamTrait { /** * {@inheritdoc} + * + * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface + */ + public function getUrlOriginBasePath() { + $originBasePath = \Drupal::service('stage_file_proxy.fetch_info_service') + ->config()->get('origin_path.' . static::getScheme()); + + if ($originBasePath) { + return ltrim($originBasePath); + } + + // By default assume same path. + return $this->getUrlBasePath(); + } + + /** + * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function handlesPath($urlPath) { $basePath = $this->getUrlBasePath(); - return strpos(ltrim($urlPath), $basePath) === 0; + return strpos(ltrim($urlPath, '/'), $basePath) === 0; } /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function getRelativePath($urlPath) { $basePath = $this->getUrlBasePath(); - $relativePath = substr(ltrim($urlPath), strlen($basePath)); + $relativePath = ltrim(substr(ltrim($urlPath, '/'), strlen($basePath)), '/'); return $relativePath; } /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function getUriFromUrlPath($urlPath) { - $urlPath = ltrim($urlPath, '/'); if (!$this->handlesPath($urlPath)) { return FALSE; } - return static::$scheme . '://' . $this->getRelativePath($urlPath); + return static::getScheme() . '://' . $this->getRelativePath($urlPath); } /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ - public function getUrlPathFromUri($uri) { - list ($scheme, $relativePath) = explode($uri, '://', 1); - if (!$scheme || static::$scheme !== $scheme) { + public function getUrlPathFromUri($uri, $forOrigin = FALSE) { + list ($scheme, $relativePath) = explode('://', $uri, 2); + if (!$scheme || static::getScheme() !== $scheme) { return FALSE; } - $basePath = $this->getUrlBasePath(); + $basePath = $forOrigin ? $this->getUrlOriginBasePath() : $this->getUrlBasePath(); return rtrim($basePath, '/') . '/' . $relativePath; } - public function getOriginPath($localPath) { - // By default assume same path. - return $localPath; + /** + * {@inheritdoc} + * + * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface + */ + public function transformToOriginPath($urlPath) { + $localBase = trim($this->getUrlBasePath(), '/'); + $originBase = trim($this->getUrlOriginBasePath(), '/'); + + if ($localBase == $originBase) { + return $urlPath; + } + + return $originBase . '/' . $this->getRelativePath($urlPath); } /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public function getFetchInfo($urlPath) { @@ -149,13 +187,14 @@ trait SfpStreamTrait { } return new FileFetchInfo( - $this->getOriginPath($urlPath), $uri, - static::$scheme, static::getAccessType() + $this->transformToOriginPath($urlPath), $uri, + static::getAccessType() ); } /** * {@inheritdoc} + * * @see \Drupal\stage_file_proxy\StreamWrapper\SfpStreamInterface */ public static function getAccessType() { @@ -163,4 +202,4 @@ trait SfpStreamTrait { return FileFetchInfo::ACCESS_PUBLIC; } -} \ No newline at end of file +} diff --git a/stage_file_proxy.drush.inc b/stage_file_proxy.drush.inc index 48809af..86c6c06 100644 --- a/stage_file_proxy.drush.inc +++ b/stage_file_proxy.drush.inc @@ -5,10 +5,9 @@ * Contains drush commands. */ -use Drupal\Component\Utility\Unicode; -use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Database\Database; use GuzzleHttp\Exception\ClientException; +use Drupal\stage_file_proxy\Model\FileFetchInfoInterface; /** * Implements hook_drush_command(). @@ -28,7 +27,7 @@ function drush_stage_file_proxy_dl() { $server = Drupal::config('stage_file_proxy.settings')->get('origin'); if (empty($server)) { drupal_set_message( - dt('Configure stage_file_proxy.settings.origin in your settings.php (see INSTALL.txt).'), + t('Configure stage_file_proxy.settings.origin in your settings.php (see INSTALL.txt).'), 'error' ); @@ -41,40 +40,36 @@ function drush_stage_file_proxy_dl() { $results = $query->execute()->fetchCol(); $fetch_manager = Drupal::service('stage_file_proxy.fetch_manager'); + /** @var \Drupal\stage_file_proxy\FetchInfoServiceInterface $fetch_info_service */ + $fetch_info_service = Drupal::service('stage_file_proxy.fetch_info_service'); $logger = Drupal::service('logger.channel.stage_file_proxy'); $translation = Drupal::translation(); - $file_dir = $fetch_manager->filePublicPath(); - $remote_file_dir = trim(\Drupal::config('stage_file_proxy.settings')->get('origin_dir')); - if (!$remote_file_dir) { - $remote_file_dir = $file_dir; - } - $got_files_number = 0; $error_files_number = 0; $not_public_files_number = 0; foreach ($results as $uri) { - if (strpos($uri, 'public://') !== 0) { - $not_public_files_number++; + // @TODO: Need a way to login user with this command for private files. + $fetchInfo = $fetch_info_service->getOriginFetchInfo($uri); + + if (!$fetchInfo) { continue; } - $relative_path = Unicode::substr($uri, Unicode::strlen('public://')); - - if (file_exists(DRUPAL_ROOT . '/' . $file_dir . '/' . $relative_path)) { - continue; + if ($fetchInfo->getAccessType() != FileFetchInfoInterface::ACCESS_PUBLIC) { + ++$not_public_files_number; } try { - if ($fetch_manager->fetch($server, $remote_file_dir, $relative_path)) { + if ($fetch_manager->fetch($fetchInfo)) { $got_files_number++; } else { $error_files_number++; $logger->error( 'Stage File Proxy encountered an unknown error by retrieving file @file', - ['@file' => $server . '/' . UrlHelper::encodePath($remote_file_dir . '/' . $relative_path)] + ['@file' => $fetchInfo->getUrlPath()] ); } } diff --git a/stage_file_proxy.install b/stage_file_proxy.install index ed7b9ff..6fc182c 100644 --- a/stage_file_proxy.install +++ b/stage_file_proxy.install @@ -11,3 +11,15 @@ function stage_file_proxy_update_8001() { \Drupal::configFactory()->getEditable('stage_file_proxy.settings')->set('verify', TRUE)->save(); } + +/** + * Migrate base path settings. + */ +function stage_file_proxy_update_8002() { + $config = \Drupal::configFactory()->getEditable('stage_file_proxy.settings'); + + $basePath = $config->get('origin_dir'); + if ($basePath) { + $config->set('origin_path.public', $basePath)->save(); + } +} diff --git a/stage_file_proxy.services.yml b/stage_file_proxy.services.yml index bc61187..633f52f 100644 --- a/stage_file_proxy.services.yml +++ b/stage_file_proxy.services.yml @@ -9,7 +9,6 @@ services: - '@event_dispatcher' - '@request_stack' - '@file_system' - - '@path_processor_manager' stage_file_proxy.fetch_manager: class: Drupal\stage_file_proxy\FetchManager @@ -36,8 +35,13 @@ services: class: Drupal\stage_file_proxy\StreamWrapper\SfpPublicStream tags: - { name: stage_file_proxy_stream, wraps: stream_wrapper.public } + arguments: + - '@stage_file_proxy.fetch_info_service' stage_file_proxy.stream.private: class: Drupal\stage_file_proxy\StreamWrapper\SfpPrivateStream tags: - { name: stage_file_proxy_stream, wraps: stream_wrapper.private } + arguments: + - '@path_processor_manager' + - '@stage_file_proxy.fetch_info_service'