Skip to content

Commit eab6e5e

Browse files
committed
Fixes setting metadata when loading files to Amazon S3 (PR 266)
1 parent 7a9211e commit eab6e5e

28 files changed

+782
-48
lines changed

DependencyInjection/Configuration.php

+27
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,33 @@ private function addFilesystemSection(ArrayNodeDefinition $node)
165165
->scalarNode('accessKey')->isRequired()->end()
166166
->scalarNode('secretKey')->isRequired()->end()
167167
->scalarNode('create')->defaultValue(false)->end()
168+
->scalarNode('storage')
169+
->defaultValue('standard')
170+
->validate()
171+
->ifNotInArray(array('standard', 'reduced'))
172+
->thenInvalid('Invalid storage type - "%s"')
173+
->end()
174+
->end()
175+
->scalarNode('cache_control')->defaultValue('')->end()
176+
->scalarNode('acl')
177+
->defaultValue('public')
178+
->validate()
179+
->ifNotInArray(array('private', 'public', 'open', 'auth_read', 'owner_read', 'owner_full_control'))
180+
->thenInvalid('Invalid acl permission - "%s"')
181+
->end()
182+
->end()
183+
->scalarNode('encryption')
184+
->defaultValue('')
185+
->validate()
186+
->ifNotInArray(array('aes256'))
187+
->thenInvalid('Invalid encryption type - "%s"')
188+
->end()
189+
->end()
190+
->arrayNode('meta')
191+
->useAttributeAsKey('name')
192+
->prototype('scalar')
193+
->end()
194+
->end()
168195
->end()
169196
->end()
170197

DependencyInjection/SonataMediaExtension.php

+10
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,16 @@ public function configureFilesystemAdapter(ContainerBuilder $container, array $c
318318
->addMethodCall('setDirectory', array($config['filesystem']['s3']['directory']));
319319
;
320320

321+
$container->getDefinition('sonata.media.metadata.amazon')
322+
->addArgument(array(
323+
'acl' => $config['filesystem']['s3']['acl'],
324+
'storage' => $config['filesystem']['s3']['storage'],
325+
'encryption' => $config['filesystem']['s3']['encryption'],
326+
'meta' => $config['filesystem']['s3']['meta'],
327+
'cache_control' => $config['filesystem']['s3']['cache_control']
328+
))
329+
;
330+
321331
$container->getDefinition('sonata.media.adapter.service.s3')
322332
->replaceArgument(0, array(
323333
'secret' => $config['filesystem']['s3']['secretKey'],

Filesystem/Replicate.php

+53-5
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
namespace Sonata\MediaBundle\Filesystem;
1212

1313
use Gaufrette\Adapter as AdapterInterface;
14+
use Gaufrette\Adapter\MetadataSupporter;
1415
use Gaufrette\Filesystem;
1516

16-
class Replicate implements AdapterInterface
17+
class Replicate implements AdapterInterface, MetadataSupporter
1718
{
1819
protected $master;
1920

@@ -38,8 +39,7 @@ public function __construct(AdapterInterface $master, AdapterInterface $slave)
3839
*/
3940
public function delete($key)
4041
{
41-
$this->slave->delete($key);
42-
$this->master->delete($key);
42+
return $this->slave->delete($key) && $this->master->delete($key);
4343
}
4444

4545
/**
@@ -120,13 +120,61 @@ public function rename($key, $new)
120120
}
121121

122122
/**
123-
* If the adapter can allow inserting metadata
123+
* If one of the adapters can allow inserting metadata
124124
*
125125
* @return bool true if supports metadata, false if not
126126
*/
127127
public function supportsMetadata()
128128
{
129-
return $this->master->supportsMetadata() && $this->slave->supportsMetadata();
129+
return $this->master instanceof MetadataSupporter || $this->slave instanceof MetadataSupporter;
130+
}
131+
132+
/**
133+
* Sets metadata for adapters if they allow it
134+
*
135+
* @param string $key
136+
* @param array $metadata
137+
*
138+
*/
139+
public function setMetadata($key, $metadata)
140+
{
141+
if ($this->master instanceof MetadataSupporter) {
142+
$this->master->setMetadata($key, $metadata);
143+
}
144+
if ($this->slave instanceof MetadataSupporter) {
145+
$this->slave->setMetadata($key, $metadata);
146+
}
147+
}
148+
149+
/**
150+
* Gets metadata for master or slave adapter if they allow it
151+
*
152+
* @param string $key
153+
*
154+
*/
155+
public function getMetadata($key)
156+
{
157+
if ($this->master instanceof MetadataSupporter) {
158+
return $this->master->getMetadata($key);
159+
} elseif ($this->slave instanceof MetadataSupporter) {
160+
return $this->slave->getMetadata($key);
161+
}
162+
163+
return array();
164+
}
165+
166+
/**
167+
* Gets the class names as an array for both adapters
168+
*
169+
* @return array
170+
*
171+
*/
172+
public function getAdapterClassNames()
173+
{
174+
return array(
175+
get_class($this->master),
176+
get_class($this->slave),
177+
);
130178
}
131179

132180
/**

Metadata/AmazonMetadataBuilder.php

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\MediaBundle\Metadata;
13+
14+
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
15+
use Sonata\MediaBundle\Model\MediaInterface;
16+
use \AmazonS3 as AmazonS3;
17+
use \CFMimeTypes;
18+
19+
class AmazonMetadataBuilder implements MetadataBuilderInterface
20+
{
21+
22+
protected $settings;
23+
24+
protected $acl = array(
25+
'private' => AmazonS3::ACL_PRIVATE,
26+
'public' => AmazonS3::ACL_PUBLIC,
27+
'open' => AmazonS3::ACL_OPEN,
28+
'auth_read' => AmazonS3::ACL_AUTH_READ,
29+
'owner_read' => AmazonS3::ACL_OWNER_READ,
30+
'owner_full_control' => AmazonS3::ACL_OWNER_FULL_CONTROL,
31+
);
32+
33+
/**
34+
* @param array $settings
35+
*/
36+
public function __construct(array $settings)
37+
{
38+
$this->settings = $settings;
39+
}
40+
41+
/**
42+
* Get data passed from the config
43+
*
44+
* @return array
45+
*/
46+
protected function getDefaultMetadata()
47+
{
48+
//merge acl
49+
$output = array();
50+
if (isset($this->settings['acl']) && !empty($this->settings['acl'])) {
51+
$output['acl'] = $this->acl[$this->settings['acl']];
52+
}
53+
54+
//merge storage
55+
if (isset($this->settings['storage'])) {
56+
if ($this->settings['storage'] == 'standard') {
57+
$output['storage'] = AmazonS3::STORAGE_STANDARD;
58+
} elseif ($this->settings['storage'] == 'reduced') {
59+
$output['storage'] = AmazonS3::STORAGE_REDUCED;
60+
}
61+
}
62+
63+
//merge meta
64+
if (isset($this->settings['meta']) && !empty($this->settings['meta'])) {
65+
$output['meta'] = $this->settings['meta'];
66+
}
67+
68+
//merge cache control header
69+
if (isset($this->settings['cache_control']) && !empty($this->settings['cache_control'])) {
70+
$output['headers']['Cache-Control'] = $this->settings['cache_control'];
71+
}
72+
73+
//merge encryption
74+
if (isset($this->settings['encryption']) && !empty($this->settings['encryption'])) {
75+
if ($this->settings['encryption'] == 'aes256') {
76+
$output['encryption'] = 'AES256';
77+
}
78+
}
79+
80+
return $output;
81+
}
82+
83+
/**
84+
* Gets the correct content-type
85+
*
86+
* @param string $filename
87+
*
88+
* @return array
89+
*/
90+
protected function getContentType($filename)
91+
{
92+
$extension = pathinfo($filename, PATHINFO_EXTENSION);
93+
$contentType = CFMimeTypes::get_mimetype($extension);
94+
95+
return array('contentType'=> $contentType);
96+
}
97+
98+
/**
99+
* {@inheritdoc}
100+
*/
101+
public function get(MediaInterface $media, $filename)
102+
{
103+
return array_replace_recursive(
104+
$this->getDefaultMetadata(),
105+
$this->getContentType($filename)
106+
);
107+
}
108+
}

Metadata/MetadataBuilderInterface.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\MediaBundle\Metadata;
13+
14+
use Sonata\MediaBundle\Model\MediaInterface;
15+
16+
interface MetadataBuilderInterface
17+
{
18+
19+
/**
20+
* Get metadata for media object
21+
*
22+
* @param MediaInterface $media
23+
* @param string $filename
24+
*
25+
* @return array
26+
*/
27+
public function get(MediaInterface $media, $filename);
28+
}

Metadata/NoopMetadataBuilder.php

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\MediaBundle\Metadata;
13+
14+
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
15+
use Sonata\MediaBundle\Model\MediaInterface;
16+
17+
class NoopMetadataBuilder implements MetadataBuilderInterface
18+
{
19+
/**
20+
* {@inheritdoc}
21+
*/
22+
public function get(MediaInterface $media, $filename)
23+
{
24+
return array();
25+
}
26+
}

Metadata/ProxyMetadataBuilder.php

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Sonata project.
5+
*
6+
* (c) Thomas Rabaix <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Sonata\MediaBundle\Metadata;
13+
14+
use Sonata\MediaBundle\Metadata\MetadataBuilderInterface;
15+
use Sonata\MediaBundle\Model\MediaInterface;
16+
use Sonata\MediaBundle\Filesystem\Replicate;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
18+
19+
class ProxyMetadataBuilder implements MetadataBuilderInterface
20+
{
21+
private $container;
22+
private $map;
23+
private $metadata;
24+
25+
/**
26+
* @param ContainerInterface $metadata
27+
* @param array $map
28+
*/
29+
public function __construct(ContainerInterface $container, array $map)
30+
{
31+
$this->container = $container;
32+
$this->map = $map;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function get(MediaInterface $media, $filename)
39+
{
40+
//get adapter for current media
41+
if (!$this->container->has($media->getProviderName())) {
42+
return array();
43+
}
44+
45+
if ($meta = $this->getAmazonBuilder($media, $filename)) {
46+
return $meta;
47+
}
48+
49+
if (!$this->container->has('sonata.media.metadata.noop')) {
50+
return array();
51+
}
52+
53+
return $this->container->get('sonata.media.metadata.noop')->get($media, $filename);
54+
}
55+
56+
/**
57+
* @param MediaInterface $metadata
58+
* @param string $filename
59+
*
60+
* @return array
61+
*/
62+
protected function getAmazonBuilder(MediaInterface $media, $filename)
63+
{
64+
$adapter = $this->container->get($media->getProviderName())->getFilesystem()->getAdapter();
65+
66+
//handle special Replicate adapter
67+
if ($adapter instanceof Replicate) {
68+
$adapterClassNames = $adapter->getAdapterClassNames();
69+
} else {
70+
$adapterClassNames = array(get_class($adapter));
71+
}
72+
73+
//for amazon s3
74+
if (!in_array('Gaufrette\Adapter\AmazonS3', $adapterClassNames) || !$this->container->has('sonata.media.metadata.amazon')) {
75+
return false;
76+
}
77+
78+
return $this->container->get('sonata.media.metadata.amazon')->get($media, $filename);;
79+
}
80+
}

0 commit comments

Comments
 (0)