Completed
Pull Request — master (#5408)
by Damian
23:40 queued 12:41
created

Folder::getIcon()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Represents a logical folder, which may be used to organise assets
4
 * stored in the configured backend.
5
 *
6
 * Unlike {@see File} dataobjects, there is not necessarily a physical filesystem entite which
7
 * represents a Folder, and it may be purely logical. However, a physical folder may exist
8
 * if the backend creates one.
9
 *
10
 * Additionally, folders do not have URLs (relative or absolute), nor do they have paths.
11
 *
12
 * When a folder is moved or renamed, records within it will automatically be copied to the updated
13
 * location.
14
 *
15
 * Deleting a folder will remove all child records, but not any physical files.
16
 *
17
 * See {@link File} documentation for more details about the
18
 * relationship between the database and filesystem in the SilverStripe file APIs.
19
 *
20
 * @package framework
21
 * @subpackage filesystem
22
 */
23
class Folder extends File {
24
25
	private static $singular_name = "Folder";
26
27
	private static $plural_name = "Folders";
28
29
	public function exists() {
30
		return $this->isInDB();
31
	}
32
33
	/**
34
	 *
35
	 */
36
	public function populateDefaults() {
37
		parent::populateDefaults();
38
39
		if(!$this->Name) {
40
			$this->Name = _t('AssetAdmin.NEWFOLDER', "NewFolder");
41
		}
42
	}
43
44
	/**
45
	 * Find the given folder or create it as a database record
46
	 *
47
	 * @param string $folderPath Directory path relative to assets root
48
	 * @return Folder|null
49
	 */
50
	public static function find_or_make($folderPath) {
51
		// replace leading and trailing slashes
52
		$folderPath = preg_replace('/^\/?(.*)\/?$/', '$1', trim($folderPath));
53
		$parts = explode("/",$folderPath);
54
55
		$parentID = 0;
56
		$item = null;
57
		$filter = FileNameFilter::create();
58
		foreach($parts as $part) {
59
			if(!$part) {
60
				continue; // happens for paths with a trailing slash
61
			}
62
63
			// Ensure search includes folders with illegal characters removed, but
64
			// err in favour of matching existing folders if $folderPath
65
			// includes illegal characters itself.
66
			$partSafe = $filter->filter($part);
67
			$item = Folder::get()->filter(array(
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
68
				'ParentID' => $parentID,
69
				'Name' => array($partSafe, $part)
70
			))->first();
71
72
			if(!$item) {
73
				$item = new Folder();
74
				$item->ParentID = $parentID;
75
				$item->Name = $partSafe;
76
				$item->Title = $part;
77
				$item->write();
78
			}
79
			$parentID = $item->ID;
80
		}
81
82
		return $item;
83
	}
84
85
	public function onBeforeDelete() {
86
		foreach($this->AllChildren() as $child) {
87
			$child->delete();
88
		}
89
90
		parent::onBeforeDelete();
91
	}
92
93
	/**
94
 	 * Return the relative URL of an icon for this file type
95
 	 *
96
 	 * @return string
97
 	 */
98
 	public function getIcon() {
99
 		return FRAMEWORK_DIR . "/client/dist/images/app_icons/folder_32.png";
100
 	}
101
102
	/**
103
	 * Override setting the Title of Folders to that Name and Title are always in sync.
104
	 * Note that this is not appropriate for files, because someone might want to create a human-readable name
105
	 * of a file that is different from its name on disk. But folders should always match their name on disk.
106
	 *
107
	 * @param string $title
108
	 * @return $this
109
	 */
110
	public function setTitle($title) {
111
		$this->setName($title);
112
		return $this;
113
	}
114
115
	/**
116
	 * Get the folder title
117
	 *
118
	 * @return string
119
	 */
120
	public function getTitle() {
121
		return $this->Name;
122
	}
123
124
	/**
125
	 * Override setting the Title of Folders to that Name and Title are always in sync.
126
	 * Note that this is not appropriate for files, because someone might want to create a human-readable name
127
	 * of a file that is different from its name on disk. But folders should always match their name on disk.
128
	 *
129
	 * @param string $name
130
	 * @return $this
131
	 */
132
	public function setName($name) {
133
		parent::setName($name);
134
		$this->setField('Title', $this->Name);
135
		return $this;
136
	}
137
138
	/**
139
	 * A folder doesn't have a (meaningful) file size.
140
	 *
141
	 * @return null
142
	 */
143
	public function getSize() {
144
		return null;
145
	}
146
147
	/**
148
	 * Returns all children of this folder
149
	 *
150
	 * @return DataList
151
	 */
152
	public function myChildren() {
153
		return File::get()->filter("ParentID", $this->ID);
154
	}
155
156
	/**
157
	 * Returns true if this folder has children
158
	 *
159
	 * @return bool
160
	 */
161
	public function hasChildren() {
162
		return $this->myChildren()->exists();
163
	}
164
165
	/**
166
	 * Returns true if this folder has children
167
	 *
168
	 * @return bool
169
	 */
170
	public function hasChildFolders() {
171
		return $this->ChildFolders()->exists();
172
	}
173
174
	/**
175
	 * Return the FieldList used to edit this folder in the CMS.
176
	 * You can modify this FieldList by subclassing folder, or by creating a {@link DataExtension}
177
	 * and implemeting updateCMSFields(FieldList $fields) on that extension.
178
	 *
179
	 * @return FieldList
180
	 */
181
	public function getCMSFields() {
182
		// Hide field on root level, which can't be renamed
183
		if(!$this->ID || $this->ID === "root") {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $this->ID (integer) and 'root' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
184
			$titleField = new HiddenField("Name");
185
		} else {
186
			$titleField = new TextField("Name", $this->fieldLabel('Name'));
187
		}
188
189
		$fields = new FieldList(
190
			$titleField,
191
			new HiddenField('ParentID')
192
		);
193
		$this->extend('updateCMSFields', $fields);
194
195
		return $fields;
196
	}
197
198
	/**
199
	 * Get the children of this folder that are also folders.
200
	 *
201
	 * @return DataList
202
	 */
203
	public function ChildFolders() {
204
		return Folder::get()->filter('ParentID', $this->ID);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
205
	}
206
207
	/**
208
	 * Get the number of children of this folder that are also folders.
209
	 *
210
	 * @return int
211
	 */
212
	public function numChildFolders() {
213
		return $this->ChildFolders()->count();
214
	}
215
	/**
216
	 * @return string
217
	 */
218
	public function CMSTreeClasses() {
219
		$classes = sprintf('class-%s', $this->class);
220
221
		if(!$this->canDelete()) {
222
			$classes .= " nodelete";
223
		}
224
225
		if(!$this->canEdit()) {
226
			$classes .= " disabled";
227
		}
228
229
		$classes .= $this->markingClasses('numChildFolders');
230
231
		return $classes;
232
	}
233
234
	/**
235
	 * @return string
236
	 */
237
	public function getTreeTitle() {
238
		return sprintf(
239
			"<span class=\"jstree-foldericon\"></span><span class=\"item\">%s</span>",
240
			Convert::raw2att(preg_replace('~\R~u', ' ', $this->Title))
241
		);
242
	}
243
244
	public function getFilename() {
245
		return parent::generateFilename() . '/';
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (generateFilename() instead of getFilename()). Are you sure this is correct? If so, you might want to change this to $this->generateFilename().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
246
	}
247
248
	/**
249
	 * Folders do not have public URLs
250
	 *
251
	 * @param bool $grant
252
	 * @return null|string
253
	 */
254
	public function getURL($grant = true) {
255
		return null;
256
	}
257
258
	/**
259
	 * Folders do not have public URLs
260
	 *
261
	 * @return string
262
	 */
263
	public function getAbsoluteURL() {
264
		return null;
265
	}
266
267
	public function onAfterWrite() {
268
		parent::onAfterWrite();
269
270
		// No publishing UX for folders, so just cascade changes live
271
		if(Versioned::get_stage() === Versioned::DRAFT) {
272
			$this->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
273
		}
274
275
		// Update draft version of all child records
276
		$this->updateChildFilesystem();
277
	}
278
279
	public function onAfterDelete() {
280
		parent::onAfterDelete();
281
282
		// Cascade deletions to live
283
		if(Versioned::get_stage() === Versioned::DRAFT) {
284
			$this->deleteFromStage(Versioned::LIVE);
285
		}
286
	}
287
288
	public function updateFilesystem() {
289
		// No filesystem changes to update
290
	}
291
292
	/**
293
	 * If a write is skipped due to no changes, ensure that nested records still get asked to update
294
	 */
295
	public function onAfterSkippedWrite() {
296
		$this->updateChildFilesystem();
297
	}
298
299
	/**
300
	 * Update filesystem of all children
301
	 */
302
	public function updateChildFilesystem() {
303
		// Don't synchronise on live (rely on publishing instead)
304
		if(Versioned::get_stage() === Versioned::LIVE) {
305
			return;
306
		}
307
308
		$this->flushCache();
309
		// Writing this record should trigger a write (and potential updateFilesystem) on each child
310
		foreach ($this->AllChildren() as $child) {
311
			$child->write();
312
		}
313
	}
314
315
	public function StripThumbnail() {
316
		return null;
317
	}
318
}
319