Compare commits

...

87 Commits

Author SHA1 Message Date
zyphlar
3f430da113 Update composer.json 2017-03-11 02:05:18 -08:00
Mathias STRASSER
0d52e63fe4 Merge pull request #144 from stephanvierkant/docs
Improved documentation
2017-01-19 10:00:11 +01:00
stephanvierkant
d21986797f Improved documentation 2017-01-16 14:00:11 +01:00
Andrew Kovalyov
cee7dbfbed Reuse has method in FilesystemMap 2016-02-04 09:27:14 +02:00
Andrew Kovalyov
07a46cc277 Merge pull request #135 from JHGitty/patch-1
Add has($name) function to FilesystemMap
2016-02-04 06:56:56 +00:00
JHGitty
22229b5349 Add has($name) function to FilesystemMap 2016-01-31 00:12:35 +01:00
Andrew Kovalyov
02e1281373 Add a not about upgrade from 0.2 to 0.3 2016-01-16 03:47:59 +02:00
Andrew Kovalyov
9d894fd2cf Typo fix 2016-01-16 03:47:06 +02:00
Andrew Kovalyov
4beba820fb Rename UPGRADE to UPGRADE.md 2016-01-16 03:46:46 +02:00
Andrew Kovalyov
c68f9a7b4e Point to stable release in readme 2016-01-16 03:46:04 +02:00
Andrew Kovalyov
44cf552e14 Add travis badge 2016-01-16 02:12:11 +02:00
Andrew Kovalyov
59afa91134 Rename README.markdown to README.md 2016-01-16 02:11:43 +02:00
Andrew Kovalyov
988188477f Prepare for 0.3 2016-01-16 02:08:29 +02:00
Albin Kerouanton
45dc19248b Merge pull request #127 from tgabi333/sf3
[WIP] allow symfony3
2015-12-07 11:14:48 +01:00
Tóth Gábor
ad36fa50d0 allow sf3 2015-12-03 19:29:31 +01:00
Florian Klein
0b2750033a Merge pull request #102 from Karisch/feature/google-cloud-storage
Add support for Google Cloud Storage
2015-11-10 10:25:29 +01:00
Yann Rabiller
7edea6cfcc Merge pull request #115 from diimpp/patch-1
Fix typo in phpseclib adapter's readme example
2015-09-18 14:09:25 +02:00
Andrew Kovalyov
bf9652537f Add a note about Dropbox 2015-09-11 15:08:19 +03:00
Florian Klein
b6dbd93d12 Merge pull request #117 from stof/patch-1
Improve the composer.json
2015-08-31 08:57:20 +02:00
Christophe Coevoet
b8596b4ca1 Improve the composer.json
- allow both stable and dev versions of Gaufrette to make the usage of the bundle easier
- switch autoloading to PSR-4 rather than using the legacy "target-dir" setting
- change the PHPUnit requirement to use maintained versions
2015-08-05 11:24:03 +02:00
Dmitri Perunov
0536334f7c Fix typo in phpseclib adapter's readme example 2015-05-14 11:44:58 +05:00
Florian Klein
b928e97767 Merge pull request #108 from diimpp/phpseclibsftp_adapter
Add phpseclib sftp adapter
2015-05-11 09:02:46 +02:00
Florian Klein
6878189ba2 Merge pull request #94 from escapestudios/master
ObjectStore-method became objectStoreService-method in php-opencloud
2015-04-21 16:42:49 +00:00
Dmitri Perunov
5b1463cdc5 Add phpseclib sftp adapter 2015-02-10 16:28:08 +06:00
Patrik Karisch
65f587ae17 Add documentation 2014-11-01 12:28:12 +01:00
Patrik Karisch
db25e67b10 Add support for Google Cloud Storage 2014-11-01 12:27:35 +01:00
Leszek Prabucki
c88039e80c Merge pull request #96 from pilot/master
Remove support symfony 2.0 deps instruction
2014-07-08 18:28:42 +02:00
Alex Demchenko
a075802e29 Remove support symfony 2.0 deps instruction 2014-07-08 17:50:44 +03:00
David Joos
b6d58c99c3 ObjectStore-method became objectStoreService-method in php-opencloud
Fixes "call to undefined method OpenCloud\Rackspace::ObjectStore()" we were experiencing...
2014-06-19 14:42:41 +01:00
Leszek Prabucki
ce0e00c554 Merge pull request #91 from bmeynell/master
added detect_content_type option for AwsS3 adapter
2014-06-03 10:10:53 +02:00
Leszek Prabucki
b2a387580e Merge pull request #85 from kbond/config-dump
Enable dumping of config
2014-06-03 10:09:31 +02:00
Ben Meynell
d1eb6f2550 added detect_content_type option for AwsS3 adapter 2014-05-05 09:10:50 -04:00
Kevin Bond
ef57204cd5 enable dumping of config 2014-04-08 14:41:46 -04:00
l3l0
ae598e6114 Allows to latest gaufrette version at master 2014-03-27 09:59:55 +01:00
Leszek Prabucki
f330085843 Merge pull request #80 from shieldo/patch-1
use stable releases of gaufrette (~0.1.7)
2014-03-24 23:15:03 +01:00
Douglas Greenshields
886fdf6ad3 use stable releases of gaufrette (~0.1.7) 2014-03-09 02:20:29 +00:00
Leszek Prabucki
171844b400 Merge pull request #69 from Gesthispania/master
Add FTP SSL/TSL Option
2013-12-30 00:59:49 -08:00
Leszek Prabucki
212c2db832 Merge pull request #53 from nmpolo/master
Allow AclAwareAmazonS3Adapter to have options set
2013-12-30 00:55:42 -08:00
Leszek Prabucki
3bf4e03513 Merge pull request #76 from teohhanhui/master
AwsS3 adapter: added ACL option in configuration
2013-11-12 23:41:39 -08:00
Teoh Han Hui
96061210e4 AwsS3 adapter: added ACL option in configuration 2013-11-13 02:14:41 +08:00
Leszek Prabucki
7c210044bf Merge pull request #73 from krafas/master
Allow AmazonsS3 adapter to have acl option
2013-11-10 01:08:40 -08:00
Leszek Prabucki
9e70925730 Merge pull request #67 from radutopala/master
Dropbox Adapter Factory added
2013-11-04 00:31:06 -08:00
Giedrius Sabaliauskas
b5c3b03285 Allow AmazonsS3 adapter to gave acl option 2013-10-29 18:01:53 +02:00
Gesthispania
8b143c5065 Update FtpAdapterFactory.php 2013-10-10 10:19:27 +02:00
Gesthispania
ef1f8a75ac Add SSL option to work with modified Gaufrette that use SSL/TSL FTP 2013-10-10 09:58:17 +02:00
Leszek Prabucki
c01f131f7c Merge pull request #68 from havvg/master
add example service for Aws\S3\S3Client
2013-10-07 06:36:41 -07:00
Radu Topala
209928f896 Update DropboxAdapterFactory.php 2013-10-06 09:25:51 +03:00
Toni Uebernickel
50ecc39137 add example service for Aws\S3\S3Client 2013-10-04 10:52:28 +02:00
Radu Topala
8875fccb5e Update README.markdown 2013-10-04 10:41:20 +03:00
Radu Topala
a4a06af459 Merge branch 'master' of github.com:radutopala/KnpGaufretteBundle 2013-10-04 06:23:40 +00:00
Radu Topala
ae786b1faa added Dropbox Adapter Factory 2013-10-04 06:17:52 +00:00
Leszek Prabucki
90080cacdb Merge pull request #62 from havvg/feature/aws_s3-adapter
add AwsS3AdapterFactory
2013-09-27 09:23:19 -07:00
Toni Uebernickel
051ab917b9 add AwsS3 docs to README 2013-09-26 20:02:45 +02:00
Toni Uebernickel
169f466849 add AwsS3AdapterFactory 2013-09-26 19:50:50 +02:00
Leszek Prabucki
b4f15993a2 Merge pull request #59 from fadoe/patch-doctrine
Patch doctrine
2013-09-23 23:39:44 -07:00
Falk Doering
2f97c9fbff Update documentation. Add doctrine usage. 2013-09-13 13:37:36 +02:00
Falk Doering
622e52c550 Use doctrine dbal connection name, rename doctrine_dbal_id into connection_name 2013-09-13 13:34:24 +02:00
Falk Doering
336e203655 Update documentation. Change composer registerNamespaces method to addClassMap. 2013-09-13 11:56:38 +02:00
Leszek Prabucki
bd695f9194 Merge pull request #60 from lmammino/azure-blob-storage
Added support for azure blob storage
2013-08-24 03:58:34 -07:00
Luciano Mammino
ee50cd8766 Added support for azure blob storage (introduced here: https://github.com/KnpLabs/Gaufrette/pull/206) 2013-08-24 11:00:36 +02:00
Falk Doering
df1d1855a5 Update inline documentation. 2013-08-12 16:53:05 +02:00
Falk Doering
02483aca83 Add doctrine dbal adapter factory 2013-08-12 16:45:35 +02:00
Nicholas Masters
d450786a7b Allow adapter options to be passed 2013-06-23 18:43:17 +00:00
Leszek Prabucki
6c615bde72 Merge pull request #52 from caponica/master
Cleaned up README and edited for readability
2013-06-17 00:53:15 -07:00
Christian Morgan
da1b5f9750 Added documentation for amazon_s3 adapter
I've added what I understand based on getting this working today. Not sure about all the options. 

I also added a note about bucket names with dots in them - this had me stuck for a while until I found mention of it in passing in an AWS ticket https://forums.aws.amazon.com/thread.jspa?threadID=122890#jive-message-445833

If it's not a generally repeatable problem feel free to delete that line, but it certainly caused me problems!
2013-06-14 22:18:40 +02:00
Christian Morgan
744a0b63c0 Cleaned up README and edited for readability 2013-06-14 14:49:25 +02:00
Leszek Prabucki
de1ef2f9b2 Merge pull request #46 from WriteOrRead/opencloud-adapter-factory
Documentation for configuring HPCloud with OpenStack
2013-04-14 23:12:02 -07:00
Richard Shank
c9c1d0006d Add documentation for using HPCloud with OpenStack 2013-04-13 00:20:13 -07:00
Luciano Mammino
9ffa559db7 Added documentation 2013-04-12 04:07:38 +03:00
Luciano Mammino
2a3cae7156 Added OpenCloudAdapterFactory 2013-04-12 02:32:10 +02:00
Leszek Prabucki
013f50495b Merge pull request #41 from gavD/master
Bugfix: Acl Aware S3 factory could not set options
2013-03-01 05:08:05 -08:00
Gavin Davies
21841d68ee Make sure definition is set in create 2013-03-01 12:43:20 +00:00
Leszek Prabucki
79cad5c65a Merge pull request #38 from l3l0/fix/configuration-for-acl-amazon
Add configuration for AclAmazonS3.
2013-02-22 10:52:47 -08:00
l3l0
216903aa08 Add configuration for AclAmazonS3. Replaces #31 2013-02-22 17:29:35 +01:00
l3l0
c260b10a0b Use 0.2.0@dev at master branch 2013-01-30 12:47:06 +01:00
l3l0
c6da43595c Point to latest stable version of Gaufrette. 2013-01-30 12:41:55 +01:00
Leszek Prabucki
c3b489721b Merge pull request #32 from lmammino/master
Updated Documentation annotations on factories
2013-01-25 00:43:21 -08:00
lmammino
5d1f0ee2f8 Merge branch 'master' of git://github.com/KnpLabs/KnpGaufretteBundle 2013-01-21 11:08:20 +01:00
lmammino
f9614aa3d9 Updated gitignore 2013-01-21 11:07:30 +01:00
lmammino
ccc6a6db15 Fixed documentation annotation for factories 2013-01-21 11:07:19 +01:00
Leszek Prabucki
fe09a0cd03 Merge pull request #28 from lmammino/patch-1
Update FilesystemMap.php
2013-01-03 23:37:20 -08:00
Luciano Mammino
fa0516fb7e Update FilesystemMap.php
Fixed a typo into the documentation
2013-01-04 01:40:30 +01:00
l3l0
2a505d1b30 Fixed definition of ftp adapter. 2012-11-20 10:15:40 +01:00
Leszek Prabucki
4370da0ad4 Merge pull request #26 from l3l0/compatibility-with-new-gaufrette-version
Changes allow to KnpGaufretteBundle work with latest Gaufrette
2012-11-16 00:35:05 -08:00
l3l0
6f5bb2a78a Make amazons3 config to be BC. Added UPGRADE file 2012-11-16 09:30:13 +01:00
l3l0
503e2899c0 Changed version og Gaufrette used by bundle. 2012-11-14 15:40:17 +01:00
l3l0
f27138e80b Replace AmazonS3 create boolean param with array one. Changed
composer.json
2012-11-14 15:36:59 +01:00
41 changed files with 1481 additions and 598 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
Tests/Resources/cache
.idea/
vendor/
composer.lock

View File

@ -1,12 +1,33 @@
language: php
sudo: false
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
before_script:
- curl -s http://getcomposer.org/installer | php -- --quiet
- php composer.phar install --dev
cache:
directories:
- $HOME/.composer/cache
matrix:
include:
- php: 5.6
env: SYMFONY_VERSION='~2.3'
- php: 5.6
env: SYMFONY_VERSION='~2.8'
- php: 5.6
env: SYMFONY_VERSION='~3.0'
before_install:
- composer self-update
- sh -c 'if [ "$SYMFONY_VERSION" != "" ]; then composer require --no-update symfony/symfony=$SYMFONY_VERSION; fi;'
install: composer install
script:
- phpunit --coverage-text
- php bin/phpunit --coverage-text

View File

@ -10,21 +10,22 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
class AclAwareAmazonS3AdapterFactory implements AdapterFactoryInterface
{
/**
* Creates the adapter, registers it and returns its id
*
* @param ContainerBuilder $container A ContainerBuilder instance
* @param string $id The id of the service
* @param array $config An array of configuration
*/
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$container
$definition = $container
->setDefinition($id.'.delegate', new DefinitionDecorator('knp_gaufrette.adapter.amazon_s3'))
->addArgument(new Reference($config['amazon_s3_id']))
->addArgument($config['bucket_name'])
->addArgument($config['create'])
;
if (isset($config['options'])) {
$definition->addArgument($config['options']);
} elseif (isset($config['create'])) {
$definition->addArgument(array('create' => $config['create']));
}
$def = $container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.acl_aware_amazon_s3'))
->addArgument(new Reference($id.'.delegate'))
@ -42,9 +43,7 @@ class AclAwareAmazonS3AdapterFactory implements AdapterFactoryInterface
}
/**
* Returns the key for the factory configuration
*
* @return string
* {@inheritDoc}
*/
public function getKey()
{
@ -52,9 +51,7 @@ class AclAwareAmazonS3AdapterFactory implements AdapterFactoryInterface
}
/**
* Adds configuration nodes for the factory
*
* @param NodeBuilder $builder
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $builder)
{
@ -92,6 +89,15 @@ class AclAwareAmazonS3AdapterFactory implements AdapterFactoryInterface
->end()
->end()
->booleanNode('create')->defaultFalse()->end()
->arrayNode('options')
->children()
->booleanNode('create')
->defaultFalse()
->end()
->scalarNode('region')->end()
->scalarNode('directory')->end()
->end()
->end()
->end()
;
}

View File

@ -31,7 +31,7 @@ interface AdapterFactoryInterface
/**
* Adds configuration nodes for the factory
*
* @param NodeBuilder $builder
* @param NodeDefinition $builder
*/
function addConfiguration(NodeDefinition $builder);
}

View File

@ -10,26 +10,24 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
class AmazonS3AdapterFactory implements AdapterFactoryInterface
{
/**
* Creates the adapter, registers it and returns its id
*
* @param ContainerBuilder $container A ContainerBuilder instance
* @param string $id The id of the service
* @param array $config An array of configuration
*/
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$container
$definition = $container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.amazon_s3'))
->addArgument(new Reference($config['amazon_s3_id']))
->addArgument($config['bucket_name'])
->addArgument($config['create'])
;
->addArgument($config['bucket_name']);
if (isset($config['options'])) {
$definition->addArgument($config['options']);
} elseif (isset($config['create'])) {
$definition->addArgument(array('create' => $config['create']));
}
}
/**
* Returns the key for the factory configuration
*
* @return string
* {@inheritDoc}
*/
public function getKey()
{
@ -37,9 +35,7 @@ class AmazonS3AdapterFactory implements AdapterFactoryInterface
}
/**
* Adds configuration nodes for the factory
*
* @param NodeBuilder $builder
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $builder)
{
@ -47,8 +43,18 @@ class AmazonS3AdapterFactory implements AdapterFactoryInterface
->children()
->scalarNode('amazon_s3_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('bucket_name')->isRequired()->cannotBeEmpty()->end()
->booleanNode('create')->defaultFalse()->end()
->booleanNode('create')->end()
->arrayNode('options')
->children()
->booleanNode('create')
->defaultFalse()
->end()
->scalarNode('region')->end()
->scalarNode('directory')->end()
->scalarNode('acl')->end()
->end()
->end()
->end()
;
}
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
class AwsS3AdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.aws_s3'))
->addArgument(new Reference($config['service_id']))
->addArgument($config['bucket_name'])
->addArgument($config['options'])
->addArgument($config['detect_content_type'])
;
}
/**
* {@inheritDoc}
*/
public function getKey()
{
return 'aws_s3';
}
/**
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('service_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('bucket_name')->isRequired()->cannotBeEmpty()->end()
->booleanNode('detect_content_type')->defaultFalse()->end()
->arrayNode('options')
->addDefaultsIfNotSet()
->children()
->scalarNode('directory')->defaultValue('')->end()
->booleanNode('create')->defaultFalse()->end()
->scalarNode('acl')->defaultValue('private')->end()
->end()
->end()
->end()
;
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
class AzureBlobStorageAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$definition = $container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.azure_blob_storage'))
->addArgument(new Reference($config['blob_proxy_factory_id']))
->addArgument($config['container_name'])
->addArgument($config['create_container'])
->addArgument($config['detect_content_type']);
}
/**
* {@inheritDoc}
*/
public function getKey()
{
return 'azure_blob_storage';
}
/**
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('blob_proxy_factory_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('container_name')->isRequired()->cannotBeEmpty()->end()
->booleanNode('create_container')->defaultValue(false)->end()
->booleanNode('detect_content_type')->defaultValue(true)->end()
->end()
;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
/**
* doctrine dbal adapter factory
*
* @author Falk Doering <falk.doering@marktjagd.de>
*/
class DoctrineDbalAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
function create(ContainerBuilder $container, $id, array $config)
{
$definition = $container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.doctrine_dbal'))
->addArgument(new Reference('doctrine.dbal.' . $config['connection_name'] . '_connection'))
->addArgument($config['table'])
;
if (isset($config['columns'])) {
$definition->addArgument($config['columns']);
}
}
/**
* {@inheritDoc}
*/
function getKey()
{
return 'doctrine_dbal';
}
/**
* {@inheritDoc}
*/
function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('connection_name')->isRequired()->cannotBeEmpty()->end()
->scalarNode('table')->isRequired()->cannotBeEmpty()->end()
->arrayNode('columns')
->children()
->scalarNode('key')->end()
->scalarNode('content')->end()
->scalarNode('mtime')->end()
->scalarNode('checksum')->end()
->end()
->end()
->end()
;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
/**
* Dropbox Adapter Factory
*/
class DropboxAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
function create(ContainerBuilder $container, $id, array $config)
{
$container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.dropbox'))
->addArgument(new Reference($config['api_id']))
;
}
/**
* {@inheritDoc}
*/
function getKey()
{
return 'dropbox';
}
/**
* {@inheritDoc}
*/
function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('api_id')->cannotBeEmpty()->end()
->end()
;
}
}

View File

@ -21,12 +21,7 @@ class FtpAdapterFactory implements AdapterFactoryInterface
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.ftp'))
->addArgument($config['directory'])
->addArgument($config['host'])
->addArgument($config['username'])
->addArgument($config['password'])
->addArgument($config['port'])
->addArgument($config['passive'])
->addArgument($config['create'])
->addArgument($config['mode'])
->addArgument($config)
;
}
@ -52,6 +47,7 @@ class FtpAdapterFactory implements AdapterFactoryInterface
->scalarNode('password')->defaultNull()->end()
->booleanNode('passive')->defaultFalse()->end()
->booleanNode('create')->defaultFalse()->end()
->booleanNode('ssl')->defaultFalse()->end()
->scalarNode('mode')
->defaultValue(defined('FTP_ASCII') ? FTP_ASCII : null)
->beforeNormalization()

View File

@ -0,0 +1,55 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Reference;
class GoogleCloudStorageAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.google_cloud_storage'))
->addArgument(new Reference($config['service_id']))
->addArgument($config['bucket_name'])
->addArgument($config['options'])
->addArgument($config['detect_content_type'])
;
}
/**
* {@inheritDoc}
*/
public function getKey()
{
return 'google_cloud_storage';
}
/**
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('service_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('bucket_name')->isRequired()->cannotBeEmpty()->end()
->booleanNode('detect_content_type')->defaultTrue()->end()
->arrayNode('options')
->addDefaultsIfNotSet()
->children()
->scalarNode('directory')->defaultValue('')->end()
->scalarNode('acl')->defaultValue('private')->end()
->end()
->end()
->end()
;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
/**
* OpenCloud adapter factory
*
* @author Mammino Luciano 2013 <lmammino@oryzone.com>
*/
class OpenCloudAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
public function create(ContainerBuilder $container, $id, array $config)
{
$container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.opencloud'))
->replaceArgument(0, new Reference($config['object_store_id']))
->replaceArgument(1, $config['container_name'])
->replaceArgument(2, $config['create_container'])
->replaceArgument(3, $config['detect_content_type'])
;
}
/**
* {@inheritDoc}
*/
public function getKey()
{
return 'opencloud';
}
/**
* {@inheritDoc}
*/
public function addConfiguration(NodeDefinition $node)
{
$node
->children()
->scalarNode('object_store_id')->isRequired()->cannotBeEmpty()->end()
->scalarNode('container_name')->isRequired()->cannotBeEmpty()->end()
->booleanNode('create_container')->defaultFalse()->end()
->booleanNode('detect_content_type')->defaultTrue()->end()
->end()
;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Knp\Bundle\GaufretteBundle\DependencyInjection\Factory;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
/**
* Phpseclib Sftp Adapter Factory
*/
class PhpseclibSftpAdapterFactory implements AdapterFactoryInterface
{
/**
* {@inheritDoc}
*/
function create(ContainerBuilder $container, $id, array $config)
{
$container
->setDefinition($id, new DefinitionDecorator('knp_gaufrette.adapter.phpseclib_sftp'))
->addArgument(new Reference($config['phpseclib_sftp_id']))
->addArgument($config['directory'])
->addArgument($config['create'])
;
}
/**
* {@inheritDoc}
*/
function getKey()
{
return 'phpseclib_sftp';
}
/**
* {@inheritDoc}
*/
function addConfiguration(NodeDefinition $builder)
{
$builder
->children()
->scalarNode('phpseclib_sftp_id')->isRequired()->end()
->scalarNode('directory')->defaultNull()->end()
->booleanNode('create')->defaultFalse()->end()
->end()
;
}
}

View File

@ -28,21 +28,15 @@ class KnpGaufretteExtension extends Extension
public function load(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
// first assemble the adapter factories
$factoryConfig = new FactoryConfiguration();
$config = $processor->processConfiguration($factoryConfig, $configs);
$factories = $this->createAdapterFactories($config, $container);
// then normalize the configs
$mainConfig = new MainConfiguration($factories);
$config = $processor->processConfiguration($mainConfig, $configs);
$config = $processor->processConfiguration($this->getConfiguration($configs, $container), $configs);
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('gaufrette.xml');
$adapters = array();
foreach ($config['adapters'] as $name => $adapter) {
$adapters[$name] = $this->createAdapter($name, $adapter, $container, $factories);
$adapters[$name] = $this->createAdapter($name, $adapter, $container, $this->factories);
}
$map = array();
@ -59,6 +53,19 @@ class KnpGaufretteExtension extends Extension
}
}
public function getConfiguration(array $configs, ContainerBuilder $container)
{
$processor = new Processor();
// first assemble the adapter factories
$factoryConfig = new FactoryConfiguration();
$config = $processor->processConfiguration($factoryConfig, $configs);
$factories = $this->createAdapterFactories($config, $container);
// then normalize the configs
return new MainConfiguration($factories);
}
private function createAdapter($name, array $config, ContainerBuilder $container, array $factories)
{
$adapter = null;

View File

@ -4,25 +4,25 @@ namespace Knp\Bundle\GaufretteBundle;
/**
* Holds references to all declared filesystems
* and allows to access them through their name
* and allows to access them through their name.
*/
class FilesystemMap implements \IteratorAggregate
{
/**
* Map of filesystems indexed by their name
* Map of filesystems indexed by their name.
*
* @var array
*/
protected $map;
protected $maps;
/**
* Instanciates a new filesystem map
* Instantiates a new filesystem map.
*
* @param array $map
* @param array $maps
*/
public function __construct(array $map)
public function __construct(array $maps)
{
$this->map = $map;
$this->maps = $maps;
}
/**
@ -36,15 +36,25 @@ class FilesystemMap implements \IteratorAggregate
*/
public function get($name)
{
if (!isset($this->map[$name])) {
throw new \InvalidArgumentException(sprintf('No filesystem register for name "%s"', $name));
if (!$this->has($name)) {
throw new \InvalidArgumentException(sprintf('No filesystem is registered for name "%s"', $name));
}
return $this->map[$name];
return $this->maps[$name];
}
/**
* @param string $name name of a filesystem
*
* @return bool
*/
public function has($name)
{
return isset($this->maps[$name]);
}
public function getIterator()
{
return new \ArrayIterator($this->map);
return new \ArrayIterator($this->maps);
}
}

View File

@ -1,526 +0,0 @@
Gaufrette Bundle
================
Provides a [Gaufrette][gaufrette-homepage] integration for your Symfony projects.
About Gaufrette
---------------
Gaufrette is a PHP 5.3+ library providing a filesystem abstraction layer.
This abstraction layer permits you to develop your applications without the need to know where all their medias will be stored and how.
Documentation is available the [official page of Gaufrette][gaufrette-homepage].
Installation
------------
## Prerequisites
As this bundle is an integration for Symfony of the [Gaufrette][gaufrette-homepage] library, it requires you to first install [Gaufrette][gaufrette-homepage] in a Symfony project.
## Download the bundle
You can download an archive of the bundle and unpack it in the `vendor/bundles/Knp/Bundle/GaufretteBundle` directory of your application.
### Standard Edition Style
If you are using the `deps` file to manage your project's dependencies,
you must add the following lines to it:
[gaufrette]
git=http://github.com/KnpLabs/Gaufrette.git
[KnpGaufretteBundle]
git=http://github.com/KnpLabs/KnpGaufretteBundle.git
target=/bundles/Knp/Bundle/GaufretteBundle
### Composer Style
Bundle can be installed using composer by add to require `composer.json` part `"knplabs/knp-gaufrette-bundle": "dev-master"` line.
### Git Submodule Style
If you are versioning your project with git, you had better to embed it
as a submodule:
$ git submodule add https://github.com/KnpLabs/KnpGaufretteBundle.git vendor/bundles/Knp/Bundle/GaufretteBundle
## Add the namespace in the autoloader
You must register both Gaufrette and the KnpGaufretteBundle in your autoloader:
(You do not have to do that if you are using composer autoload system.)
``` php
<?php
// app/autoload.php
$loader->registerNamespaces(array(
'Knp\Bundle' => __DIR__.'/../vendor/bundles',
'Gaufrette' => __DIR__.'/../vendor/gaufrette/src',
// ...
));
```
## Register the bundle
You must register the bundle in your kernel:
``` php
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Knp\Bundle\GaufretteBundle\KnpGaufretteBundle()
);
// ...
}
```
Configuration
-------------
The Gaufrette bundle allows you to declare your filesystems as services without having to reach into the famous "Service Container".
Indeed, you can do it with the configuration!
The configuration of the Gaufrette bundle is divided into two parts: the `adapters` and the `filesystems`.
## Configuring the Adapters
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
local:
directory: /path/to/my/filesystem
```
The defined adapters are usable to create the filesystems.
## Configuring the Filesystems
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
# ...
filesystems:
bar:
adapter: foo
alias: foo_filesystem
```
Each defined filesystem must have an `adapter` with the key of an adapter as value.
The filesystem defined above with result in a service with id `gaufrette.bar_filesystem`.
The `alias` parameter permits to also defines an alias for it.
The filesystem map
------------------
You can access to all declared filesystems through the map service.
In the previous exemple, we declared a `bar` filesystem:
``` php
$container->get('knp_gaufrette.filesystem_map')->get('bar');
```
Returns the instance of `Gaufrette\Filesystem` for `bar`.
Adapters Reference
------------------
## Local Adapter
A simple local filesystem based adapter.
### Parameters
* `directory` The directory of the filesystem *(required)*
* `create` Whether to create the directory if it does not exist *(default true)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
local:
directory: /path/to/my/filesystem
create: true
```
## Safe Local Adapter (safe\_local)
Almost as simple as the **local** adapter, but it encodes key to avoid having to deal with the directories structure.
### Parameters
* `directory` The directory of the filesystem *(required)*
* `create` Whether to create the directory if it does not exist *(default true)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
safe_local:
directory: /path/to/my/filesystem
create: true
```
## Service (service)
Allows you to use a user defined adapter service.
### Parameters
* `id` The id of the service *(required)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
service:
id: my.adapter.service
```
## In Memory (in\_memory)
Adapter for test purposes, it stores files in an internal array.
### Parameters
* `files` An array of files *(optional)*
The `files` is an array of files where each file is a sub-array having the `content`, `checksum` and `mtime` optional keys.
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
in_memory:
files:
'file1.txt': ~
'file2.txt':
content: Some content
checksum: abc1efg2hij3
mtime: 123456890123
```
## GridFS (gridfs)
Adapter that allows you to use a MongoDB GridFS for storing files.
### Parameters
* `mongogridfs_id` The id of the service that provides MongoGridFS object instance for adapter *(required)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
gridfs:
mongogridfs_id: acme_test.gridfs
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.mongo.server: "mongodb://localhost:27017"
acme_test.mongo.options:
connect: true
acme_test.mongodb.name: "test_database"
acme_test.gridfs.prefix: "fs" #Default
services:
acme_test.mongo:
class: Mongo
arguments: [%acme_test.mongo.server%, %acme_test.mongo.options%]
acme_test.mongodb:
class: MongoDB
arguments: [@acme_test.mongo, %acme_test.mongodb.name%]
acme_test.gridfs:
class: MongoGridFS
arguments: [@acme_test.mongodb, %acme_test.gridfs.prefix%]
```
Note that it is possible to prepare MongoGridFS service anyway you like. This is just one way to do it.
## MogileFS (mogilefs)
Adapter that allows you to use MogileFS for storing files.
### Parameters
* `domain` MogileFS domain
* `hosts` Available trackers
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
mogilefs:
domain: foobar
hosts: ["192.168.0.1:7001", "192.168.0.2:7001"]
```
[gaufrette-homepage]: https://github.com/KnpLabs/Gaufrette
## Ftp
Adapter for FTP.
### Parameters
* `directory` The directory of the filesystem *(required)*
* `host` FTP host *(required)*
* `username` FTP username *(default null)*
* `password` FTP password *(default null)*
* `port` FTP port *(default 21)*
* `passive` FTP passive mode *(default false)*
* `create` Whether to create the directory if it does not exist *(default false)*
* `mode` FTP transfer mode *(defaut FTP_ASCII)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
ftp:
host: example.com
username: user
password: pass
directory: /example/ftp
create: true
mode: FTP_BINARY
```
## Sftp
Adapter for SFTP (SSH-FTP).
### Parameters
* `sftp_id` The id of the service that provides SFTP access.
* `directory* The distant directory *(default null)*.
* `create` Whether to create the directory if it does not exist *(default false)*.
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
sftp:
sftp_id: acme_test.sftp
directory: /example/sftp
create: true
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.ssh.host: my_host_name
acme_test.ssh.username: user_name
acme_test.ssh.password: some_secret
services:
acme_test.ssh.configuration:
class: Ssh\Configuration
arguments: [%acme_test.ssh.host%]
acme_test.ssh.authentication:
class: Ssh\Authentication\Password
arguments: [%acme_test.ssh.username%, %acme_test.ssh.password%]
acme_test.ssh.session:
class: Ssh\Session
arguments: [@acme_test.ssh.configuration, @acme_test.ssh.authentication]
acme_test.sftp:
class: Ssh\Sftp
arguments: [@acme_test.ssh.session]
```
## Apc
Adapter for APC.
A non-persistent adapter, use it in the dev environment, in demo sites, ...
### Parameters
* `prefix` The prefix to this filesystem (APC 'namespace', it is recommended that this end in a dot '.') *(required)*
* `ttl` Time to live *(default 0)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
apc:
prefix: APC 'namespace' prefix
ttl: 0
```
## Cache
Adapter which allow to cache other adapters
### Parameters
* `source` The source adapter that must be cached *(required)*
* `cache` The adapter used to cache the source *(required)*
* `ttl` Time to live *(default 0)*
* `serializer` The adapter used to cache serializations *(default null)*
### Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
media_ftp:
ftp:
host: example.com
username: user
password: pass
directory: /example/ftp
create: true
mode: FTP_BINARY
media_apc:
apc:
prefix: APC 'namespace' prefix
ttl: 0
media_cache:
cache:
source: media_ftp
cache: media_apc
ttl: 7200
filesystems:
media:
adapter: media_cache
```
## Stream Wrapper
You can register filesystems with a specified domain.
And use as a stream wrapper anywhere in your code like :
`gaufrette://domain/file.txt`
### Parameters
* `protocol` The protocol name like `gaufrette://…` *(default gaufrette)*
* `filesystem` An array that contains files systems that you want to register with the possibility to set the key of the array
as the domain like `gaufrette://mydomain/…` *(default all filesystems)*
### Example 1
The protocol is gaufrette and all filesystems will be saved
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
backup: #...
amazon: #...
filesystems:
backup1:
adapter: backup
amazonS3:
adapter: amazon
stream_wrapper: ~
```
```
gaufrette://backup1/file
gaufrette://amazonS3/file
```
### Example 2
We define the protocol as data and all filesystem will be saved
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
```
```
data://backup1/...
data://amazonS3/...
```
### Example 3
We define the protocol as data and define which filesystem will be used
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
filesystems:
- backup1
- amazonS3
```
```
data://backup1/...
data://amazonS3/...
```
### Example 4
We define the protocol as data and define which filesystem will be used with the domain aliasing
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
filesystems:
backup: backup1
pictures: amazonS3
```
```
data://backup/...
data://pictures/...
```

127
README.md Normal file
View File

@ -0,0 +1,127 @@
Gaufrette Bundle
================
[![Build Status](https://travis-ci.org/KnpLabs/KnpGaufretteBundle.svg?branch=master)](https://travis-ci.org/KnpLabs/KnpGaufretteBundle)
Provides a [Gaufrette][gaufrette-homepage] integration for your Symfony projects.
About Gaufrette
===============
Gaufrette is a PHP 5.3+ library providing a filesystem abstraction layer.
This abstraction layer allows you to develop applications without needing to know where all their media files will be stored or how.
Documentation is available the [official page of Gaufrette][gaufrette-homepage].
Installation
============
## Prerequisites
As this bundle is an integration for Symfony of the [Gaufrette][gaufrette-homepage] library, it requires you to first install [Gaufrette][gaufrette-homepage] in a Symfony project.
## With composer
This bundle can be installed using [composer](http://getcomposer.org) by adding the following in the `require` section of your `composer.json` file:
``` json
"require": {
...
"knplabs/knp-gaufrette-bundle": "~0.3"
},
```
## Register the bundle
You must register the bundle in your kernel:
``` php
<?php
// app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Knp\Bundle\GaufretteBundle\KnpGaufretteBundle()
);
// ...
}
```
Configuration
=============
The Gaufrette bundle allows you to declare your filesystems as services without having to reach into the famous "Service Container".
Indeed, you can do it with the configuration!
The configuration of the Gaufrette bundle is divided into two parts: the `adapters` and the `filesystems`.
## Configuring the Adapters
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
local:
directory: /path/to/my/filesystem
```
The defined adapters are then used to create the filesystems.
You can use on of these adapters:
* [Local Adapter](Resources/docs/adapters/local.md)
* [Safe Local Adapter](docs/safe_local.md)
* [Service](Resources/docs/adapters/service.md)
* [In Memory](Resources/docs/adapters/memory.md)
* [Azure Blob Storage](Resources/docs/adapters/azure.md)
* [GridFS](Resources/docs/adapters/gridfs.md)
* [MogileFS](Resources/docs/adapters/mogilefs.md)
* [Ftp](Resources/docs/adapters/ftp.md)
* [Sftp](Resources/docs/adapters/sftp.md)
* [Phpseclib Sftp](Resources/docs/adapters/phpseclib_sftp.md)
* [Apc](Resources/docs/adapters/apc.md)
* [Amazon S3](Resources/docs/adapters/amazon_s3.md)
* [AwsS3](Resources/docs/adapters/awss3.md)
* [Open Cloud](Resources/docs/adapters/opencloud.md)
* [GoogleCloudStorage](Resources/docs/adapters/googlecloud.md)
* [Cache](Resources/docs/adapters/cache.md)
* [Stream Wrapper](Resources/docs/adapters/stream.md)
* [Doctrine DBAL](Resources/docs/adapters/doctrine_dbal.md)
* [Dropbox](Resources/docs/adapters/dropbox.md)
## Configuring the Filesystems
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
# ...
filesystems:
bar:
adapter: foo
alias: foo_filesystem
```
Each defined filesystem must have an `adapter` with its value set to an adapter's key.
The filesystem defined above will result in a service with id `gaufrette.bar_filesystem`.
The `alias` parameter allows us to define an alias for it (`foo_filesystem` in this case).
The filesystem map
==================
You can access all declared filesystems through the map service.
In the previous exemple, we declared a `bar` filesystem:
``` php
$container->get('knp_gaufrette.filesystem_map')->get('bar');
```
Returns the `bar` instance of `Gaufrette\Filesystem`.
[gaufrette-homepage]: https://github.com/KnpLabs/Gaufrette

View File

@ -20,9 +20,24 @@
<service id="knp_gaufrette.adapter.factory.amazon_s3" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\AmazonS3AdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.aws_s3" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\AwsS3AdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.acl_aware_amazon_s3" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\AclAwareAmazonS3AdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.doctrine_dbal" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\DoctrineDbalAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.opencloud" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\OpenCloudAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.azure_blob_storage" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\AzureBlobStorageAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.google_cloud_storage" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GoogleCloudStorageAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.gridfs" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\GridFSAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
@ -35,13 +50,18 @@
<service id="knp_gaufrette.adapter.factory.sftp" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\SftpAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.phpseclib_sftp" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\PhpseclibSftpAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.apc" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\ApcAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.cache" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\CacheAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
<service id="knp_gaufrette.adapter.factory.dropbox" class="Knp\Bundle\GaufretteBundle\DependencyInjection\Factory\DropboxAdapterFactory">
<tag name="gaufrette.adapter.factory" />
</service>
</services>
</container>

View File

@ -23,7 +23,17 @@
<argument /><!-- Create -->
</service>
<service id="knp_gaufrette.adapter.amazon_s3" class="Gaufrette\Adapter\AmazonS3" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.aws_s3" class="Gaufrette\Adapter\AwsS3" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.doctrine_dbal" class="Gaufrette\Adapter\DoctrineDbal" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.acl_aware_amazon_s3" class="Gaufrette\Adapter\AclAwareAmazonS3" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.opencloud" class="Gaufrette\Adapter\OpenCloud" abstract="true" public="false">
<argument /><!-- ObjectStore -->
<argument /><!-- Container name -->
<argument /><!-- Create container -->
<argument /><!-- Detect content type -->
</service>
<service id="knp_gaufrette.adapter.azure_blob_storage" class="Gaufrette\Adapter\AzureBlobStorage" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.google_cloud_storage" class="Gaufrette\Adapter\GoogleCloudStorage" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.gridfs" class="Gaufrette\Adapter\GridFS" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.mogilefs" class="Gaufrette\Adapter\MogileFS" abstract="true" public="false">
<argument /><!-- domain -->
@ -31,6 +41,7 @@
</service>
<service id="knp_gaufrette.adapter.ftp" class="Gaufrette\Adapter\Ftp" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.sftp" class="Gaufrette\Adapter\Sftp" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.phpseclib_sftp" class="Gaufrette\Adapter\PhpseclibSftp" abstract="true" public="false" />
<service id="knp_gaufrette.adapter.apc" class="Gaufrette\Adapter\Apc" abstract="true" public="false">
<argument /><!-- prefix -->
<argument /><!-- ttl -->
@ -39,6 +50,7 @@
<service id="knp_gaufrette.filesystem_map" class="%knp_gaufrette.filesystem_map.class%">
<argument /> <!-- map of filesystems -->
</service>
<service id="knp_gaufrette.adapter.dropbox" class="Gaufrette\Adapter\Dropbox" abstract="true" public="false" />
</services>
</container>

View File

@ -0,0 +1,55 @@
# Amazon S3
Adapter to connect to Amazon S3 instances.
This adapter requires the use of amazonwebservices/aws-sdk-for-php which can be installed by adding the following line to your composer.json:
```
"require": {
...
"amazonwebservices/aws-sdk-for-php": "1.6.2"
},
```
## Parameters
* `amazon_s3_id`: the id of the AmazonS3 service used for the underlying connection
* `bucket_name`: the name of the bucket to use
* `options`: additional (optional) settings
* `directory`: the directory to use, within the specified bucket
* `region`
* `create`
## Defining services
To use the Amazon S3 adapter you need to provide a valid `AmazonS3` instance (as defined in the Amazon SDK). This can
easily be set up as using Symfony's service configuration:
``` yaml
# app/config/config.yml
services:
amazonS3:
class: AmazonS3
arguments:
options:
key: '%aws_key%'
secret: '%aws_secret_key%'
```
## Example
Once the service is set up use its key as the amazon_s3_id in the gaufrette configuration:
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
amazon_s3:
amazon_s3_id: amazonS3
bucket_name: foo_bucket
options:
directory: foo_directory
```
Note that the SDK seems to have some issues with bucket names with dots in them, e.g. "com.mycompany.bucket" seems to have issues but "com-mycompany-bucket" works.

View File

@ -0,0 +1,20 @@
# Apc
A non-persistent adapter, use it in the dev environment, in demo sites, ...
## Parameters
* `prefix` The prefix to this filesystem (APC 'namespace', it is recommended that this end in a dot '.') *(required)*
* `ttl` Time to live *(default 0)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
apc:
prefix: APC 'namespace' prefix
ttl: 0
```

View File

@ -0,0 +1,45 @@
# Amazon S3 SDK v2
Adapter for Amazon S3 SDK v2.
## Parameters
* `service_id` The service id of the `Aws\S3\S3Client` to use. *(required)*
* `bucket_name` The name of the S3 bucket to use. *(required)*
* `options` A list of additional options passed to the adapter.
* `create` Whether to create the bucket if it doesn't exist. *(default false)*
* `directory` A directory to operate in. *(default '')*
This directory will be created in the root of the bucket and all files will be read and written there.
## Defining services
An example service definition of the `Aws\S3\S3Client`:
```yaml
services:
acme.aws_s3.client:
class: Aws\S3\S3Client
factory_class: Aws\S3\S3Client
factory_method: 'factory'
arguments:
-
key: %amazon_s3.key%
secret: %amazon_s3.secret%
region: %amazon_s3.region%
```
## Example
Once the service is set up use its key as the `service_id` in the gaufrette configuration:
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
profile_photos:
aws_s3:
service_id: 'acme.aws_s3.client'
bucket_name: 'images'
options:
directory: 'profile_photos'
```

View File

@ -0,0 +1,38 @@
# Azure Blob Storage
Adapter for Microsoft Azure Blob Storage service. To use this adapter you need to install the
[Azure SDK for php](http://www.windowsazure.com/en-us/develop/php/common-tasks/download-php-sdk/) into your project.
Further more you need a valid *connection string* and you must define a Blob Proxy factory service with it. You can use
the default `\Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactory` this way:
``` yaml
# app/config/config.yml
services:
azure_blob_proxy_factory:
class: Gaufrette\Adapter\AzureBlobStorage\BlobProxyFactory
arguments: [%azure_blob_storage_connection_string%]
```
You must set the parameter `azure_blob_storage_connection_string` to contain your windows azure blob storage connection
string. You can retrieve your connection string in your [Windows Azure management console](https://manage.windowsazure.com).
## Parameters
* `blob_proxy_factory_id` Reference to the blob proxy factory service
* `container_name` The name of the container
* `create_container` Boolean value that indicates whether to create the container if it does not exists (*optional*: default *false*)
* `detect_content_type` Boolean value that indicates whether to auto determinate and set the content type on new blobs (*optional*: default *true*)
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
azure_blob_storage:
blob_proxy_factory_id: azure_blob_proxy_factory
container_name: my_container
create_container: true
```

View File

@ -0,0 +1,38 @@
# Cache
Adapter which allows you to cache other adapters
## Parameters
* `source` The source adapter that must be cached *(required)*
* `cache` The adapter used to cache the source *(required)*
* `ttl` Time to live *(default 0)*
* `serializer` The adapter used to cache serializations *(default null)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
media_ftp:
ftp:
host: example.com
username: user
password: pass
directory: /example/ftp
create: true
mode: FTP_BINARY
media_apc:
apc:
prefix: APC 'namespace' prefix
ttl: 0
media_cache:
cache:
source: media_ftp
cache: media_apc
ttl: 7200
filesystems:
media:
adapter: media_cache
```

View File

@ -0,0 +1,29 @@
# Doctrine DBAL
Adapter that allows you to store data into a database.
## Parameters
* `connection_name` The doctrine dbal connection name like `default`
* `table` The table name like `media_data`
* `key`: The primary key in the table
* `content`: The field name of the file content
* `mtime`: The field name of the timestamp
* `checksum`: The field name of the checksum
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
database:
doctrine_dbal:
connection_name: default
table: data
columns:
key: id
content: text
mtime: date
checksum: checksum
```

View File

@ -0,0 +1,41 @@
# Dropbox
Adapter for Dropbox. In order to use it, you should add `dropbox-php/dropbox-php` as your composer dependency.
## Parameters
* `api_id` The id of the service that provides Dropbox API access.
## Example
> In order to get a Dropbox token and token_secret, you need to add a new Dropbox App in your account, and then you'll need to go through the oAuth authorization process
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
dropbox:
api_id: acme_test.dropbox.api
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.dropbox.key: my_consumer_key
acme_test.dropbox.secret: my_consumer_secret
acme_test.dropbox.token: some_token
acme_test.dropbox.token_secret: some_token_secret
services:
acme_test.dropbox.oauth:
class: Dropbox_OAuth_Curl
arguments: [%acme_test.dropbox.key%, %acme_test.dropbox.secret%]
calls:
- [setToken, ["%acme_test.dropbox.token%", "%acme_test.dropbox.token_secret%"]]
acme_test.dropbox.api:
class: Dropbox_API
arguments: [@acme_test.dropbox.oauth, "sandbox"]
```

View File

@ -0,0 +1,30 @@
# FTP
Adapter for FTP.
## Parameters
* `directory` The remote directory *(required)*
* `host` FTP host *(required)*
* `username` FTP username *(default null)*
* `password` FTP password *(default null)*
* `port` FTP port *(default 21)*
* `passive` FTP passive mode *(default false)*
* `create` Whether to create the directory if it does not exist *(default false)*
* `mode` FTP transfer mode *(defaut FTP_ASCII)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
ftp:
host: example.com
username: user
password: pass
directory: /example/ftp
create: true
mode: FTP_BINARY
```

View File

@ -0,0 +1,44 @@
# Google Cloud
Adapter for Google APIs Client Library for PHP.
## Parameters
* `service_id` The service id of the `\Google_Service_Storage` to use. *(required)*
* `bucket_name` The name of the GCS bucket to use. *(required)*
* `detect_content_type`: if `true` will detect the content type for each file *(default `true`)*
* `options` A list of additional options passed to the adapter.
* `directory` A directory to operate in. *(default '')*
* `acl` Whether the uploaded files should be `private` or `public` *(default `private`)*
## Defining services
You need to create a custom factory service which creates a `\Google_Client` and authorizes with the correct scopes
and then returns a `\Google_Service_Storage` class connected to the client class:
```yaml
services:
app.google_cloud_storage.service:
class: \Google_Service_Storage
factory_class: App\Factory\GoogleCloudStorageServiceFactory
factory_method: 'createService'
arguments:
-
# all the arguments needed like service account email and path to key.p12
```
## Example
Once the service is set up use its key as the `service_id` in the gaufrette configuration:
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
profile_photos:
google_cloud_storage:
service_id: 'app.google_cloud_storage.service'
bucket_name: 'images'
options:
directory: 'profile_photos'
```

View File

@ -0,0 +1,42 @@
# MongoDB GridFS
Adapter that allows you to use a MongoDB GridFS for storing files.
## Parameters
* `mongogridfs_id` The id of the service that provides MongoGridFS object instance for adapter *(required)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
gridfs:
mongogridfs_id: acme_test.gridfs
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.mongo.server: "mongodb://localhost:27017"
acme_test.mongo.options:
connect: true
acme_test.mongodb.name: "test_database"
acme_test.gridfs.prefix: "fs" #Default
services:
acme_test.mongo:
class: Mongo
arguments: [%acme_test.mongo.server%, %acme_test.mongo.options%]
acme_test.mongodb:
class: MongoDB
arguments: [@acme_test.mongo, %acme_test.mongodb.name%]
acme_test.gridfs:
class: MongoGridFS
arguments: [@acme_test.mongodb, %acme_test.gridfs.prefix%]
```
Note that it is possible to prepare MongoGridFS service any way you like. This is just one way to do it.

View File

@ -0,0 +1,20 @@
# Local Adapter
A simple local filesystem based adapter.
## Parameters
* `directory` The directory of the filesystem *(required)*
* `create` Whether to create the directory if it does not exist *(default true)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
local:
directory: /path/to/my/filesystem
create: true
```

View File

@ -0,0 +1,25 @@
# In Memory
Adapter for test purposes, it stores files in an internal array.
## Parameters
* `files` An array of files *(optional)*
The `files` is an array of files where each file is a sub-array having the `content`, `checksum` and `mtime` optional keys.
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
in_memory:
files:
'file1.txt': ~
'file2.txt':
content: Some content
checksum: abc1efg2hij3
mtime: 123456890123
```

View File

@ -0,0 +1,20 @@
# MogileFS
Adapter that allows you to use MogileFS for storing files.
## Parameters
* `domain` MogileFS domain
* `hosts` Available trackers
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
mogilefs:
domain: foobar
hosts: ["192.168.0.1:7001", "192.168.0.2:7001"]
```

View File

@ -0,0 +1,101 @@
# OpenCloud
Adapter for OpenCloud (Rackspace)
## Parameters
* `object_store_id`: the id of the object store service
* `container_name`: the name of the container to use
* `create_container`: if `true` will create the container if it doesn't exist *(default `false`)*
* `detect_content_type`: if `true` will detect the content type for each file *(default `true`)*
## Defining services
To use the OpenCloud adapter you should provide a valid `ObjectStore` instance. You can retrieve an instance through the
`OpenCloud\OpenStack` or `OpenCloud\Rackspace` instances. We can provide a comprehensive configuration through the Symfony
DIC configuration.
### Define OpenStack/Rackspace service
Generic OpenStack:
``` yaml
# app/config/config.yml
services:
opencloud.connection:
class: OpenCloud\OpenStack
arguments:
- %openstack_identity_url%
- {username: %openstack_username%, password: %openstack_password%, tenantName: %openstack_tenant_name%}
```
HPCloud:
``` yaml
# app/config/config.yml
services:
opencloud.connection.hpcloud:
class: OpenCloud\OpenStack
arguments:
- 'https://region-a.geo-1.identity.hpcloudsvc.com:123456/v2.0/' // check https://account.hpcloud.com/account/api_keys for identities urls
- {username: %hpcloud_username%, password: %hpcloud_password%, tenantName: %hpcloud_tenant_name%}
```
The username and password are your login credentials, not the api key. Your tenantName is your Project Name on the api keys page.
Rackspace:
``` yaml
# app/config/config.yml
services:
opencloud.connection.rackspace:
class: OpenCloud\Rackspace
arguments:
- 'https://identity.api.rackspacecloud.com/v2.0/'
- {username: %rackspace_username%, apiKey: %rackspace_apikey%}
```
### Define ObjectStore service
HPCloud:
``` yaml
# app/config/config.yml
services:
opencloud.object_store:
class: OpenCloud\ObjectStoreBase
factory_service: opencloud.connection.hpcloud
factory_method: ObjectStore
arguments:
- 'Object Storage' # Object storage type
- 'region-a.geo-1' # Object storage region
- 'publicURL' # url type
```
Rackspace:
``` yaml
# app/config/config.yml
services:
opencloud.object_store:
class: OpenCloud\ObjectStoreBase
factory_service: opencloud.connection
factory_method: objectStoreService
arguments:
- 'cloudFiles' # Object storage type
- 'DFW' # Object storage region
- 'publicURL' # url type
```
## Example
Finally you can define your adapter in configuration:
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
opencloud:
object_store_id: opencloud.object_store
container_name: foo
```

View File

@ -0,0 +1,40 @@
# Phpseclib Sftp
Adapter for phpseclib SFTP (SSH-FTP).
## Parameters
* `phpseclib_sftp_id` The id of the service that provides SFTP access.
* `directory` The remote directory *(default null)*.
* `create` Whether to create the directory if it does not exist *(default false)*.
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
phpseclib_sftp:
phpseclib_sftp_id: acme_test.sftp
directory: /example/sftp
create: true
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.ssh.host: my_host_name
acme_test.ssh.username: user_name
acme_test.ssh.password: some_secret
services:
acme_test.sftp:
class: Net_SFTP
arguments: [%acme_test.ssh.host%]
calls:
- [login, [%acme_test.ssh.username%, %acme_test.ssh.password%]]
```

View File

@ -0,0 +1,20 @@
# Safe Local Adapter
Almost as simple as the **local** adapter, but it encodes key to avoid having to deal with the directories structure.
## Parameters
* `directory` The directory of the filesystem *(required)*
* `create` Whether to create the directory if it does not exist *(default true)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
safe_local:
directory: /path/to/my/filesystem
create: true
```

View File

@ -0,0 +1,18 @@
# Service
Allows you to use a user defined adapter service.
## Parameters
* `id` The id of the service *(required)*
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
service:
id: my.adapter.service
```

View File

@ -0,0 +1,49 @@
# SFTP (SSH-FTP)
Adapter for SFTP (SSH-FTP).
## Parameters
* `sftp_id` The id of the service that provides SFTP access.
* `directory` The remote directory *(default null)*.
* `create` Whether to create the directory if it does not exist *(default false)*.
## Example
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
foo:
sftp:
sftp_id: acme_test.sftp
directory: /example/sftp
create: true
```
In your AcmeTestBundle, add following service definitions:
``` yaml
# src/Acme/TestBundle/Resources/config/services.yml
parameters:
acme_test.ssh.host: my_host_name
acme_test.ssh.username: user_name
acme_test.ssh.password: some_secret
services:
acme_test.ssh.configuration:
class: Ssh\Configuration
arguments: [%acme_test.ssh.host%]
acme_test.ssh.authentication:
class: Ssh\Authentication\Password
arguments: [%acme_test.ssh.username%, %acme_test.ssh.password%]
acme_test.ssh.session:
class: Ssh\Session
arguments: [@acme_test.ssh.configuration, @acme_test.ssh.authentication]
acme_test.sftp:
class: Ssh\Sftp
arguments: [@acme_test.ssh.session]
```

View File

@ -0,0 +1,98 @@
# Stream Wrapper
The `stream_wrapper` settings allow you to register filesystems with a specified domain and
then use as a stream wrapper anywhere in your code like:
`gaufrette://domain/file.txt`
## Parameters
* `protocol` The protocol name like `gaufrette://…` *(default gaufrette)*
* `filesystem` An array that contains filesystems that you want to register to this stream_wrapper.
If you set array keys these will be used as an alias for the filesystem (see examples below) *(default all filesystems without aliases)*
## Example 1
Using default settings, the protocol is "gaufrette" and all filesystems will be served
``` yaml
# app/config/config.yml
knp_gaufrette:
adapters:
backup: #...
amazon: #...
filesystems:
backup1:
adapter: backup
amazonS3:
adapter: amazon
stream_wrapper: ~
```
```
gaufrette://backup1/...
gaufrette://amazonS3/...
```
## Example 2
We define the protocol as "data", all filesystem will still be served (by default)
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
```
```
data://backup1/...
data://amazonS3/...
```
## Example 3
We define the protocol as data and define which filesystem(s) will be available
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
filesystems:
- backup1
```
```
data://backup1/... (works since it is defined above)
data://amazonS3/... (will not be available)
```
## Example 4
We define the protocol as data and define which filesystems will be available using array keys to set domain aliases
``` yaml
# app/config/config.yml
knp_gaufrette:
filesystems:
#...
stream_wrapper:
protocol: data
filesystems:
backup: backup1
pictures: amazonS3
```
```
data://backup/...
data://pictures/...
```

View File

@ -34,6 +34,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowForFilesystemAlias()
{
@ -42,6 +43,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldWorkForOtherEnv()
{
@ -54,6 +56,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowAccessToAllPublicServices()
{
@ -70,6 +73,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowAccessToLocalFilesystem()
{
@ -78,6 +82,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowAccessToCacheFilesystem()
{
@ -86,6 +91,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowAccessToFtpFilesystem()
{
@ -94,6 +100,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowToNotConfigureStreamWrapper()
{
@ -102,6 +109,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldConfigureStreamWrapperWithDefaultValues()
{
@ -127,6 +135,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowToDefineProtocolOfStreamWrapper()
{
@ -140,6 +149,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowToDefineWhichFileSystemsShouldBeAddToStreamWrapper()
{
@ -159,6 +169,7 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
/**
* @test
* @functional
*/
public function shouldAllowToDefineFileSystemsWithoutDomain()
{

36
UPGRADE.md Normal file
View File

@ -0,0 +1,36 @@
UPGRADE FROM 0.2 to 0.3
=======================
No known BC breaks.
UPGRADE FROM 0.1 to 0.2
=======================
### AmazonS3
* In 0.2 we pass additional options for AmazonS3 Gaufrette provider AmazonS3 config was changed (old way is DEPRECATED)
before:
```yml
knp_gaufrette:
adapters:
adaptername:
amazon_s3:
amazon_s3_id: amazon_s3.service.id
bucket_name: mybucketname
create: true
```
after:
```yml
knp_gaufrette:
adapters:
adaptername:
amazon_s3:
amazon_s3_id: amazon_s3.service.id
bucket_name: mybucketname
options:
create: true
```

View File

@ -15,18 +15,32 @@
"homepage": "https://github.com/knplabs/KnpGaufretteBundle/contributors"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/zyphlar/Gaufrette"
}
],
"require": {
"symfony/framework-bundle": "2.*",
"knplabs/gaufrette": "0.1.3"
"symfony/framework-bundle": "~2.0|~3.0",
"knplabs/gaufrette": "dev-sw"
},
"require-dev": {
"symfony/yaml": "2.*",
"symfony/console": "2.*"
"symfony/yaml": "~2.0|~3.0",
"symfony/console": "~2.0|~3.0",
"phpunit/phpunit": "~4.2"
},
"autoload": {
"psr-0": {
"Knp\\Bundle\\GaufretteBundle": ""
"psr-4": {
"Knp\\Bundle\\GaufretteBundle\\": ""
}
},
"target-dir": "Knp/Bundle/GaufretteBundle"
"extra": {
"branch-alias": {
"dev-master": "0.4.x-dev"
}
},
"config": {
"bin-dir": "bin"
}
}