Skip to content

Commit

Permalink
Merge pull request #883 from bobvandevijver/1.0
Browse files Browse the repository at this point in the history
[Loader] Add bundle resource path auto-registration option and root path placeholder referencing
  • Loading branch information
robfrawley authored Feb 28, 2017
2 parents de43101 + 4e8d5bd commit f8542e5
Show file tree
Hide file tree
Showing 21 changed files with 942 additions and 276 deletions.
6 changes: 4 additions & 2 deletions Binary/Locator/FileSystemInsecureLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ class FileSystemInsecureLocator extends FileSystemLocator
*/
protected function generateAbsolutePath($root, $path)
{
if (false !== strpos($path, '..'.DIRECTORY_SEPARATOR)) {
if (false !== strpos($path, '..'.DIRECTORY_SEPARATOR) ||
false !== strpos($path, DIRECTORY_SEPARATOR.'..') ||
false === file_exists($absolute = $root.DIRECTORY_SEPARATOR.$path)) {
return false;
}

return file_exists($absolute = $root.DIRECTORY_SEPARATOR.$path) ? $absolute : false;
return $absolute;
}
}
43 changes: 40 additions & 3 deletions Binary/Locator/FileSystemLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,52 @@ public function setOptions(array $options = array())
* @return string
*/
public function locate($path)
{
if (false !== $absolute = $this->locateUsingRootPlaceholder($path)) {
return $this->sanitizeAbsolutePath($absolute);
}

if (false !== $absolute = $this->locateUsingRootPathsSearch($path)) {
return $this->sanitizeAbsolutePath($absolute);
}

throw new NotLoadableException(sprintf('Source image not resolvable "%s" in root path(s) "%s"',
$path, implode(':', $this->roots)));
}

/**
* @param string $path
*
* @return bool|string
*/
private function locateUsingRootPathsSearch($path)
{
foreach ($this->roots as $root) {
if (false !== $absolute = $this->generateAbsolutePath($root, $path)) {
return $this->sanitizeAbsolutePath($absolute);
return $absolute;
}
}

throw new NotLoadableException(sprintf('Source image not resolvable "%s" in root path(s) "%s"',
$path, implode(':', $this->roots)));
return false;
}

/**
* @param string $path
*
* @return bool|string
*/
private function locateUsingRootPlaceholder($path)
{
if (0 !== strpos($path, '@') || 1 !== preg_match('{@(?<name>[^:]+):(?<path>.+)}', $path, $matches)) {
return false;
}

if (isset($this->roots[$matches['name']])) {
return $this->generateAbsolutePath($this->roots[$matches['name']], $matches['path']);
}

throw new NotLoadableException(sprintf('Invalid root placeholder "%s" for path "%s"',
$matches['name'], $matches['path']));
}

/**
Expand Down
101 changes: 100 additions & 1 deletion DependencyInjection/Factory/Loader/FileSystemLoaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Liip\ImagineBundle\DependencyInjection\Factory\Loader;

use Liip\ImagineBundle\Exception\InvalidArgumentException;
use Liip\ImagineBundle\Utility\Framework\SymfonyFramework;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand All @@ -25,7 +26,7 @@ class FileSystemLoaderFactory extends AbstractLoaderFactory
public function create(ContainerBuilder $container, $loaderName, array $config)
{
$definition = $this->getChildLoaderDefinition();
$definition->replaceArgument(2, $config['data_root']);
$definition->replaceArgument(2, $this->resolveDataRoots($config['data_root'], $config['bundle_resources'], $container));
$definition->replaceArgument(3, $this->createLocatorReference($config['locator']));

return $this->setTaggedLoaderDefinition($loaderName, $definition, $container);
Expand Down Expand Up @@ -56,14 +57,112 @@ public function addConfiguration(ArrayNodeDefinition $builder)
->ifString()
->then(function ($value) { return array($value); })
->end()
->treatNullLike(array())
->treatFalseLike(array())
->defaultValue(array('%kernel.root_dir%/../web'))
->prototype('scalar')
->cannotBeEmpty()
->end()
->end()
->arrayNode('bundle_resources')
->addDefaultsIfNotSet()
->children()
->booleanNode('enabled')
->defaultFalse()
->end()
->enumNode('access_control_type')
->values(array('blacklist', 'whitelist'))
->info('Sets the access control method applied to bundle names in "access_control_list" into a blacklist or whitelist.')
->defaultValue('blacklist')
->end()
->arrayNode('access_control_list')
->defaultValue(array())
->prototype('scalar')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end();
}


/*
* @param string[] $staticPaths
* @param array $config
* @param ContainerBuilder $container
*
* @return string[]
*/
private function resolveDataRoots(array $staticPaths, array $config, ContainerBuilder $container)
{
if (false === $config['enabled']) {
return $staticPaths;
}

$resourcePaths = array();

foreach ($this->getBundleResourcePaths($container) as $name => $path) {
if (('whitelist' === $config['access_control_type']) === in_array($name, $config['access_control_list']) && is_dir($path)) {
$resourcePaths[$name] = $path;
}
}

return array_merge($staticPaths, $resourcePaths);
}

/**
* @param ContainerBuilder $container
*
* @return string[]
*/
private function getBundleResourcePaths(ContainerBuilder $container)
{
if ($container->hasParameter('kernel.bundles_metadata')) {
$paths = $this->getBundlePathsUsingMetadata($container->getParameter('kernel.bundles_metadata'));
} else {
$paths = $this->getBundlePathsUsingNamedObj($container->getParameter('kernel.bundles'));
}

return array_map(function ($path) {
return $path.DIRECTORY_SEPARATOR.'Resources'.DIRECTORY_SEPARATOR.'public';
}, $paths);
}

/**
* @param array[] $metadata
*
* @return string[]
*/
private function getBundlePathsUsingMetadata(array $metadata)
{
return array_combine(array_keys($metadata), array_map(function ($data) {
return $data['path'];
}, $metadata));
}

/**
* @param string[] $classes
*
* @return string[]
*/
private function getBundlePathsUsingNamedObj(array $classes)
{
$paths = array();

foreach ($classes as $c) {
try {
$r = new \ReflectionClass($c);
} catch (\ReflectionException $exception) {
throw new InvalidArgumentException(sprintf('Unable to resolve bundle "%s" while auto-registering bundle resource paths.', $c), null, $exception);
}

$paths[$r->getShortName()] = dirname($r->getFileName());
}

return $paths;
}

/**
* @param string $reference
*
Expand Down
75 changes: 69 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,12 @@ $response = $imagine
```


## Images Outside Data Root
## Data Roots

When your setup requires your source images reside outside the web root,
you have to set your loader's `data_root` parameter in your configuration
(often `app/config/config.yml`) with the absolute path to your source image
location.
By default, Symfony's `web/` directory is registered as a data root to load
assets from. For many installations this will be sufficient, but sometime you
may need to load images from other locations. To do this, you must set the
`data_root` parameter in your configuration (often located at `app/config/config.yml`).

```yml
liip_imagine:
Expand All @@ -382,7 +382,70 @@ liip_imagine:
data_root: /path/to/source/images/dir
```

This location must be readable by your web server. On a system that supports
As of version `1.7.2` you can register multiple data root paths, and the
file locator will search each for the requested file.

```yml
liip_imagine:
loaders:
default:
filesystem:
data_root:
- /path/foo
- /path/bar
```

As of version `1.7.3` you ask for the public resource paths from all registered bundles
to be auto-registered as data roots. This allows you to load assets from the
`Resources/public` folders that reside within the loaded bundles. To enable this
feature, set the `bundle_resources.enabled` configuration option to `true`.

```yml
liip_imagine:
loaders:
default:
filesystem:
bundle_resources:
enabled: true
```

If you want to register some of the `Resource/public` folders, but not all, you can do
so by blacklisting the bundles you don't want registered or whitelisting the bundles you
do want registered. For example, to blacklist (not register) the bundles "FooBundle" and
"BarBundle", you would use the following configuration.

```yml
liip_imagine:
loaders:
default:
filesystem:
bundle_resources:
enabled: true
access_control_type: blacklist
access_control_list:
- FooBundle
- BarBundle
```

Alternatively, if you want to whitelist (only register) the bundles "FooBundle" and "BarBundle",
you would use the following configuration.

```yml
liip_imagine:
loaders:
default:
filesystem:
bundle_resources:
enabled: true
access_control_type: whitelist
access_control_list:
- FooBundle
- BarBundle
```

### Permissions

Image locations must be readable by your web server. On a system that supports
`setfacl` (such as Linux/BSD), use

```sh
Expand Down
Loading

0 comments on commit f8542e5

Please sign in to comment.