Completed
Push — master ( 1049b7...e6ae53 )
by Damian
09:20
created

Folder::setName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Assets;
4
5
use SilverStripe\Core\Convert;
6
use SilverStripe\Forms\FieldList;
7
use SilverStripe\Forms\HeaderField;
8
use SilverStripe\Forms\HiddenField;
9
use SilverStripe\Forms\LiteralField;
10
use SilverStripe\Forms\TextField;
11
use SilverStripe\ORM\DataList;
12
use SilverStripe\ORM\ValidationResult;
13
use SilverStripe\ORM\Versioning\Versioned;
14
use SilverStripe\Forms\Tab;
15
use SilverStripe\Forms\TabSet;
16
17
/**
18
 * Represents a logical folder, which may be used to organise assets
19
 * stored in the configured backend.
20
 *
21
 * Unlike {@see File} dataobjects, there is not necessarily a physical filesystem entite which
22
 * represents a Folder, and it may be purely logical. However, a physical folder may exist
23
 * if the backend creates one.
24
 *
25
 * Additionally, folders do not have URLs (relative or absolute), nor do they have paths.
26
 *
27
 * When a folder is moved or renamed, records within it will automatically be copied to the updated
28
 * location.
29
 *
30
 * Deleting a folder will remove all child records, but not any physical files.
31
 *
32
 * See {@link File} documentation for more details about the
33
 * relationship between the database and filesystem in the SilverStripe file APIs.
34
 */
35
class Folder extends File
36
{
37
38
    private static $singular_name = "Folder";
39
40
    private static $plural_name = "Folders";
41
42
    private static $table_name = 'Folder';
43
44
    public function exists()
45
    {
46
        return $this->isInDB();
47
    }
48
49
    /**
50
     *
51
     */
52
    public function populateDefaults()
53
    {
54
        parent::populateDefaults();
55
56
        if (!$this->Name) {
57
            $this->Name = _t('AssetAdmin.NEWFOLDER', "NewFolder");
58
        }
59
    }
60
61
    /**
62
     * Find the given folder or create it as a database record
63
     *
64
     * @param string $folderPath Directory path relative to assets root
65
     * @return Folder|null
66
     */
67
    public static function find_or_make($folderPath)
68
    {
69
        // replace leading and trailing slashes
70
        $folderPath = preg_replace('/^\/?(.*)\/?$/', '$1', trim($folderPath));
71
        $parts = explode("/", $folderPath);
72
73
        $parentID = 0;
74
        $item = null;
75
        $filter = FileNameFilter::create();
76
        foreach ($parts as $part) {
77
            if (!$part) {
78
                continue; // happens for paths with a trailing slash
79
            }
80
81
            // Ensure search includes folders with illegal characters removed, but
82
            // err in favour of matching existing folders if $folderPath
83
            // includes illegal characters itself.
84
            $partSafe = $filter->filter($part);
85
            $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...
86
                'ParentID' => $parentID,
87
                'Name' => array($partSafe, $part)
88
            ))->first();
89
90
            if (!$item) {
91
                $item = new Folder();
92
                $item->ParentID = $parentID;
93
                $item->Name = $partSafe;
94
                $item->Title = $part;
95
                $item->write();
96
            }
97
            $parentID = $item->ID;
98
        }
99
100
        return $item;
101
    }
102
103
    public function onBeforeDelete()
104
    {
105
        foreach ($this->AllChildren() as $child) {
106
            $child->delete();
107
        }
108
109
        parent::onBeforeDelete();
110
    }
111
112
    /**
113
     * Return the relative URL of an icon for this file type
114
     *
115
     * @return string
116
     */
117
    public function getIcon()
118
    {
119
        return FRAMEWORK_DIR . "/client/dist/images/app_icons/folder_icon_large.png";
120
    }
121
122
    /**
123
     * Override setting the Title of Folders to that Name and Title are always in sync.
124
     * Note that this is not appropriate for files, because someone might want to create a human-readable name
125
     * of a file that is different from its name on disk. But folders should always match their name on disk.
126
     *
127
     * @param string $title
128
     * @return $this
129
     */
130
    public function setTitle($title)
131
    {
132
        $this->setField('Title', $title);
133
        $this->setField('Name', $title);
134
135
        return $this;
136
    }
137
138
    /**
139
     * Get the folder title
140
     *
141
     * @return string
142
     */
143
    public function getTitle()
144
    {
145
        return $this->Name;
146
    }
147
148
    /**
149
     * A folder doesn't have a (meaningful) file size.
150
     *
151
     * @return null
152
     */
153
    public function getSize()
154
    {
155
        return null;
156
    }
157
158
    /**
159
     * Returns all children of this folder
160
     *
161
     * @return DataList
162
     */
163
    public function myChildren()
164
    {
165
        return File::get()->filter("ParentID", $this->ID);
166
    }
167
168
    /**
169
     * Returns true if this folder has children
170
     *
171
     * @return bool
172
     */
173
    public function hasChildren()
174
    {
175
        return $this->myChildren()->exists();
176
    }
177
178
    /**
179
     * Returns true if this folder has children
180
     *
181
     * @return bool
182
     */
183
    public function hasChildFolders()
184
    {
185
        return $this->ChildFolders()->exists();
186
    }
187
188
    /**
189
     * Return the FieldList used to edit this folder in the CMS.
190
     * You can modify this FieldList by subclassing folder, or by creating a {@link DataExtension}
191
     * and implemeting updateCMSFields(FieldList $fields) on that extension.
192
     *
193
     * @return FieldList
194
     */
195
    public function getCMSFields()
196
    {
197
        // Don't show readonly path until we can implement parent folder selection,
198
        // it's too confusing when readonly (makes sense for files only).
199
200
        $width = (int)Image::config()->get('asset_preview_width');
201
        $previewLink = Convert::raw2att($this->ScaleMaxWidth($width)->getIcon());
202
        $image = "<img src=\"{$previewLink}\" class=\"editor__thumbnail\" />";
203
204
        $content = Tab::create(
205
            'Main',
206
            HeaderField::create('TitleHeader', $this->Title, 1)
207
                ->addExtraClass('editor__heading'),
208
            LiteralField::create("IconFull", $image)
209
                ->addExtraClass('editor__file-preview'),
210
            TabSet::create(
211
                'Editor',
212
                Tab::create(
213
                    'Details',
214
                    TextField::create("Name", $this->fieldLabel('Filename'))
215
                )
216
            ),
217
            HiddenField::create('ID', $this->ID)
218
        );
219
220
        $fields = FieldList::create(TabSet::create('Root', $content));
221
222
        $this->extend('updateCMSFields', $fields);
223
224
        return $fields;
225
    }
226
227
    /**
228
     * Get the children of this folder that are also folders.
229
     *
230
     * @return DataList
231
     */
232
    public function ChildFolders()
233
    {
234
        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...
235
    }
236
237
    /**
238
     * Get the number of children of this folder that are also folders.
239
     *
240
     * @return int
241
     */
242
    public function numChildFolders()
243
    {
244
        return $this->ChildFolders()->count();
245
    }
246
    /**
247
     * @return string
248
     */
249
    public function CMSTreeClasses()
250
    {
251
        $classes = sprintf('class-%s', $this->class);
252
253
        if (!$this->canDelete()) {
254
            $classes .= " nodelete";
255
        }
256
257
        if (!$this->canEdit()) {
258
            $classes .= " disabled";
259
        }
260
261
        $classes .= $this->markingClasses('numChildFolders');
262
263
        return $classes;
264
    }
265
266
    /**
267
     * @return string
268
     */
269
    public function getTreeTitle()
270
    {
271
        return sprintf(
272
            "<span class=\"jstree-foldericon\"></span><span class=\"item\">%s</span>",
273
            Convert::raw2att(preg_replace('~\R~u', ' ', $this->Title))
274
        );
275
    }
276
277
    public function getFilename()
278
    {
279
        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...
280
    }
281
282
    /**
283
     * Folders do not have public URLs
284
     *
285
     * @param bool $grant
286
     * @return null|string
287
     */
288
    public function getURL($grant = true)
289
    {
290
        return null;
291
    }
292
293
    /**
294
     * Folders do not have public URLs
295
     *
296
     * @return string
297
     */
298
    public function getAbsoluteURL()
299
    {
300
        return null;
301
    }
302
303
    public function onAfterWrite()
304
    {
305
        parent::onAfterWrite();
306
307
        // No publishing UX for folders, so just cascade changes live
308
        if (Versioned::get_stage() === Versioned::DRAFT) {
309
            $this->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
310
        }
311
312
        // Update draft version of all child records
313
        $this->updateChildFilesystem();
314
    }
315
316
    public function onAfterDelete()
317
    {
318
        parent::onAfterDelete();
319
320
        // Cascade deletions to live
321
        if (Versioned::get_stage() === Versioned::DRAFT) {
322
            $this->deleteFromStage(Versioned::LIVE);
323
        }
324
    }
325
326
    public function updateFilesystem()
327
    {
328
        // No filesystem changes to update
329
    }
330
331
    /**
332
     * If a write is skipped due to no changes, ensure that nested records still get asked to update
333
     */
334
    public function onAfterSkippedWrite()
335
    {
336
        $this->updateChildFilesystem();
337
    }
338
339
    /**
340
     * Update filesystem of all children
341
     */
342
    public function updateChildFilesystem()
343
    {
344
        // Don't synchronise on live (rely on publishing instead)
345
        if (Versioned::get_stage() === Versioned::LIVE) {
346
            return;
347
        }
348
349
        $this->flushCache();
350
        // Writing this record should trigger a write (and potential updateFilesystem) on each child
351
        foreach ($this->AllChildren() as $child) {
352
            $child->write();
353
        }
354
    }
355
356
    public function StripThumbnail()
357
    {
358
        return null;
359
    }
360
361
    public function validate()
362
    {
363
        $result = ValidationResult::create();
364
        $this->extend('validate', $result);
365
        return $result;
366
    }
367
}
368