diff --git a/.gitignore b/.gitignore index 972937e..62d2502 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -vendor/ -node_modules/ +/vendor/ +/node_modules/ .idea/ *.log *.txt diff --git a/assets/admin-settings.js b/assets/admin-settings.js deleted file mode 100644 index c966c83..0000000 --- a/assets/admin-settings.js +++ /dev/null @@ -1,42 +0,0 @@ -(function ($) { - const settings = window.flysystemOffloadSettings || {}; - const s3Label = settings.labels ? settings.labels.s3 : 'Amazon S3 / Compatible'; - - function ensureS3Wrapper() { - const $heading = $('h2').filter(function () { - return $(this).text().trim() === s3Label; - }); - - if ($heading.length && !$heading.parent().hasClass('flysystem-offload-adapter-section')) { - const $table = $heading.next('table'); - if ($table.length) { - const $wrapper = $('
' . esc_html__('Proporciona credenciales de un bucket S3 o compatible (MinIO, DigitalOcean Spaces, etc.).', 'flysystem-offload') . '
'; - }, - 'flysystem-offload' - ); - - $this->registerS3Fields(); - } - - private function registerS3Fields(): void - { - $fields = [ - [ - 'id' => 's3_access_key', - 'label' => __('Access Key', 'flysystem-offload'), - 'args' => ['key' => 'access_key', 'type' => 'text'], - ], - [ - 'id' => 's3_secret_key', - 'label' => __('Secret Key', 'flysystem-offload'), - 'args' => ['key' => 'secret_key', 'type' => 'password'], - ], - [ - 'id' => 's3_region', - 'label' => __('Región', 'flysystem-offload'), - 'args' => ['key' => 'region', 'type' => 'text', 'placeholder' => 'us-east-1'], - ], - [ - 'id' => 's3_bucket', - 'label' => __('Bucket', 'flysystem-offload'), - 'args' => ['key' => 'bucket', 'type' => 'text'], - ], - [ - 'id' => 's3_prefix', - 'label' => __('Prefijo (opcional)', 'flysystem-offload'), - 'args' => ['key' => 'prefix', 'type' => 'text', 'placeholder' => 'uploads'], - ], - [ - 'id' => 's3_endpoint', - 'label' => __('Endpoint personalizado', 'flysystem-offload'), - 'args' => ['key' => 'endpoint', 'type' => 'text', 'placeholder' => 'https://nyc3.digitaloceanspaces.com'], - ], - [ - 'id' => 's3_cdn_url', - 'label' => __('URL CDN (opcional)', 'flysystem-offload'), - 'args' => ['key' => 'cdn_url', 'type' => 'text', 'placeholder' => 'https://cdn.midominio.com'], - ], - ]; - - foreach ($fields as $field) { - $args = array_merge( - $field['args'], - ['class' => 'flysystem-offload-field flysystem-offload-field--s3'] - ); - - add_settings_field( - $field['id'], - $field['label'], - [$this, 'renderS3Field'], - 'flysystem-offload', - 'flysystem_offload_s3', - $args - ); - } - } - - public function enqueueAssets(): void - { - $screen = get_current_screen(); - - if (! $screen || $screen->id !== 'settings_page_flysystem-offload') { - return; - } - - wp_enqueue_script( - 'flysystem-offload-settings', - plugins_url('assets/admin-settings.js', $this->pluginFile), - ['jquery'], - '0.1.1', - true - ); - - wp_localize_script( - 'flysystem-offload-settings', - 'flysystemOffloadSettings', - [ - 'labels' => [ - 's3' => __('Amazon S3 / Compatible', 'flysystem-offload'), - ], - ] - ); - } - - public function sanitizeSettings(array $input): array - { - $current = get_option('flysystem_offload_settings', []); - $adapter = sanitize_key($input['adapter'] ?? $current['adapter'] ?? 'local'); - $basePrefix = trim($input['base_prefix'] ?? ''); - - $s3 = $current['adapters']['s3'] ?? []; - $inputS3 = $input['adapters']['s3'] ?? []; - - $secretRaw = $inputS3['secret_key'] ?? ''; - $secret = $secretRaw === '' ? ($s3['secret_key'] ?? '') : $secretRaw; - - $sanitizedS3 = [ - 'access_key' => sanitize_text_field($inputS3['access_key'] ?? $s3['access_key'] ?? ''), - 'secret_key' => sanitize_text_field($secret), - 'region' => sanitize_text_field($inputS3['region'] ?? $s3['region'] ?? ''), - 'bucket' => sanitize_text_field($inputS3['bucket'] ?? $s3['bucket'] ?? ''), - 'prefix' => trim($inputS3['prefix'] ?? $s3['prefix'] ?? ''), - 'endpoint' => esc_url_raw($inputS3['endpoint'] ?? $s3['endpoint'] ?? ''), - 'cdn_url' => esc_url_raw($inputS3['cdn_url'] ?? $s3['cdn_url'] ?? ''), - ]; - - $current['adapter'] = $adapter; - $current['base_prefix'] = $basePrefix; - $current['adapters']['s3'] = $sanitizedS3; - - return $current; - } - - public function render(): void - { - ?> -- -
- - - -- -
- pluginDir = dirname($pluginFile); + } + + /** + * Devuelve la configuración efectiva (defaults + overrides). + * + * @throws \RuntimeException cuando un archivo config no retorna array. + */ + public function load(): array + { + $defaults = $this->defaults(); + $files = $this->discoverConfigFiles(); + + $config = []; + + foreach ($files as $file) { + $data = require $file; + + if (! is_array($data)) { + throw new \RuntimeException( + sprintf('[Flysystem Offload] El archivo de configuración "%s" debe retornar un array.', $file) + ); + } + + $config = array_replace_recursive($config, $data); + } + + if (empty($config)) { + $config = $defaults; + } else { + $config = array_replace_recursive($defaults, $config); + } + + /** + * Permite ajustar/ensanchar la configuración en tiempo de ejecución. + * Ideal para multisite o integraciones externas. + * + * @param array $config + * @param array $files Lista de archivos usados (en orden de carga). + */ + return apply_filters('flysystem_offload_config', $config, $files); + } + + /** + * Defaults que garantizan compatibilidad si falta un archivo. + */ + public function defaults(): array + { + return [ + 'adapter' => 'local', + 'base_prefix' => '', + 'adapters' => [ + 's3' => [ + 'access_key' => '', + 'secret_key' => '', + 'region' => '', + 'bucket' => '', + 'prefix' => '', + 'endpoint' => '', + 'cdn_url' => '', + ], + 'sftp' => [ + 'host' => '', + 'port' => 22, + 'username' => '', + 'password' => '', + 'root' => '/uploads', + ], + 'gcs' => [ + 'project_id' => '', + 'bucket' => '', + 'key_file_path' => '', + ], + 'azure' => [ + 'account_name' => '', + 'account_key' => '', + 'container' => '', + 'prefix' => '', + ], + 'webdav' => [ + 'base_uri' => '', + 'username' => '', + 'password' => '', + 'path_prefix'=> '', + ], + 'googledrive' => [], + 'onedrive' => [], + 'dropbox' => [], + ], + ]; + } + + /** + * Descubre archivos de configuración en orden de prioridad. + * + * @return string[] + */ + private function discoverConfigFiles(): array + { + $candidates = []; + + if (defined('FLYSYSTEM_OFFLOAD_CONFIG')) { + $candidates[] = FLYSYSTEM_OFFLOAD_CONFIG; + } + + // Opción por defecto en wp-content/. + $candidates[] = WP_CONTENT_DIR . '/flysystem-offload.php'; + + // Alias alternativo frecuente. + $candidates[] = WP_CONTENT_DIR . '/flysystem-offload-config.php'; + + // Fallback incluido dentro del plugin (para entornos sin personalización inicial). + $candidates[] = $this->pluginDir . '/config/flysystem-offload.php'; + + $unique = array_unique(array_filter( + $candidates, + static fn (string $path) => is_readable($path) + )); + + return array_values($unique); + } +} diff --git a/src/Filesystem/Adapters/S3Adapter.php b/src/Filesystem/Adapters/S3Adapter.php index 6a47e00..12f5bf6 100644 --- a/src/Filesystem/Adapters/S3Adapter.php +++ b/src/Filesystem/Adapters/S3Adapter.php @@ -49,17 +49,32 @@ class S3Adapter implements AdapterInterface public function publicBaseUrl(array $settings): string { - if (!empty($settings['cdn_url'])) { + if (! empty($settings['cdn_url'])) { return rtrim($settings['cdn_url'], '/'); } - $bucket = $settings['bucket']; - $region = $settings['region']; + $bucket = $settings['bucket'] ?? ''; + $prefix = isset($settings['prefix']) ? trim($settings['prefix'], '/') : ''; + $prefix = $prefix === '' ? '' : '/' . $prefix; - if ($region === 'us-east-1') { - return "https://{$bucket}.s3.amazonaws.com"; + if (! empty($settings['endpoint'])) { + $endpoint = trim($settings['endpoint']); + if (! preg_match('#^https?://#i', $endpoint)) { + $endpoint = 'https://' . $endpoint; + } + + $endpoint = rtrim($endpoint, '/'); + + // Cuando se usa endpoint propio forzamos path-style (+ bucket en la ruta) + return $endpoint . '/' . $bucket . $prefix; } - return "https://{$bucket}.s3.{$region}.amazonaws.com"; + $region = $settings['region'] ?? 'us-east-1'; + + if ($region === 'us-east-1') { + return "https://{$bucket}.s3.amazonaws.com{$prefix}"; + } + + return "https://{$bucket}.s3.{$region}.amazonaws.com{$prefix}"; } } diff --git a/src/Media/ImageEditorImagick.php b/src/Media/ImageEditorImagick.php new file mode 100644 index 0000000..b5c849b --- /dev/null +++ b/src/Media/ImageEditorImagick.php @@ -0,0 +1,137 @@ +image instanceof \Imagick) { + return true; + } + + if (empty($this->file)) { + return new WP_Error( + 'flysystem_offload_missing_file', + __('Archivo no definido.', 'flysystem-offload') + ); + } + + if (! $this->isFlysystemPath($this->file)) { + return parent::load(); + } + + $localPath = $this->mirrorToLocal($this->file); + + if (is_wp_error($localPath)) { + return $localPath; + } + + $this->remoteFilename = $this->file; + $this->file = $localPath; + + $result = parent::load(); + + $this->file = $this->remoteFilename; + + return $result; + } + + protected function _save($image, $filename = null, $mime_type = null) + { + [$filename, $extension, $mime_type] = $this->get_output_format($filename, $mime_type); + + if (! $filename) { + $filename = $this->generate_filename(null, null, $extension); + } + + $isRemote = $this->isFlysystemPath($filename); + $tempTarget = $isRemote ? $this->createTempFile($filename) : false; + + $result = parent::_save($image, $tempTarget ?: $filename, $mime_type); + + if (is_wp_error($result)) { + if ($tempTarget) { + @unlink($tempTarget); + } + + return $result; + } + + if ($tempTarget) { + $copy = copy($result['path'], $filename); + + @unlink($result['path']); + @unlink($tempTarget); + + if (! $copy) { + return new WP_Error( + 'flysystem_offload_copy_failed', + __('No se pudo copiar la imagen procesada al almacenamiento remoto.', 'flysystem-offload') + ); + } + + $result['path'] = $filename; + $result['file'] = wp_basename($filename); + } + + return $result; + } + + public function __destruct() + { + foreach ($this->tempFiles as $temp) { + @unlink($temp); + } + + parent::__destruct(); + } + + protected function mirrorToLocal(string $remotePath) + { + $tempFile = $this->createTempFile($remotePath); + + if (! $tempFile) { + return new WP_Error( + 'flysystem_offload_temp_missing', + __('No se pudo crear un archivo temporal para la imagen remota.', 'flysystem-offload') + ); + } + + if (! copy($remotePath, $tempFile)) { + @unlink($tempFile); + + return new WP_Error( + 'flysystem_offload_remote_copy_failed', + __('No se pudo copiar la imagen desde el almacenamiento remoto.', 'flysystem-offload') + ); + } + + $this->tempFiles[] = $tempFile; + + return $tempFile; + } + + protected function createTempFile(string $context) + { + if (! function_exists('wp_tempnam')) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + + $tempFile = wp_tempnam(wp_basename($context)); + + return $tempFile ?: false; + } + + protected function isFlysystemPath(string $path): bool + { + return strpos($path, 'fly://') === 0; + } +} diff --git a/src/Media/MediaHooks.php b/src/Media/MediaHooks.php new file mode 100644 index 0000000..d10e2be --- /dev/null +++ b/src/Media/MediaHooks.php @@ -0,0 +1,287 @@ +filesystem = $filesystem; + } + + public function register(): void + { + if ($this->registered) { + return; + } + + add_filter('wp_image_editors', [$this, 'filterImageEditors'], 5); + add_filter('pre_move_uploaded_file', [$this, 'handlePreMoveUploadedFile'], 10, 3); + add_filter('wp_read_image_metadata', [$this, 'filterReadImageMetadata'], 10, 2); + add_filter('wp_generate_attachment_metadata', [$this, 'filterGenerateAttachmentMetadata'], 10, 2); + add_filter('pre_wp_unique_filename_file_list', [$this, 'filterUniqueFilenameFileList'], 10, 3); + add_action('delete_attachment', [$this, 'handleDeleteAttachment'], 10); + + $this->registered = true; + } + + public function unregister(): void + { + if (! $this->registered) { + return; + } + + remove_filter('wp_image_editors', [$this, 'filterImageEditors'], 5); + remove_filter('pre_move_uploaded_file', [$this, 'handlePreMoveUploadedFile'], 10); + remove_filter('wp_read_image_metadata', [$this, 'filterReadImageMetadata'], 10); + remove_filter('wp_generate_attachment_metadata', [$this, 'filterGenerateAttachmentMetadata'], 10); + remove_filter('pre_wp_unique_filename_file_list', [$this, 'filterUniqueFilenameFileList'], 10); + remove_action('delete_attachment', [$this, 'handleDeleteAttachment']); + + $this->registered = false; + } + + public function filterImageEditors(array $editors): array + { + $imagickIndex = array_search(\WP_Image_Editor_Imagick::class, $editors, true); + + if ($imagickIndex !== false) { + unset($editors[$imagickIndex]); + } + + array_unshift($editors, ImageEditorImagick::class); + + return array_values(array_unique($editors)); + } + + /** + * Sobreescribe el movimiento final del archivo para subirlo a fly:// mediante Flysystem. + */ + public function handlePreMoveUploadedFile($override, array $file, string $destination) + { + if ($override !== null) { + return $override; + } + + if (! $this->isFlyPath($destination)) { + return $override; + } + + if (! $this->filesystem) { + return new WP_Error( + 'flysystem_offload_missing_filesystem', + __('No se pudo acceder al filesystem remoto.', 'flysystem-offload') + ); + } + + $relativePath = $this->relativeFlyPath($destination); + + if ($relativePath === null) { + return new WP_Error( + 'flysystem_offload_invalid_destination', + __('Ruta de destino inválida para el stream fly://.', 'flysystem-offload') + ); + } + + $directory = dirname($relativePath); + + if ($directory !== '' && $directory !== '.') { + try { + $this->filesystem->createDirectory($directory); + } catch (FilesystemException $e) { + return new WP_Error( + 'flysystem_offload_directory_error', + sprintf( + __('No se pudo crear el directorio remoto "%s": %s', 'flysystem-offload'), + esc_html($directory), + $e->getMessage() + ) + ); + } + } + + $resource = @fopen($file['tmp_name'], 'rb'); + + if (! $resource) { + return new WP_Error( + 'flysystem_offload_tmp_read_fail', + __('No se pudo leer el archivo temporal subido.', 'flysystem-offload') + ); + } + + try { + $this->filesystem->writeStream($relativePath, $resource); + } catch (FilesystemException $e) { + if (is_resource($resource)) { + fclose($resource); + } + + return new WP_Error( + 'flysystem_offload_write_fail', + sprintf( + __('No se pudo guardar el archivo en el almacenamiento remoto: %s', 'flysystem-offload'), + $e->getMessage() + ) + ); + } + + if (is_resource($resource)) { + fclose($resource); + } + + @unlink($file['tmp_name']); + + return true; + } + + public function filterReadImageMetadata($metadata, string $file) + { + if ($this->metadataMirrorInProgress || ! $this->isFlyPath($file)) { + return $metadata; + } + + $this->metadataMirrorInProgress = true; + + $temp = $this->mirrorToLocal($file); + + if (! is_wp_error($temp)) { + $metadata = wp_read_image_metadata($temp); + @unlink($temp); + } + + $this->metadataMirrorInProgress = false; + + return $metadata; + } + + public function filterGenerateAttachmentMetadata(array $metadata, int $attachmentId): array + { + if (isset($metadata['filesize'])) { + return $metadata; + } + + $file = get_attached_file($attachmentId); + + if ($file && file_exists($file)) { + $metadata['filesize'] = filesize($file); + } + + return $metadata; + } + + public function filterUniqueFilenameFileList($files, string $dir, string $filename) + { + if (! $this->isFlyPath($dir) || ! $this->filesystem) { + return $files; + } + + $relativeDir = $this->relativeFlyPath($dir); + + if ($relativeDir === null) { + return $files; + } + + $existing = []; + + foreach ($this->filesystem->listContents($relativeDir, false) as $item) { + /** @var StorageAttributes $item */ + if ($item->isDir()) { + continue; + } + + $existing[] = basename($item->path()); + } + + return $existing; + } + + public function handleDeleteAttachment(int $attachmentId): void + { + $file = get_attached_file($attachmentId); + + if (! $file || ! $this->isFlyPath($file)) { + return; + } + + $meta = wp_get_attachment_metadata($attachmentId); + + if (! empty($meta['sizes'])) { + foreach ($meta['sizes'] as $sizeInfo) { + if (empty($sizeInfo['file'])) { + continue; + } + + wp_delete_file(str_replace(basename($file), $sizeInfo['file'], $file)); + } + } + + $original = get_post_meta($attachmentId, 'original_image', true); + if ($original) { + wp_delete_file(str_replace(basename($file), $original, $file)); + } + + $backup = get_post_meta($attachmentId, '_wp_attachment_backup_sizes', true); + if (is_array($backup)) { + foreach ($backup as $sizeInfo) { + if (empty($sizeInfo['file'])) { + continue; + } + + wp_delete_file(str_replace(basename($file), $sizeInfo['file'], $file)); + } + } + + wp_delete_file($file); + } + + protected function mirrorToLocal(string $remotePath) + { + if (! function_exists('wp_tempnam')) { + require_once ABSPATH . 'wp-admin/includes/file.php'; + } + + $temp = wp_tempnam(wp_basename($remotePath)); + + if (! $temp) { + return new WP_Error( + 'flysystem_offload_temp_fail', + __('No se pudo crear un archivo temporal para la lectura de metadatos.', 'flysystem-offload') + ); + } + + if (! @copy($remotePath, $temp)) { + @unlink($temp); + + return new WP_Error( + 'flysystem_offload_remote_copy_fail', + __('No se pudo copiar la imagen desde el almacenamiento remoto.', 'flysystem-offload') + ); + } + + return $temp; + } + + protected function isFlyPath(string $path): bool + { + return strpos($path, 'fly://') === 0; + } + + protected function relativeFlyPath(string $path): ?string + { + if (! $this->isFlyPath($path)) { + return null; + } + + return ltrim(substr($path, 6), '/'); + } +} diff --git a/src/Plugin.php b/src/Plugin.php index cb37308..e78eab7 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -1,20 +1,28 @@ mediaHooks = new MediaHooks(); + } public static function bootstrap(string $pluginFile): void { @@ -35,53 +43,20 @@ class Plugin public static function activate(): void { - $defaults = [ - 'adapter' => 'local', - 'base_prefix' => '', - 'adapters' => [ - 's3' => [ - 'access_key' => '', - 'secret_key' => '', - 'region' => '', - 'bucket' => '', - 'endpoint' => '', - 'cdn_url' => '' - ], - 'sftp' => [ - 'host' => '', - 'port' => 22, - 'username' => '', - 'password' => '', - 'root' => '/uploads' - ], - 'gcs' => [ - 'project_id' => '', - 'bucket' => '', - 'key_file_path' => '' - ], - 'azure' => [ - 'account_name' => '', - 'account_key' => '', - 'container' => '', - 'prefix' => '' - ], - 'webdav' => [ - 'base_uri' => '', - 'username' => '', - 'password' => '', - 'path_prefix' => '' - ], - 'googledrive' => [], - 'onedrive' => [], - 'dropbox' => [] - ] - ]; + wp_mkdir_p(WP_CONTENT_DIR . '/flysystem-uploads'); - add_option('flysystem_offload_settings', $defaults); + 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 (in_array('fly', stream_get_wrappers(), true)) { stream_wrapper_unregister('fly'); } @@ -89,50 +64,54 @@ class Plugin public function init(): void { - $this->settings = get_option('flysystem_offload_settings', []); + $this->configLoader = new ConfigLoader(self::$pluginFile); - add_action( - 'update_option_flysystem_offload_settings', - function ($oldValue, $newValue) { - $this->settings = $newValue; - $this->filesystem = null; - $this->streamRegistered = false; - $this->registerStreamWrapper(); - }, - 10, - 2 - ); - - $this->registerStreamWrapper(); + $this->reloadConfig(); 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('wp_delete_file', [$this, 'handleDeleteFile'], 20); + add_action('delete_attachment', [$this, 'handleDeleteAttachment'], 20); + add_action('switch_blog', [$this, 'handleSwitchBlog']); - add_action('switch_blog', function () { - $this->filesystem = null; - $this->streamRegistered = false; - $this->settings = get_option('flysystem_offload_settings', []); - $this->registerStreamWrapper(); - }); - - if (is_admin()) { - (new SettingsPage(self::$pluginFile))->boot(); - } + add_action('flysystem_offload_reload_config', [$this, 'reloadConfig']); if (defined('WP_CLI') && WP_CLI) { \WP_CLI::add_command('flysystem-offload health-check', [Admin\HealthCheck::class, 'run']); } } + public function reloadConfig(): void + { + try { + $this->config = $this->configLoader->load(); + } catch (\Throwable $e) { + error_log('[Flysystem Offload] Error cargando configuración: ' . $e->getMessage()); + $this->config = $this->configLoader->defaults(); + } + + $this->filesystem = null; + $this->streamRegistered = false; + + $this->mediaHooks->unregister(); + $this->mediaHooks->setFilesystem(null); + + $this->registerStreamWrapper(); + } + + public function handleSwitchBlog(): void + { + $this->reloadConfig(); + } + public function getFilesystem(): FilesystemOperator { - if (!$this->filesystem) { - $factory = new FilesystemFactory($this->settings); - $result = $factory->make(); + if (! $this->filesystem) { + $factory = new FilesystemFactory($this->config); + $result = $factory->make(); if (is_wp_error($result)) { throw new \RuntimeException($result->get_error_message()); @@ -151,22 +130,36 @@ class Plugin } try { - FlysystemStreamWrapper::register($this->getFilesystem(), 'fly', PathHelper::normalizePrefix($this->settings['base_prefix'] ?? '')); + $filesystem = $this->getFilesystem(); + } catch (\Throwable $e) { + error_log('[Flysystem Offload] No se pudo obtener el filesystem: ' . $e->getMessage()); + return; + } + + try { + FlysystemStreamWrapper::register( + $filesystem, + 'fly', + PathHelper::normalizePrefix($this->config['base_prefix'] ?? '') + ); $this->streamRegistered = true; } catch (\Throwable $e) { error_log('[Flysystem Offload] No se pudo registrar el stream wrapper: ' . $e->getMessage()); } + + $this->mediaHooks->setFilesystem($filesystem); + $this->mediaHooks->register(); } public function filterUploadDir(array $dirs): array { $remoteBase = $this->getRemoteUrlBase(); - $prefix = PathHelper::normalizePrefix($this->settings['base_prefix'] ?? ''); - $subdir = $dirs['subdir'] ?? ''; + $prefix = PathHelper::normalizePrefix($this->config['base_prefix'] ?? ''); + $subdir = $dirs['subdir'] ?? ''; - $dirs['path'] = "fly://{$prefix}{$subdir}"; + $dirs['path'] = "fly://{$prefix}{$subdir}"; $dirs['basedir'] = "fly://{$prefix}"; - $dirs['url'] = trailingslashit($remoteBase) . ltrim($subdir, '/'); + $dirs['url'] = trailingslashit($remoteBase) . ltrim($subdir, '/'); $dirs['baseurl'] = $remoteBase; return $dirs; @@ -174,7 +167,7 @@ class Plugin public function filterAttachmentUrl(string $url, int $postId): string { - $localBase = trailingslashit(wp_get_upload_dir()['baseurl']); + $localBase = trailingslashit(wp_get_upload_dir()['baseurl']); $remoteBase = trailingslashit($this->getRemoteUrlBase()); return str_replace($localBase, $remoteBase, $url); @@ -182,16 +175,17 @@ class Plugin public function filterAttachmentMetadata(array $metadata): array { - if (!empty($metadata['file'])) { + if (! empty($metadata['file'])) { $metadata['file'] = PathHelper::stripProtocol($metadata['file']); } - if (!empty($metadata['sizes'])) { + if (! empty($metadata['sizes'])) { foreach ($metadata['sizes'] as &$size) { - if (!empty($size['file'])) { + if (! empty($size['file'])) { $size['file'] = ltrim($size['file'], '/'); } } + unset($size); } return $metadata; @@ -230,9 +224,9 @@ class Plugin private function getRemoteUrlBase(): string { - $adapterKey = $this->settings['adapter'] ?? 'local'; - $config = $this->settings['adapters'][$adapterKey] ?? []; + $adapterKey = $this->config['adapter'] ?? 'local'; + $settings = $this->config['adapters'][$adapterKey] ?? []; - return (new FilesystemFactory($this->settings))->resolvePublicBaseUrl($adapterKey, $config); + return (new FilesystemFactory($this->config))->resolvePublicBaseUrl($adapterKey, $settings); } }