Completed
Push — die-dist-conflicts-die ( ed850a...c24201 )
by Ingo
10:57
created

DBFile::PreviewLink()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Assets\Storage;
4
5
use SilverStripe\Assets\File;
6
use SilverStripe\Assets\Thumbnail;
7
use SilverStripe\Assets\ImageManipulation;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Control\Director;
10
use SilverStripe\Forms\AssetField;
11
use SilverStripe\ORM\ValidationResult;
12
use SilverStripe\ORM\ValidationException;
13
use SilverStripe\ORM\FieldType\DBComposite;
14
use SilverStripe\Security\Permission;
15
use SilverStripe\Core\Convert;
16
17
/**
18
 * Represents a file reference stored in a database
19
 *
20
 * @property string $Hash SHA of the file
21
 * @property string $Filename Name of the file, including directory
22
 * @property string $Variant Variant of the file
23
 */
24
class DBFile extends DBComposite implements AssetContainer, Thumbnail {
25
26
	use ImageManipulation;
27
28
	/**
29
	 * List of allowed file categories.
30
	 *
31
	 * {@see File::$app_categories}
32
	 *
33
	 * @var array
34
	 */
35
	protected $allowedCategories = array();
36
37
	/**
38
	 * List of image mime types supported by the image manipulations API
39
	 *
40
	 * {@see File::app_categories} for matching extensions.
41
	 *
42
	 * @config
43
	 * @var array
44
	 */
45
	private static $supported_images = array(
46
		'image/jpeg',
47
		'image/gif',
48
		'image/png'
49
	);
50
51
	/**
52
	 * Create a new image manipulation
53
	 *
54
	 * @param string $name
55
	 * @param array|string $allowed List of allowed file categories (not extensions), as per File::$app_categories
56
	 */
57
	public function __construct($name = null, $allowed = array()) {
58
		parent::__construct($name);
59
		$this->setAllowedCategories($allowed);
60
	}
61
62
	/**
63
	 * Determine if a valid non-empty image exists behind this asset, which is a format
64
	 * compatible with image manipulations
65
	 *
66
	 * @return boolean
67
	 */
68
	public function getIsImage() {
69
		// Check file type
70
		$mime = $this->getMimeType();
71
		return $mime && in_array($mime, $this->config()->supported_images);
0 ignored issues
show
Bug Best Practice introduced by
The expression $mime of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
72
	}
73
74
	/**
75
	 * @return AssetStore
76
	 */
77
	protected function getStore() {
78
		return Injector::inst()->get('AssetStore');
79
	}
80
81
	private static $composite_db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
82
		"Hash" => "Varchar(255)", // SHA of the base content
83
		"Filename" => "Varchar(255)", // Path identifier of the base content
84
		"Variant" => "Varchar(255)", // Identifier of the variant to the base, if given
85
	);
86
87
	private static $casting = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
88
		'URL' => 'Varchar',
89
		'AbsoluteURL' => 'Varchar',
90
		'Basename' => 'Varchar',
91
		'Title' => 'Varchar',
92
		'MimeType' => 'Varchar',
93
		'String' => 'Text',
94
		'Tag' => 'HTMLFragment',
95
		'Size' => 'Varchar'
96
	);
97
98
	public function scaffoldFormField($title = null, $params = null) {
99
		return AssetField::create($this->getName(), $title);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \SilverStripe\For...is->getName(), $title); (SilverStripe\Forms\AssetField) is incompatible with the return type of the parent method SilverStripe\ORM\FieldTy...ield::scaffoldFormField of type SilverStripe\Forms\TextField.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
100
	}
101
102
	/**
103
	 * Return a html5 tag of the appropriate for this file (normally img or a)
104
	 *
105
	 * @return string
106
	 */
107
	public function XML() {
108
		return $this->getTag() ?: '';
109
	}
110
111
	/**
112
	 * Return a html5 tag of the appropriate for this file (normally img or a)
113
	 *
114
	 * @return string
115
	 */
116
	public function getTag() {
117
		$template = $this->getFrontendTemplate();
118
		if(empty($template)) {
119
			return '';
120
		}
121
		return (string)$this->renderWith($template);
122
	}
123
124
	/**
125
	 * Determine the template to render as on the frontend
126
	 *
127
	 * @return string Name of template
128
	 */
129
	public function getFrontendTemplate() {
130
		// Check that path is available
131
		$url = $this->getURL();
132
		if(empty($url)) {
133
			return null;
134
		}
135
136
		// Image template for supported images
137
		if($this->getIsImage()) {
138
			return 'DBFile_image';
139
		}
140
141
		// Default download
142
		return 'DBFile_download';
143
	}
144
145
	/**
146
	 * Get trailing part of filename
147
	 *
148
	 * @return string
149
	 */
150
	public function getBasename() {
151
		if(!$this->exists()) {
152
			return null;
153
		}
154
		return basename($this->getSourceURL());
155
	}
156
157
	/**
158
	 * Get file extension
159
	 *
160
	 * @return string
161
	 */
162
	public function getExtension() {
163
		if(!$this->exists()) {
164
			return null;
165
		}
166
		return pathinfo($this->Filename, PATHINFO_EXTENSION);
167
	}
168
169
	/**
170
	 * Alt title for this
171
	 *
172
	 * @return string
173
	 */
174
	public function getTitle() {
175
		// If customised, use the customised title
176
		if($this->failover && ($title = $this->failover->Title)) {
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<SilverStripe\View\ViewableData>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
177
			return $title;
178
		}
179
		// fallback to using base name
180
		return $this->getBasename();
181
	}
182
183 View Code Duplication
	public function setFromLocalFile($path, $filename = null, $hash = null, $variant = null, $config = array()) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
		$this->assertFilenameValid($filename ?: $path);
185
		$result = $this
186
			->getStore()
187
			->setFromLocalFile($path, $filename, $hash, $variant, $config);
188
		// Update from result
189
		if($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
190
			$this->setValue($result);
191
		}
192
		return $result;
193
	}
194
195 View Code Duplication
	public function setFromStream($stream, $filename, $hash = null, $variant = null, $config = array()) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
		$this->assertFilenameValid($filename);
197
		$result = $this
198
			->getStore()
199
			->setFromStream($stream, $filename, $hash, $variant, $config);
200
		// Update from result
201
		if($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
202
			$this->setValue($result);
203
		}
204
		return $result;
205
	}
206
207 View Code Duplication
	public function setFromString($data, $filename, $hash = null, $variant = null, $config = array()) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208
		$this->assertFilenameValid($filename);
209
		$result = $this
210
			->getStore()
211
			->setFromString($data, $filename, $hash, $variant, $config);
212
		// Update from result
213
		if($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
214
			$this->setValue($result);
215
		}
216
		return $result;
217
	}
218
219 View Code Duplication
	public function getStream() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
		if(!$this->exists()) {
221
			return null;
222
		}
223
		return $this
224
			->getStore()
225
			->getAsStream($this->Filename, $this->Hash, $this->Variant);
226
	}
227
228 View Code Duplication
	public function getString() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
229
		if(!$this->exists()) {
230
			return null;
231
		}
232
		return $this
233
			->getStore()
234
			->getAsString($this->Filename, $this->Hash, $this->Variant);
235
	}
236
237
	public function getURL($grant = true) {
238
		if(!$this->exists()) {
239
			return null;
240
		}
241
		$url = $this->getSourceURL($grant);
242
		$this->updateURL($url);
243
		$this->extend('updateURL', $url);
244
		return $url;
245
	}
246
247
	/**
248
	 * Get URL, but without resampling.
249
	 * Note that this will return the url even if the file does not exist.
250
	 *
251
	 * @param bool $grant Ensures that the url for any protected assets is granted for the current user.
252
	 * @return string
253
	 */
254
	public function getSourceURL($grant = true) {
255
		return $this
256
			->getStore()
257
			->getAsURL($this->Filename, $this->Hash, $this->Variant, $grant);
258
	}
259
260
	/**
261
	 * Get the absolute URL to this resource
262
	 *
263
	 * @return string
264
	 */
265
	public function getAbsoluteURL() {
266
		if(!$this->exists()) {
267
			return null;
268
		}
269
		return Director::absoluteURL($this->getURL());
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \SilverStripe\Control\Di...teURL($this->getURL()); of type string|false adds false to the return on line 269 which is incompatible with the return type declared by the interface SilverStripe\Assets\Stor...ntainer::getAbsoluteURL of type string. It seems like you forgot to handle an error condition.
Loading history...
270
	}
271
272 View Code Duplication
	public function getMetaData() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
273
		if(!$this->exists()) {
274
			return null;
275
		}
276
		return $this
277
			->getStore()
278
			->getMetadata($this->Filename, $this->Hash, $this->Variant);
279
	}
280
281 View Code Duplication
	public function getMimeType() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
282
		if(!$this->exists()) {
283
			return null;
284
		}
285
		return $this
286
			->getStore()
287
			->getMimeType($this->Filename, $this->Hash, $this->Variant);
288
	}
289
290
	public function getValue() {
291
		if(!$this->exists()) {
292
			return null;
293
		}
294
		return array(
295
			'Filename' => $this->Filename,
296
			'Hash' => $this->Hash,
297
			'Variant' => $this->Variant
298
		);
299
	}
300
301
	public function getVisibility() {
302
		if(empty($this->Filename)) {
303
			return null;
304
		}
305
		return $this
306
			->getStore()
307
			->getVisibility($this->Filename, $this->Hash);
308
	}
309
310 View Code Duplication
	public function exists() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
		if(empty($this->Filename)) {
312
			return false;
313
		}
314
		return $this
315
			->getStore()
316
			->exists($this->Filename, $this->Hash, $this->Variant);
317
	}
318
319
	public function getFilename() {
320
		return $this->getField('Filename');
321
	}
322
323
	public function getHash() {
324
		return $this->getField('Hash');
325
	}
326
327
	public function getVariant() {
328
		return $this->getField('Variant');
329
	}
330
331
	/**
332
	 * Return file size in bytes.
333
	 *
334
	 * @return int
335
	 */
336
	public function getAbsoluteSize() {
337
		$metadata = $this->getMetaData();
338
		if(isset($metadata['size'])) {
339
			return $metadata['size'];
340
		}
341
		return 0;
342
	}
343
344
	/**
345
	 * Customise this object with an "original" record for getting other customised fields
346
	 *
347
	 * @param AssetContainer $original
348
	 * @return $this
349
	 */
350
	public function setOriginal($original) {
351
		$this->failover = $original;
0 ignored issues
show
Documentation Bug introduced by
It seems like $original of type object<SilverStripe\Asse...Storage\AssetContainer> is incompatible with the declared type object<SilverStripe\View\ViewableData> of property $failover.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
352
		return $this;
353
	}
354
355
	/**
356
	 * Get list of allowed file categories
357
	 *
358
	 * @return array
359
	 */
360
	public function getAllowedCategories() {
361
		return $this->allowedCategories;
362
	}
363
364
	/**
365
	 * Assign allowed categories
366
	 *
367
	 * @param array|string $categories
368
	 * @return $this
369
	 */
370
	public function setAllowedCategories($categories) {
371
		if(is_string($categories)) {
372
			$categories = preg_split('/\s*,\s*/', $categories);
373
		}
374
		$this->allowedCategories = (array)$categories;
375
		return $this;
376
	}
377
378
	/**
379
	 * Gets the list of extensions (if limited) for this field. Empty list
380
	 * means there is no restriction on allowed types.
381
	 *
382
	 * @return array
383
	 */
384
	protected function getAllowedExtensions() {
385
		$categories = $this->getAllowedCategories();
386
		return File::get_category_extensions($categories);
387
	}
388
389
	/**
390
	 * Validate that this DBFile accepts this filename as valid
391
	 *
392
	 * @param string $filename
393
	 * @throws ValidationException
394
	 * @return bool
395
	 */
396
	protected function isValidFilename($filename) {
397
		$extension = strtolower(File::get_file_extension($filename));
398
399
		// Validate true if within the list of allowed extensions
400
		$allowed = $this->getAllowedExtensions();
401
		if($allowed) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $allowed of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
402
			return in_array($extension, $allowed);
403
		}
404
405
		// If no extensions are configured, fallback to global list
406
		$globalList = File::config()->allowed_extensions;
0 ignored issues
show
Documentation introduced by
The property allowed_extensions does not exist on object<SilverStripe\Core\Config\Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
407
		if(in_array($extension, $globalList)) {
408
			return true;
409
		}
410
411
		// Only admins can bypass global rules
412
		return !File::config()->apply_restrictions_to_admin && Permission::check('ADMIN');
413
	}
414
415
	/**
416
	 * Check filename, and raise a ValidationException if invalid
417
	 *
418
	 * @param string $filename
419
	 * @throws ValidationException
420
	 */
421
	protected function assertFilenameValid($filename) {
422
		$result = new ValidationResult();
423
		$this->validate($result, $filename);
424
		if(!$result->valid()) {
425
			throw new ValidationException($result);
426
		}
427
	}
428
429
430
	/**
431
	 * Hook to validate this record against a validation result
432
	 *
433
	 * @param ValidationResult $result
434
	 * @param string $filename Optional filename to validate. If omitted, the current value is validated.
435
	 * @return bool Valid flag
436
	 */
437
	public function validate(ValidationResult $result, $filename = null) {
438
		if(empty($filename)) {
439
			$filename = $this->getFilename();
440
		}
441
		if(empty($filename) || $this->isValidFilename($filename)) {
442
			return true;
443
		}
444
445
		// Check allowed extensions
446
		$extensions = $this->getAllowedExtensions();
447
		if(empty($extensions)) {
448
			$extensions = File::config()->allowed_extensions;
0 ignored issues
show
Documentation introduced by
The property allowed_extensions does not exist on object<SilverStripe\Core\Config\Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
449
		}
450
		sort($extensions);
451
		$message = _t(
452
			'File.INVALIDEXTENSION',
453
			'Extension is not allowed (valid: {extensions})',
454
			'Argument 1: Comma-separated list of valid extensions',
455
			array('extensions' => wordwrap(implode(', ',$extensions)))
456
		);
457
		$result->error($message);
458
		return false;
459
	}
460
461
	public function setField($field, $value, $markChanged = true) {
462
		// Catch filename validation on direct assignment
463
		if($field === 'Filename' && $value) {
464
			$this->assertFilenameValid($value);
465
		}
466
467
		return parent::setField($field, $value, $markChanged);
468
	}
469
470
471
	/**
472
	 * Returns the size of the file type in an appropriate format.
473
	 *
474
	 * @return string|false String value, or false if doesn't exist
475
	 */
476
	public function getSize() {
477
		$size = $this->getAbsoluteSize();
478
		if($size) {
479
			return File::format_size($size);
480
		}
481
		return false;
482
	}
483
484
	public function deleteFile() {
485
		if(!$this->Filename) {
486
			return false;
487
		}
488
489
		return $this
490
			->getStore()
491
			->delete($this->Filename, $this->Hash);
492
	}
493
494
	public function publishFile() {
495
		if($this->Filename) {
496
			$this
497
				->getStore()
498
				->publish($this->Filename, $this->Hash);
499
		}
500
	}
501
502
	public function protectFile() {
503
		if($this->Filename) {
504
			$this
505
				->getStore()
506
				->protect($this->Filename, $this->Hash);
507
		}
508
	}
509
510
	public function grantFile() {
511
		if($this->Filename) {
512
			$this
513
				->getStore()
514
				->grant($this->Filename, $this->Hash);
515
		}
516
	}
517
518
	public function revokeFile() {
519
		if($this->Filename) {
520
			$this
521
				->getStore()
522
				->revoke($this->Filename, $this->Hash);
523
		}
524
	}
525
526
	public function canViewFile() {
527
		return $this->Filename
528
			&& $this
529
				->getStore()
530
				->canView($this->Filename, $this->Hash);
531
	}
532
}
533