From 65c7a7365dd0c36f126638b1549e0afb3086a789 Mon Sep 17 00:00:00 2001
From: Brasdrive
Date: Fri, 7 Nov 2025 02:24:16 -0400
Subject: [PATCH] 2.0.3
---
composer.json | 1 +
src/Admin/HealthCheck.php | 103 ++++---
src/Filesystem/Adapters/PrefixedAdapter.php | 240 +++++++++++++++
src/Filesystem/Adapters/S3Adapter.php | 17 +-
src/Helpers/PathHelper.php | 13 +
src/Media/ImageEditorGD.php | 13 +-
src/Media/ImageEditorImagick.php | 14 +-
src/Plugin.php | 299 ++++++-------------
src/Settings/SettingsPage.php | 100 +++++++
src/StreamWrapper/FlysystemStreamWrapper.php | 6 +-
10 files changed, 534 insertions(+), 272 deletions(-)
create mode 100644 src/Filesystem/Adapters/PrefixedAdapter.php
create mode 100644 src/Settings/SettingsPage.php
diff --git a/composer.json b/composer.json
index d45704c..7029339 100644
--- a/composer.json
+++ b/composer.json
@@ -10,6 +10,7 @@
"league/flysystem-azure-blob-storage": "^3.24",
"league/flysystem-google-cloud-storage": "^3.24",
"league/flysystem-webdav": "^3.24",
+ "league/flysystem-path-prefixing": "^3.24",
"aws/aws-sdk-php": "^3.330",
"google/cloud-storage": "^1.33",
"microsoft/azure-storage-blob": "^1.5",
diff --git a/src/Admin/HealthCheck.php b/src/Admin/HealthCheck.php
index a8eeb8f..a0503e6 100644
--- a/src/Admin/HealthCheck.php
+++ b/src/Admin/HealthCheck.php
@@ -4,61 +4,70 @@ declare(strict_types=1);
namespace FlysystemOffload\Admin;
-use FlysystemOffload\Helpers\PathHelper;
-use FlysystemOffload\Plugin;
-use League\Flysystem\PortableVisibility;
-use Throwable;
+use FlysystemOffload\Filesystem\FilesystemFactory;
+use League\Flysystem\FilesystemException;
+use League\Flysystem\Visibility;
+use WP_CLI;
+use WP_CLI_Command;
+use WP_Error;
-final class HealthCheck
+class HealthCheck extends WP_CLI_Command
{
- /**
- * @param array $args
- * @param array $assocArgs
- */
- public static function run(array $args, array $assocArgs): void
- {
- unset($args, $assocArgs);
+ private FilesystemFactory $factory;
+
+ public function __construct(FilesystemFactory $factory)
+ {
+ $this->factory = $factory;
+ }
+
+ /**
+ * Ejecuta un chequeo básico de conectividad e integración.
+ *
+ * ## EXAMPLES
+ *
+ * wp flysystem-offload health-check
+ */
+ public function __invoke(): void
+ {
+ $settings = get_option('flysystem_offload_settings', []);
+
+ if (! is_array($settings)) {
+ WP_CLI::error('No se encontraron ajustes del plugin.');
- if (! class_exists('\WP_CLI')) {
return;
}
- try {
- $plugin = Plugin::instance();
- $filesystem = $plugin->getFilesystem();
+ $result = $this->run($settings);
- if (! in_array('fly', stream_get_wrappers(), true)) {
- \WP_CLI::error('El stream wrapper "fly://" no está registrado.');
- }
+ if ($result instanceof WP_Error) {
+ WP_CLI::error($result->get_error_message());
- $report = [];
-
- $report[] = 'Adapter: ' . get_class($filesystem);
-
- $uploadDir = wp_get_upload_dir();
- $report[] = 'Base URL remoto: ' . ($uploadDir['baseurl'] ?? '(desconocido)');
- $report[] = 'Directorio remoto: ' . ($uploadDir['basedir'] ?? '(desconocido)');
-
- $testKey = trim(PathHelper::stripProtocol(($uploadDir['path'] ?? '') . '/flysystem-offload-health-check-' . uniqid('', true)), '/');
-
- $filesystem->write($testKey, 'ok', ['visibility' => PortableVisibility::PUBLIC]);
- $content = $filesystem->read($testKey);
-
- if ($content !== 'ok') {
- \WP_CLI::warning('El contenido leído no coincide con lo escrito.');
- } else {
- $report[] = 'Lectura/escritura remota verificada.';
- }
-
- $filesystem->delete($testKey);
-
- foreach ($report as $line) {
- \WP_CLI::line($line);
- }
-
- \WP_CLI::success('Health-check completado correctamente.');
- } catch (Throwable $exception) {
- \WP_CLI::error('Health-check falló: ' . $exception->getMessage());
+ return;
}
+
+ WP_CLI::success('Chequeo completado correctamente.');
+ }
+
+ /**
+ * @param array $settings
+ */
+ public function run(array $settings)
+ {
+ try {
+ $filesystem = $this->factory->build($settings);
+ } catch (FilesystemException|\Throwable $exception) {
+ return new WP_Error('flysystem_offload_health_check_failed', $exception->getMessage());
+ }
+
+ $testKey = sprintf('health-check/%s.txt', wp_generate_uuid4());
+
+ try {
+ $filesystem->write($testKey, 'ok', ['visibility' => Visibility::PUBLIC]);
+ $filesystem->delete($testKey);
+ } catch (\Throwable $exception) {
+ return new WP_Error('flysystem_offload_health_check_failed', $exception->getMessage());
+ }
+
+ return true;
}
}
diff --git a/src/Filesystem/Adapters/PrefixedAdapter.php b/src/Filesystem/Adapters/PrefixedAdapter.php
new file mode 100644
index 0000000..7c3eff6
--- /dev/null
+++ b/src/Filesystem/Adapters/PrefixedAdapter.php
@@ -0,0 +1,240 @@
+adapter = $adapter;
+ $this->prefix = new PathPrefixer($prefix);
+ }
+
+ public function read(string $location): string
+ {
+ try {
+ return $this->adapter->read($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToReadFile::fromLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function readStream(string $location)
+ {
+ try {
+ return $this->adapter->readStream($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToReadFile::fromLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function listContents(string $location, bool $deep): Generator
+ {
+ foreach ($this->adapter->listContents($this->prefix->prefixPath($location), $deep) as $attributes) {
+ yield $attributes->withPath($this->prefix->stripPrefix($attributes->path()));
+ }
+ }
+
+ public function fileExists(string $location): bool
+ {
+ try {
+ return $this->adapter->fileExists($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToCheckFileExistence::forLocation($location, $previous);
+ }
+ }
+
+ public function directoryExists(string $location): bool
+ {
+ try {
+ return $this->adapter->directoryExists($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToCheckDirectoryExistence::forLocation($location, $previous);
+ }
+ }
+
+ public function lastModified(string $path): FileAttributes
+ {
+ try {
+ return $this->adapter->lastModified($this->prefix->prefixPath($path));
+ } catch (Throwable $previous) {
+ throw UnableToRetrieveMetadata::lastModified($path, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function fileSize(string $path): FileAttributes
+ {
+ try {
+ return $this->adapter->fileSize($this->prefix->prefixPath($path));
+ } catch (Throwable $previous) {
+ throw UnableToRetrieveMetadata::fileSize($path, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function mimeType(string $path): FileAttributes
+ {
+ try {
+ return $this->adapter->mimeType($this->prefix->prefixPath($path));
+ } catch (Throwable $previous) {
+ throw UnableToRetrieveMetadata::mimeType($path, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function visibility(string $path): FileAttributes
+ {
+ try {
+ return $this->adapter->visibility($this->prefix->prefixPath($path));
+ } catch (Throwable $previous) {
+ throw UnableToRetrieveMetadata::visibility($path, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function write(string $location, string $contents, Config $config): void
+ {
+ try {
+ $this->adapter->write($this->prefix->prefixPath($location), $contents, $config);
+ } catch (Throwable $previous) {
+ throw UnableToWriteFile::atLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function writeStream(string $location, $contents, Config $config): void
+ {
+ try {
+ $this->adapter->writeStream($this->prefix->prefixPath($location), $contents, $config);
+ } catch (Throwable $previous) {
+ throw UnableToWriteFile::atLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function setVisibility(string $path, string $visibility): void
+ {
+ try {
+ $this->adapter->setVisibility($this->prefix->prefixPath($path), $visibility);
+ } catch (Throwable $previous) {
+ throw UnableToSetVisibility::atLocation($path, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function delete(string $location): void
+ {
+ try {
+ $this->adapter->delete($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToDeleteFile::atLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function deleteDirectory(string $location): void
+ {
+ try {
+ $this->adapter->deleteDirectory($this->prefix->prefixPath($location));
+ } catch (Throwable $previous) {
+ throw UnableToDeleteDirectory::atLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function createDirectory(string $location, Config $config): void
+ {
+ try {
+ $this->adapter->createDirectory($this->prefix->prefixPath($location), $config);
+ } catch (Throwable $previous) {
+ throw UnableToCreateDirectory::atLocation($location, $previous->getMessage(), $previous);
+ }
+ }
+
+ public function move(string $source, string $destination, Config $config): void
+ {
+ try {
+ $this->adapter->move(
+ $this->prefix->prefixPath($source),
+ $this->prefix->prefixPath($destination),
+ $config
+ );
+ } catch (Throwable $previous) {
+ throw UnableToMoveFile::fromLocationTo($source, $destination, $previous);
+ }
+ }
+
+ public function copy(string $source, string $destination, Config $config): void
+ {
+ try {
+ $this->adapter->copy(
+ $this->prefix->prefixPath($source),
+ $this->prefix->prefixPath($destination),
+ $config
+ );
+ } catch (Throwable $previous) {
+ throw UnableToCopyFile::fromLocationTo($source, $destination, $previous);
+ }
+ }
+
+ public function publicUrl(string $path, Config $config): string
+ {
+ if (! $this->adapter instanceof PublicUrlGenerator) {
+ throw UnableToGeneratePublicUrl::noGeneratorConfigured($path);
+ }
+
+ return $this->adapter->publicUrl($this->prefix->prefixPath($path), $config);
+ }
+
+ public function checksum(string $path, Config $config): string
+ {
+ if ($this->adapter instanceof ChecksumProvider) {
+ return $this->adapter->checksum($this->prefix->prefixPath($path), $config);
+ }
+
+ return $this->calculateChecksumFromStream($path, $config);
+ }
+
+ public function temporaryUrl(string $path, DateTimeInterface $expiresAt, Config $config): string
+ {
+ if (! $this->adapter instanceof TemporaryUrlGenerator) {
+ throw UnableToGenerateTemporaryUrl::noGeneratorConfigured($path);
+ }
+
+ return $this->adapter->temporaryUrl(
+ $this->prefix->prefixPath($path),
+ $expiresAt,
+ $config
+ );
+ }
+}
diff --git a/src/Filesystem/Adapters/S3Adapter.php b/src/Filesystem/Adapters/S3Adapter.php
index 060cf01..2b23bf7 100644
--- a/src/Filesystem/Adapters/S3Adapter.php
+++ b/src/Filesystem/Adapters/S3Adapter.php
@@ -50,7 +50,7 @@ class S3Adapter implements AdapterInterface
];
if (! empty($settings['endpoint'])) {
- $clientConfig['endpoint'] = rtrim($settings['endpoint'], '/');
+ $clientConfig['endpoint'] = rtrim($settings['endpoint'], '/');
$clientConfig['use_path_style_endpoint'] = (bool) ($settings['use_path_style_endpoint'] ?? true);
}
@@ -68,11 +68,14 @@ class S3Adapter implements AdapterInterface
mimeTypeDetector: $this->mimeTypeDetector
);
- if (! empty($settings['prefix'])) {
- $adapter = new PathPrefixedAdapter(
- $adapter,
- trim($settings['prefix'], '/')
- );
+ $prefix = trim((string) ($settings['prefix'] ?? ''), '/');
+
+ if ($prefix !== '') {
+ if (class_exists(PathPrefixedAdapter::class)) {
+ $adapter = new PathPrefixedAdapter($adapter, $prefix);
+ } else {
+ $adapter = new PrefixedAdapter($adapter, $prefix);
+ }
}
return $adapter;
@@ -92,7 +95,7 @@ class S3Adapter implements AdapterInterface
$endpoint = $settings['endpoint'] ?? null;
$region = $settings['region'] ?? 'us-east-1';
$usePathStyle = (bool) ($settings['use_path_style_endpoint'] ?? false);
- $prefix = trim($settings['prefix'] ?? '', '/');
+ $prefix = trim((string) ($settings['prefix'] ?? ''), '/');
$normalizedUrl = null;
diff --git a/src/Helpers/PathHelper.php b/src/Helpers/PathHelper.php
index f566573..1eb452b 100644
--- a/src/Helpers/PathHelper.php
+++ b/src/Helpers/PathHelper.php
@@ -39,4 +39,17 @@ final class PathHelper
return self::trimTrailingSlash($path);
}
+
+ public static function normalizePrefix(?string $prefix): string
+ {
+ if ($prefix === null || $prefix === '') {
+ return '';
+ }
+
+ $prefix = self::stripProtocol($prefix);
+ $prefix = self::normalizeDirectory($prefix);
+ $prefix = self::trimSlashes($prefix);
+
+ return $prefix === '' ? '' : self::ensureTrailingSlash($prefix);
+ }
}
diff --git a/src/Media/ImageEditorGD.php b/src/Media/ImageEditorGD.php
index 706c205..776595e 100644
--- a/src/Media/ImageEditorGD.php
+++ b/src/Media/ImageEditorGD.php
@@ -11,11 +11,18 @@ if (! defined('ABSPATH')) {
exit;
}
-if (! class_exists(\WP_Image_Editor_GD::class) && file_exists(ABSPATH . WPINC . '/class-wp-image-editor-gd.php')) {
- require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
+if (! class_exists(\WP_Image_Editor::class, false)) {
+ require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
}
-if (! class_exists(\WP_Image_Editor_GD::class)) {
+if (! class_exists(\WP_Image_Editor_GD::class, false)) {
+ $gdEditorFile = ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
+ if (file_exists($gdEditorFile)) {
+ require_once $gdEditorFile;
+ }
+}
+
+if (! class_exists(\WP_Image_Editor_GD::class, false)) {
return;
}
diff --git a/src/Media/ImageEditorImagick.php b/src/Media/ImageEditorImagick.php
index 7db893f..0e0fab6 100644
--- a/src/Media/ImageEditorImagick.php
+++ b/src/Media/ImageEditorImagick.php
@@ -11,11 +11,19 @@ if (! defined('ABSPATH')) {
exit;
}
-if (! class_exists(\WP_Image_Editor_Imagick::class) && file_exists(ABSPATH . WPINC . '/class-wp-image-editor-imagick.php')) {
- require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
+if (! class_exists(\WP_Image_Editor::class, false)) {
+ require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
}
-if (! class_exists(\WP_Image_Editor_Imagick::class)) {
+if (! class_exists(\WP_Image_Editor_Imagick::class, false)) {
+ $imagickEditorFile = ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
+
+ if (file_exists($imagickEditorFile)) {
+ require_once $imagickEditorFile;
+ }
+}
+
+if (! class_exists(\WP_Image_Editor_Imagick::class, false)) {
return;
}
diff --git a/src/Plugin.php b/src/Plugin.php
index 644c26d..1ce998e 100644
--- a/src/Plugin.php
+++ b/src/Plugin.php
@@ -1,261 +1,142 @@
mediaHooks = new MediaHooks();
+ public function __construct(
+ FilesystemFactory $filesystemFactory,
+ MediaHooks $mediaHooks,
+ SettingsPage $settingsPage
+ ) {
+ $this->filesystemFactory = $filesystemFactory;
+ $this->mediaHooks = $mediaHooks;
+ $this->settingsPage = $settingsPage;
}
- public static function bootstrap(string $pluginFile): void
+ public function register(): void
{
- self::$pluginFile = $pluginFile;
-
- register_activation_hook($pluginFile, [self::class, 'activate']);
- register_deactivation_hook($pluginFile, [self::class, 'deactivate']);
-
- add_action('plugins_loaded', static function (): void {
- self::instance()->init();
- });
+ add_action('plugins_loaded', [$this, 'bootstrap'], 5);
+ add_action('init', [$this, 'loadTextDomain']);
+ add_filter('plugin_action_links_' . plugin_basename(FlysystemOffload::PLUGIN_FILE), [$this, 'pluginLinks']);
}
- public static function instance(): self
+ public function loadTextDomain(): void
{
- return self::$instance ??= new self();
+ load_plugin_textdomain(
+ 'flysystem-offload',
+ false,
+ dirname(plugin_basename(FlysystemOffload::PLUGIN_FILE)) . '/languages'
+ );
}
- public static function activate(): void
+ public function bootstrap(): void
{
- wp_mkdir_p(WP_CONTENT_DIR . '/flysystem-uploads');
-
- if (! defined('FLYSYSTEM_OFFLOAD_CONFIG') && ! file_exists(WP_CONTENT_DIR . '/flysystem-offload.php')) {
- error_log('[Flysystem Offload] No se encontró un archivo de configuración. Copia config/flysystem-offload.example.php a wp-content/flysystem-offload.php y ajústalo.');
- }
- }
-
- public static function deactivate(): void
- {
- if ($instance = self::$instance) {
- $instance->mediaHooks->unregister();
- $instance->mediaHooks->setFilesystem(null);
+ if ($this->isInitialized) {
+ return;
}
- if (in_array('fly', stream_get_wrappers(), true)) {
- stream_wrapper_unregister('fly');
+ $this->config = $this->settingsPage->getSettings();
+
+ if (! is_array($this->config)) {
+ $this->config = [];
}
- }
- public function init(): void
- {
- $this->configLoader = new ConfigLoader(self::$pluginFile);
- $this->reloadConfig();
+ $this->registerStreamWrapper();
+ $this->mediaHooks->register($this);
+ $this->settingsPage->register($this);
- add_filter('upload_dir', [$this, 'filterUploadDir'], 20);
- add_filter('wp_get_attachment_url', [$this, 'filterAttachmentUrl'], 20, 2);
- add_filter('wp_get_attachment_metadata', [$this, 'filterAttachmentMetadata'], 20);
- add_filter('wp_get_original_image_path', [$this, 'filterOriginalImagePath'], 20);
- add_filter('get_attached_file', [$this, 'filterAttachedFile'], 20, 2);
-
- add_filter('wp_delete_file', [$this, 'handleDeleteFile'], 20);
- add_action('delete_attachment', [$this, 'handleDeleteAttachment'], 20);
- add_action('switch_blog', [$this, 'handleSwitchBlog']);
- add_action('flysystem_offload_reload_config', [$this, 'reloadConfig']);
-
- if (defined('WP_CLI') && WP_CLI && class_exists(\FlysystemOffload\Admin\HealthCheck::class)) {
- \WP_CLI::add_command('flysystem-offload health-check', [\FlysystemOffload\Admin\HealthCheck::class, 'run']);
+ if (defined('WP_CLI') && WP_CLI) {
+ if (class_exists(HealthCheck::class)) {
+ \WP_CLI::add_command('flysystem-offload health-check', new HealthCheck($this->filesystemFactory));
+ }
}
+
+ $this->isInitialized = true;
}
public function reloadConfig(): void
{
- try {
- $this->config = $this->configLoader->load();
- } catch (Throwable $exception) {
- error_log('[Flysystem Offload] Error cargando configuración: ' . $exception->getMessage());
- $this->config = $this->configLoader->defaults();
- }
-
- $this->filesystem = null;
- $this->streamRegistered = false;
-
$this->mediaHooks->unregister();
- $this->mediaHooks->setFilesystem(null);
+ $this->config = $this->settingsPage->getSettings();
+ if (! is_array($this->config)) {
+ $this->config = [];
+ }
$this->registerStreamWrapper();
+ $this->mediaHooks->register($this);
}
- public function handleSwitchBlog(): void
+ public function registerStreamWrapper(): void
{
- $this->reloadConfig();
- }
-
- /**
- * @throws Throwable
- */
- public function getFilesystem(): FilesystemOperator
- {
- if ($this->filesystem === null) {
- $factory = new FilesystemFactory($this->config);
- $result = $factory->make();
-
- if (is_wp_error($result)) {
- throw new \RuntimeException($result->get_error_message());
- }
-
- $this->filesystem = $result;
- }
-
- return $this->filesystem;
- }
-
- public function filterUploadDir(array $dirs): array
- {
- $remoteBase = $this->getRemoteUrlBase();
- $prefix = PathHelper::normalizePrefix($this->config['base_prefix'] ?? '');
-
- $subdir = $dirs['subdir'] ?? '';
- $dirs['path'] = "fly://{$prefix}" . ltrim($subdir, '/');
- $dirs['basedir'] = "fly://{$prefix}";
- $dirs['url'] = trailingslashit($remoteBase) . ltrim($subdir, '/');
- $dirs['baseurl'] = $remoteBase;
-
- return $dirs;
- }
-
- public function filterAttachmentUrl(string $url, int $postId): string
- {
- unset($postId);
-
- $uploadDir = wp_get_upload_dir();
- $localBase = trailingslashit($uploadDir['baseurl'] ?? '');
- $remoteBase = trailingslashit($this->getRemoteUrlBase());
-
- return str_replace($localBase, $remoteBase, $url);
- }
-
- public function filterAttachmentMetadata(array $metadata): array
- {
- if (! empty($metadata['file'])) {
- $metadata['file'] = PathHelper::stripProtocol($metadata['file']);
- }
-
- if (! empty($metadata['sizes'])) {
- foreach ($metadata['sizes'] as &$size) {
- if (! empty($size['file'])) {
- $size['file'] = ltrim(PathHelper::stripProtocol($size['file']), '/');
- }
- }
-
- unset($size);
- }
-
- return $metadata;
- }
-
- public function filterOriginalImagePath(string $path): string
- {
- return PathHelper::ensureFlyProtocol($path);
- }
-
- public function filterAttachedFile(string $file, int $attachmentId): string
- {
- unset($attachmentId);
-
- if (PathHelper::isFlyProtocol($file)) {
- return $file;
- }
-
- return PathHelper::ensureFlyProtocol($file);
- }
-
- public function handleDeleteFile(string $file): string|false
- {
- $flyPath = PathHelper::stripProtocol($file);
-
try {
- $this->getFilesystem()->delete($flyPath);
- } catch (Throwable $exception) {
- error_log('[Flysystem Offload] Error al borrar archivo: ' . $flyPath . ' - ' . $exception->getMessage());
- }
-
- return false;
- }
-
- public function handleDeleteAttachment(int $postId): void
- {
- $files = PathHelper::collectFilesFromAttachment($postId);
-
- foreach ($files as $relativePath) {
- try {
- $this->getFilesystem()->delete($relativePath);
- } catch (Throwable $exception) {
- error_log('[Flysystem Offload] Error al borrar attachment: ' . $relativePath . ' - ' . $exception->getMessage());
- }
- }
- }
-
- private function registerStreamWrapper(): void
- {
- if ($this->streamRegistered) {
- return;
- }
-
- try {
- $filesystem = $this->getFilesystem();
- } catch (Throwable $exception) {
- error_log('[Flysystem Offload] No se pudo obtener el filesystem: ' . $exception->getMessage());
-
- return;
- }
-
- try {
- $visibility = $this->config['visibility'] ?? PortableVisibility::PUBLIC;
+ $filesystem = $this->filesystemFactory->build($this->config);
+ $protocol = $this->config['protocol'] ?? 'fly';
+ $prefix = $this->config['root_prefix'] ?? '';
FlysystemStreamWrapper::register(
$filesystem,
- 'fly',
- PathHelper::normalizePrefix($this->config['base_prefix'] ?? ''),
- ['visibility' => $visibility]
+ $protocol,
+ $prefix,
+ [
+ 'visibility' => $this->config['visibility'] ?? Visibility::PUBLIC,
+ ]
);
-
- $this->streamRegistered = true;
- } catch (Throwable $exception) {
+ } catch (\Throwable $exception) {
error_log('[Flysystem Offload] No se pudo registrar el stream wrapper: ' . $exception->getMessage());
}
-
- $this->mediaHooks->setFilesystem($filesystem);
- $this->mediaHooks->register();
}
- private function getRemoteUrlBase(): string
+ public function pluginLinks(array $links): array
{
- $adapterKey = $this->config['adapter'] ?? 'local';
- $settings = $this->config['adapters'][$adapterKey] ?? [];
+ $settingsUrl = admin_url('options-general.php?page=flysystem-offload');
- return (new FilesystemFactory($this->config))
- ->resolvePublicBaseUrl($adapterKey, $settings);
+ $links[] = '' . esc_html__('Ajustes', 'flysystem-offload') . '';
+
+ return $links;
+ }
+
+ public function getConfig(): array
+ {
+ return $this->config;
+ }
+
+ public function getFilesystemFactory(): FilesystemFactory
+ {
+ return $this->filesystemFactory;
+ }
+
+ public function getRemoteUrlBase(): string
+ {
+ $driver = $this->config['driver'] ?? null;
+
+ if (! $driver) {
+ return '';
+ }
+
+ $result = $this->filesystemFactory->resolvePublicBaseUrl($driver, $this->config);
+
+ if ($result instanceof WP_Error) {
+ throw new RuntimeException($result->get_error_message());
+ }
+
+ return $result;
}
}
diff --git a/src/Settings/SettingsPage.php b/src/Settings/SettingsPage.php
new file mode 100644
index 0000000..d26a5c4
--- /dev/null
+++ b/src/Settings/SettingsPage.php
@@ -0,0 +1,100 @@
+
+ */
+ public function getSettings(): array
+ {
+ $settings = get_option(self::OPTION_KEY, []);
+
+ return is_array($settings) ? $settings : [];
+ }
+
+ public function register(Plugin $plugin): void
+ {
+ add_action('admin_menu', function () {
+ add_options_page(
+ __('Flysystem Offload', 'flysystem-offload'),
+ __('Flysystem Offload', 'flysystem-offload'),
+ 'manage_options',
+ 'flysystem-offload',
+ [$this, 'renderPage']
+ );
+ });
+
+ add_action('admin_init', [$this, 'registerSettings']);
+ }
+
+ public function renderPage(): void
+ {
+ if (! current_user_can('manage_options')) {
+ wp_die(__('No tienes permisos para acceder a esta página.', 'flysystem-offload'));
+ }
+
+ $settings = $this->getSettings();
+ ?>
+
+ ' . esc_html__(
+ 'Introduce las credenciales del proveedor que deseas utilizar.',
+ 'flysystem-offload'
+ ) . '
';
+ },
+ 'flysystem-offload'
+ );
+
+ add_settings_field(
+ 'flysystem_offload_driver',
+ __('Driver', 'flysystem-offload'),
+ [$this, 'renderDriverField'],
+ 'flysystem-offload',
+ 'flysystem_offload_general'
+ );
+ }
+
+ public function renderDriverField(): void
+ {
+ $settings = $this->getSettings();
+ $driver = $settings['driver'] ?? '';
+ ?>
+
+ PortableVisibility::PUBLIC,
+ 'visibility' => Visibility::PUBLIC,
];
stream_wrapper_register($protocol, static::class, STREAM_IS_URL);
@@ -500,6 +500,6 @@ final class FlysystemStreamWrapper
*/
private function writeOptionsForProtocol(string $protocol): array
{
- return self::$writeOptions[$protocol] ?? ['visibility' => PortableVisibility::PUBLIC];
+ return self::$writeOptions[$protocol] ?? ['visibility' => Visibility::PUBLIC];
}
}