silverstripe-framework/Assets/Folder.php

336 lines
7.8 KiB
PHP
Raw Normal View History

<?php
namespace SilverStripe\Assets;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HeaderField;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\ValidationResult;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Represents a logical folder, which may be used to organise assets
* stored in the configured backend.
*
* Unlike {@see File} dataobjects, there is not necessarily a physical filesystem entite which
* represents a Folder, and it may be purely logical. However, a physical folder may exist
* if the backend creates one.
*
* Additionally, folders do not have URLs (relative or absolute), nor do they have paths.
*
* When a folder is moved or renamed, records within it will automatically be copied to the updated
* location.
*
* Deleting a folder will remove all child records, but not any physical files.
*
* See {@link File} documentation for more details about the
* relationship between the database and filesystem in the SilverStripe file APIs.
*/
class Folder extends File {
private static $singular_name = "Folder";
private static $plural_name = "Folders";
private static $table_name = 'Folder';
public function exists() {
return $this->isInDB();
}
/**
*
*/
public function populateDefaults() {
2011-03-15 10:30:31 +01:00
parent::populateDefaults();
if(!$this->Name) {
$this->Name = _t('AssetAdmin.NEWFOLDER', "NewFolder");
}
2011-03-15 10:30:31 +01:00
}
/**
* Find the given folder or create it as a database record
*
* @param string $folderPath Directory path relative to assets root
* @return Folder|null
*/
public static function find_or_make($folderPath) {
// replace leading and trailing slashes
$folderPath = preg_replace('/^\/?(.*)\/?$/', '$1', trim($folderPath));
$parts = explode("/",$folderPath);
$parentID = 0;
$item = null;
$filter = FileNameFilter::create();
foreach($parts as $part) {
if(!$part) {
continue; // happens for paths with a trailing slash
}
// Ensure search includes folders with illegal characters removed, but
// err in favour of matching existing folders if $folderPath
// includes illegal characters itself.
$partSafe = $filter->filter($part);
$item = Folder::get()->filter(array(
'ParentID' => $parentID,
'Name' => array($partSafe, $part)
))->first();
if(!$item) {
$item = new Folder();
$item->ParentID = $parentID;
$item->Name = $partSafe;
$item->Title = $part;
$item->write();
}
$parentID = $item->ID;
}
return $item;
}
public function onBeforeDelete() {
foreach($this->AllChildren() as $child) {
$child->delete();
}
parent::onBeforeDelete();
}
/**
Merge 3 into master # Conflicts: # .travis.yml # admin/css/ie7.css # admin/css/ie7.css.map # admin/css/ie8.css.map # admin/css/screen.css # admin/css/screen.css.map # admin/javascript/LeftAndMain.js # admin/scss/_style.scss # admin/scss/_uitheme.scss # control/HTTPRequest.php # core/Object.php # css/AssetUploadField.css # css/AssetUploadField.css.map # css/ConfirmedPasswordField.css.map # css/Form.css.map # css/GridField.css.map # css/TreeDropdownField.css.map # css/UploadField.css # css/UploadField.css.map # css/debug.css.map # dev/Debug.php # docs/en/00_Getting_Started/00_Server_Requirements.md # docs/en/02_Developer_Guides/06_Testing/00_Unit_Testing.md # docs/en/02_Developer_Guides/06_Testing/index.md # docs/en/02_Developer_Guides/14_Files/02_Images.md # docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md # filesystem/File.php # filesystem/Folder.php # filesystem/GD.php # filesystem/Upload.php # forms/ToggleField.php # forms/Validator.php # javascript/lang/en_GB.js # javascript/lang/fr.js # javascript/lang/src/en.js # javascript/lang/src/fr.js # model/Image.php # model/UnsavedRelationList.php # model/Versioned.php # model/connect/MySQLDatabase.php # model/fieldtypes/DBField.php # model/fieldtypes/Enum.php # scss/AssetUploadField.scss # scss/UploadField.scss # templates/email/ChangePasswordEmail.ss # templates/forms/DropdownField.ss # tests/behat/features/bootstrap/SilverStripe/Framework/Test/Behaviour/CmsFormsContext.php # tests/behat/features/bootstrap/SilverStripe/Framework/Test/Behaviour/CmsUiContext.php # tests/forms/EnumFieldTest.php # tests/security/MemberTest.php # tests/security/MemberTest.yml # tests/security/SecurityTest.php
2016-04-29 07:50:55 +02:00
* Return the relative URL of an icon for this file type
*
* @return string
*/
public function getIcon() {
return FRAMEWORK_DIR . "/client/dist/images/app_icons/folder_32.png";
}
/**
* Override setting the Title of Folders to that Name and Title are always in sync.
* Note that this is not appropriate for files, because someone might want to create a human-readable name
* of a file that is different from its name on disk. But folders should always match their name on disk.
2016-03-08 21:50:18 +01:00
*
* @param string $title
* @return $this
*/
public function setTitle($title) {
$this->setName($title);
return $this;
}
/**
* Get the folder title
*
* @return string
*/
public function getTitle() {
return $this->Name;
}
/**
* Override setting the Title of Folders to that Name and Title are always in sync.
* Note that this is not appropriate for files, because someone might want to create a human-readable name
* of a file that is different from its name on disk. But folders should always match their name on disk.
*
* @param string $name
* @return $this
*/
public function setName($name) {
parent::setName($name);
$this->setField('Title', $this->Name);
return $this;
}
/**
* A folder doesn't have a (meaningful) file size.
*
* @return null
*/
public function getSize() {
return null;
}
/**
* Returns all children of this folder
*
* @return DataList
*/
public function myChildren() {
return File::get()->filter("ParentID", $this->ID);
}
/**
* Returns true if this folder has children
*
* @return bool
*/
public function hasChildren() {
return $this->myChildren()->exists();
}
/**
* Returns true if this folder has children
*
* @return bool
*/
public function hasChildFolders() {
return $this->ChildFolders()->exists();
}
/**
* Return the FieldList used to edit this folder in the CMS.
* You can modify this FieldList by subclassing folder, or by creating a {@link DataExtension}
* and implemeting updateCMSFields(FieldList $fields) on that extension.
*
* @return FieldList
*/
public function getCMSFields() {
// Don't show readonly path until we can implement parent folder selection,
// it's too confusing when readonly (makes sense for files only).
$fields = FieldList::create([
HeaderField::create('TitleHeader', $this->Title, 1),
LiteralField::create("ImageFull", $this->PreviewThumbnail()),
TextField::create("Name", $this->fieldLabel('Filename')),
HiddenField::create('ID', $this->ID)
]);
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* Get the children of this folder that are also folders.
*
* @return DataList
*/
public function ChildFolders() {
return Folder::get()->filter('ParentID', $this->ID);
}
/**
* Get the number of children of this folder that are also folders.
*
* @return int
*/
public function numChildFolders() {
return $this->ChildFolders()->count();
}
/**
* @return string
*/
public function CMSTreeClasses() {
$classes = sprintf('class-%s', $this->class);
if(!$this->canDelete()) {
$classes .= " nodelete";
}
if(!$this->canEdit()) {
$classes .= " disabled";
}
$classes .= $this->markingClasses('numChildFolders');
return $classes;
}
/**
* @return string
*/
public function getTreeTitle() {
return sprintf(
"<span class=\"jstree-foldericon\"></span><span class=\"item\">%s</span>",
Convert::raw2att(preg_replace('~\R~u', ' ', $this->Title))
);
}
public function getFilename() {
return parent::generateFilename() . '/';
}
/**
* Folders do not have public URLs
*
* @param bool $grant
* @return null|string
*/
public function getURL($grant = true) {
return null;
}
/**
* Folders do not have public URLs
*
* @return string
*/
public function getAbsoluteURL() {
return null;
}
public function onAfterWrite() {
parent::onAfterWrite();
// No publishing UX for folders, so just cascade changes live
if(Versioned::get_stage() === Versioned::DRAFT) {
2016-04-01 05:27:59 +02:00
$this->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
}
// Update draft version of all child records
$this->updateChildFilesystem();
}
public function onAfterDelete() {
parent::onAfterDelete();
// Cascade deletions to live
if(Versioned::get_stage() === Versioned::DRAFT) {
$this->deleteFromStage(Versioned::LIVE);
}
}
public function updateFilesystem() {
// No filesystem changes to update
}
/**
* If a write is skipped due to no changes, ensure that nested records still get asked to update
*/
public function onAfterSkippedWrite() {
$this->updateChildFilesystem();
}
/**
* Update filesystem of all children
*/
public function updateChildFilesystem() {
// Don't synchronise on live (rely on publishing instead)
if(Versioned::get_stage() === Versioned::LIVE) {
return;
}
$this->flushCache();
// Writing this record should trigger a write (and potential updateFilesystem) on each child
foreach ($this->AllChildren() as $child) {
$child->write();
}
}
public function StripThumbnail() {
return null;
}
public function validate() {
$result = ValidationResult::create();
$this->extend('validate', $result);
return $result;
}
}