147 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
<?php
 | 
						|
 | 
						|
namespace BookStack\Uploads;
 | 
						|
 | 
						|
use Illuminate\Filesystem\FilesystemManager;
 | 
						|
use Illuminate\Support\Str;
 | 
						|
 | 
						|
class ImageStorage
 | 
						|
{
 | 
						|
    public function __construct(
 | 
						|
        protected FilesystemManager $fileSystem,
 | 
						|
    ) {
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the storage disk for the given image type.
 | 
						|
     */
 | 
						|
    public function getDisk(string $imageType = ''): ImageStorageDisk
 | 
						|
    {
 | 
						|
        $diskName = $this->getDiskName($imageType);
 | 
						|
 | 
						|
        return new ImageStorageDisk(
 | 
						|
            $diskName,
 | 
						|
            $this->fileSystem->disk($diskName),
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Check if "local secure restricted" (Fetched behind auth, with permissions enforced)
 | 
						|
     * is currently active in the instance.
 | 
						|
     */
 | 
						|
    public function usingSecureRestrictedImages(): bool
 | 
						|
    {
 | 
						|
        return config('filesystems.images') === 'local_secure_restricted';
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Clean up an image file name to be both URL and storage safe.
 | 
						|
     */
 | 
						|
    public function cleanImageFileName(string $name): string
 | 
						|
    {
 | 
						|
        $name = str_replace(' ', '-', $name);
 | 
						|
        $nameParts = explode('.', $name);
 | 
						|
        $extension = array_pop($nameParts);
 | 
						|
        $name = implode('-', $nameParts);
 | 
						|
        $name = Str::slug($name);
 | 
						|
 | 
						|
        if (strlen($name) === 0) {
 | 
						|
            $name = Str::random(10);
 | 
						|
        }
 | 
						|
 | 
						|
        return $name . '.' . $extension;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the name of the storage disk to use.
 | 
						|
     */
 | 
						|
    protected function getDiskName(string $imageType): string
 | 
						|
    {
 | 
						|
        $storageType = strtolower(config('filesystems.images'));
 | 
						|
        $localSecureInUse = ($storageType === 'local_secure' || $storageType === 'local_secure_restricted');
 | 
						|
 | 
						|
        // Ensure system images (App logo) are uploaded to a public space
 | 
						|
        if ($imageType === 'system' && $localSecureInUse) {
 | 
						|
            return 'local';
 | 
						|
        }
 | 
						|
 | 
						|
        // Rename local_secure options to get our image specific storage driver which
 | 
						|
        // is scoped to the relevant image directories.
 | 
						|
        if ($localSecureInUse) {
 | 
						|
            return 'local_secure_images';
 | 
						|
        }
 | 
						|
 | 
						|
        return $storageType;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get a storage path for the given image URL.
 | 
						|
     * Ensures the path will start with "uploads/images".
 | 
						|
     * Returns null if the url cannot be resolved to a local URL.
 | 
						|
     */
 | 
						|
    public function urlToPath(string $url): ?string
 | 
						|
    {
 | 
						|
        $url = ltrim(trim($url), '/');
 | 
						|
 | 
						|
        // Handle potential relative paths
 | 
						|
        $isRelative = !str_starts_with($url, 'http');
 | 
						|
        if ($isRelative) {
 | 
						|
            if (str_starts_with(strtolower($url), 'uploads/images')) {
 | 
						|
                return trim($url, '/');
 | 
						|
            }
 | 
						|
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
 | 
						|
        // Handle local images based on paths on the same domain
 | 
						|
        $potentialHostPaths = [
 | 
						|
            url('uploads/images/'),
 | 
						|
            $this->getPublicUrl('/uploads/images/'),
 | 
						|
        ];
 | 
						|
 | 
						|
        foreach ($potentialHostPaths as $potentialBasePath) {
 | 
						|
            $potentialBasePath = strtolower($potentialBasePath);
 | 
						|
            if (str_starts_with(strtolower($url), $potentialBasePath)) {
 | 
						|
                return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Gets a public facing url for an image or location at the given path.
 | 
						|
     */
 | 
						|
    public static function getPublicUrl(string $filePath): string
 | 
						|
    {
 | 
						|
        return static::getPublicBaseUrl() . '/' . ltrim($filePath, '/');
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the public base URL used for images.
 | 
						|
     * Will not include any path element of the image file, just the base part
 | 
						|
     * from where the path is then expected to start from.
 | 
						|
     * If s3-style store is in use it will default to guessing a public bucket URL.
 | 
						|
     */
 | 
						|
    protected static function getPublicBaseUrl(): string
 | 
						|
    {
 | 
						|
        $storageUrl = config('filesystems.url');
 | 
						|
 | 
						|
        // Get the standard public s3 url if s3 is set as storage type
 | 
						|
        // Uses the nice, short URL if bucket name has no periods in otherwise the longer
 | 
						|
        // region-based url will be used to prevent http issues.
 | 
						|
        if (!$storageUrl && config('filesystems.images') === 's3') {
 | 
						|
            $storageDetails = config('filesystems.disks.s3');
 | 
						|
            if (!str_contains($storageDetails['bucket'], '.')) {
 | 
						|
                $storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com';
 | 
						|
            } else {
 | 
						|
                $storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $basePath = $storageUrl ?: url('/');
 | 
						|
 | 
						|
        return rtrim($basePath, '/');
 | 
						|
    }
 | 
						|
}
 |