MINOR Documentation in File and Folder class (from r107265)

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@112553 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2010-10-15 03:12:39 +00:00
parent ad20ddf24b
commit 2d127753ed
4 changed files with 171 additions and 30 deletions

View File

@ -73,6 +73,8 @@ class Image extends File {
/** /**
* An image exists if it has a filename. * An image exists if it has a filename.
* Does not do any filesystem checks.
*
* @return boolean * @return boolean
*/ */
public function exists() { public function exists() {
@ -82,7 +84,9 @@ class Image extends File {
} }
/** /**
* Return an XHTML img tag for this Image. * Return an XHTML img tag for this Image,
* or NULL if the image file doesn't exist on the filesystem.
*
* @return string * @return string
*/ */
function getTag() { function getTag() {
@ -100,6 +104,7 @@ class Image extends File {
/** /**
* Return an XHTML img tag for this Image. * Return an XHTML img tag for this Image.
*
* @return string * @return string
*/ */
function forTemplate() { function forTemplate() {

View File

@ -1,14 +1,63 @@
<?php <?php
/** /**
* This class handles the representation of a File within Sapphire * This class handles the representation of a file on the filesystem within the framework.
* Most of the methods also handle the {@link Folder} subclass.
*
* Note: The files are stored in the assets/ directory, but sapphire * Note: The files are stored in the assets/ directory, but sapphire
* looks at the db object to gather information about a file such as URL * looks at the db object to gather information about a file such as URL
* It then uses this for all processing functions (like image manipulation). * It then uses this for all processing functions (like image manipulation).
* *
* <b>Security</b>
*
* Caution: It is recommended to disable any script execution in the "assets/" * Caution: It is recommended to disable any script execution in the "assets/"
* directory in the webserver configuration, to reduce the risk of exploits. * directory in the webserver configuration, to reduce the risk of exploits.
* See http://doc.silverstripe.org/secure-development#filesystem * See http://doc.silverstripe.org/secure-development#filesystem
* *
* <b>Properties</b>
*
* - "Name": File name (including extension) or folder name.
* Should be the same as the actual filesystem.
* - "Title": Optional title of the file (for display purposes only).
* Defaults to "Name".
* - "Filename": Path of the file or folder, relative to the webroot.
* Usually starts with the "assets/" directory, and has no trailing slash.
* Defaults to the "assets/" directory plus "Name" property if not set.
* Setting the "Filename" property will override the "Name" property.
* The value should be in sync with "ParentID".
* - "Content": Typically unused, but handy for a textual representation of
* files, e.g. for fulltext indexing of PDF documents.
* - "ParentID": Points to a {@link Folder} record. Should be in sync with
* "Filename". A ParentID=0 value points to the "assets/" folder, not the webroot.
*
* <b>Synchronization</b>
*
* Changes to a File database record can change the filesystem entry,
* but not the other way around. If the filesystem path is renamed outside
* of SilverStripe, there's no way for the database to recover this linkage.
* New physical files on the filesystem can be "discovered" via {@link Filesystem::sync()},
* the equivalent {@link File} and {@link Folder} records are automatically
* created by this method.
*
* Certain property changes within the File API that can cause a "delayed" filesystem change:
* The change is enforced in {@link onBeforeWrite()} later on.
* - setParentID()
* - setFilename()
* - setName()
* It is recommended that you use {@link write()} directly after setting any of these properties,
* otherwise getters like {@link getFullPath()} and {@link getRelativePath()}
* will result paths that are inconsistent with the filesystem.
*
* Caution: Calling {@link delete()} will also delete from the filesystem.
* Call {@link deleteDatabaseOnly()} if you want to avoid this.
*
* <b>Creating Files and Folders</b>
*
* Typically both files and folders should be created first on the filesystem,
* and then reflected in as database records. Folders can be created recursively
* from sapphire both in the database and filesystem through {@link Folder::findOrMake()}.
* Ensure that you always set a "Filename" property when writing to the database,
* leaving it out can lead to unexpected results.
*
* @package sapphire * @package sapphire
* @subpackage filesystem * @subpackage filesystem
*/ */
@ -79,16 +128,18 @@ class File extends DataObject {
/** /**
* Find a File object by the given filename. * Find a File object by the given filename.
*
* @param String $filename Matched against the "Name" property.
* @return mixed null if not found, File object of found file * @return mixed null if not found, File object of found file
*/ */
static function find($filename) { static function find($filename) {
// Get the base file if $filename points to a resampled file // Get the base file if $filename points to a resampled file
$filename = ereg_replace('_resampled/[^-]+-','',$filename); $filename = ereg_replace('_resampled/[^-]+-','',$filename);
// Split to folders and the actual filename, and traverse the structure.
$parts = explode("/", $filename); $parts = explode("/", $filename);
$parentID = 0; $parentID = 0;
$item = null; $item = null;
foreach($parts as $part) { foreach($parts as $part) {
if($part == ASSETS_DIR && !$parentID) continue; if($part == ASSETS_DIR && !$parentID) continue;
$SQL_part = Convert::raw2sql($part); $SQL_part = Convert::raw2sql($part);
@ -112,7 +163,11 @@ class File extends DataObject {
return $this->Title; return $this->Title;
} }
// Used by AssetTableField /**
* @todo Unnecessary shortcut for AssetTableField, coupled with cms module.
*
* @return Integer
*/
function BackLinkTrackingCount() { function BackLinkTrackingCount() {
return $this->BackLinkTracking()->Count(); return $this->BackLinkTracking()->Count();
} }
@ -120,7 +175,7 @@ class File extends DataObject {
/** /**
* Event handler called before deleting from the database. * Event handler called before deleting from the database.
* You can overload this to clean up or otherwise process data before delete this * You can overload this to clean up or otherwise process data before delete this
* record. Don't forget to call parent::onBeforeDelete(), though! * record. Don't forget to call {@link parent::onBeforeDelete()}, though!
*/ */
protected function onBeforeDelete() { protected function onBeforeDelete() {
parent::onBeforeDelete(); parent::onBeforeDelete();
@ -131,6 +186,9 @@ class File extends DataObject {
} }
} }
/**
* Updates link tracking.
*/
protected function onAfterDelete() { protected function onAfterDelete() {
parent::onAfterDelete(); parent::onAfterDelete();
@ -204,6 +262,14 @@ class File extends DataObject {
return $this->canEdit($member); return $this->canEdit($member);
} }
/**
* Returns a category based on the file extension.
* This can be useful when grouping files by type,
* showing icons on filelinks, etc.
* Possible group values are: "audio", "mov", "zip", "image".
*
* @return String
*/
public function appCategory() { public function appCategory() {
$ext = $this->Extension; $ext = $this->Extension;
switch($ext) { switch($ext) {
@ -232,7 +298,11 @@ class File extends DataObject {
} }
/** /**
* Return the URL of an icon for the file type * Return the relative URL of an icon for the file type,
* based on the {@link appCategory()} value.
* Images are searched for in "sapphire/images/app_icons/".
*
* @return String
*/ */
function Icon() { function Icon() {
$ext = $this->Extension; $ext = $this->Extension;
@ -269,6 +339,7 @@ class File extends DataObject {
protected function onBeforeWrite() { protected function onBeforeWrite() {
parent::onBeforeWrite(); parent::onBeforeWrite();
// Set default name
if(!$this->Name) $this->Name = "new-" . strtolower($this->class); if(!$this->Name) $this->Name = "new-" . strtolower($this->class);
} }
@ -290,19 +361,25 @@ class File extends DataObject {
} }
/** /**
* Setter function for Name. * Setter function for Name. Automatically sets a default title,
* Automatically sets a default title. * and removes characters that migh be invalid on the filesystem.
* Also adds a suffix to the name if the filename already exists
* on the filesystem, and is associated to a different {@link File} database record
* in the same folder. This means "myfile.jpg" might become "myfile-1.jpg".
*
* @param String $name
*/ */
function setName($name) { function setName($name) {
$oldName = $this->Name; $oldName = $this->Name;
// It can't be blank // It can't be blank, default to Title
if(!$name) $name = $this->Title; if(!$name) $name = $this->Title;
// Fix illegal characters // Fix illegal characters
$name = ereg_replace(' +','-',trim($name)); $name = ereg_replace(' +','-',trim($name)); // Replace any spaces
$name = ereg_replace('[^A-Za-z0-9.+_\-]','',$name); $name = ereg_replace('[^A-Za-z0-9.+_\-]','',$name); // Replace non alphanumeric characters
// Remove all leading dots or underscores
while(!empty($name) && ($name[0] == '_' || $name[0] == '.')) { while(!empty($name) && ($name[0] == '_' || $name[0] == '.')) {
$name = substr($name, 1); $name = substr($name, 1);
} }
@ -326,27 +403,36 @@ class File extends DataObject {
} }
} }
// Update title
if(!$this->getField('Title')) $this->__set('Title', str_replace(array('-','_'),' ',ereg_replace('\.[^.]+$','',$name))); if(!$this->getField('Title')) $this->__set('Title', str_replace(array('-','_'),' ',ereg_replace('\.[^.]+$','',$name)));
// Update actual field value
$this->setField('Name', $name); $this->setField('Name', $name);
if($oldName && $oldName != $this->Name) { if($oldName && $oldName != $this->Name) {
$this->resetFilename(); $this->resetFilename();
} else { } else {
$this->autosetFilename(); $this->autosetFilename();
} }
return $this->getField('Name'); return $this->getField('Name');
} }
/** /**
* Change a filename, moving the file if appropriate. * Change the "Filename" property based on the current "Name" property, moving the file if appropriate.
* @param $renamePhysicalFile Set this to false if you don't want to rename the physical file. Used when calling resetFilename() on the children of a folder. * Throws an Exception if the new file already exists.
*
* Caution: This method should just be called during a {@link write()} invocation,
* otherwise the database and filesystem might become out of sync.
*
* @param Boolean $renamePhysicalFile FALSE to avoiding renaming the file on the filesystem.
* Used when calling {@link resetFilename()} on the children of a folder.
*/ */
protected function resetFilename($renamePhysicalFile = true) { protected function resetFilename($renamePhysicalFile = true) {
$oldFilename = $this->getField('Filename'); $oldFilename = $this->getField('Filename'); // call without getter to get old value
$newFilename = $this->getRelativePath(); $newFilename = $this->getRelativePath(); // calculated from $this->Name
if($this->Name && $this->Filename && file_exists(Director::getAbsFile($oldFilename)) && strpos($oldFilename, '//') === false) { if($this->Name && $this->Filename && file_exists(Director::getAbsFile($oldFilename)) && strpos($oldFilename, '//') === false) {
if($renamePhysicalFile) { if($renamePhysicalFile) {
@ -383,7 +469,12 @@ class File extends DataObject {
} }
/** /**
* Rewrite links to the $old file to now point to the $new file * Rewrite links to the $old file to now point to the $new file.
*
* @uses SiteTree->rewriteFileURL()
*
* @param String $old File path relative to the webroot
* @param String $new File path relative to the webroot
*/ */
protected function updateLinks($old, $new) { protected function updateLinks($old, $new) {
if(class_exists('Subsite')) Subsite::disable_subsite_filter(true); if(class_exists('Subsite')) Subsite::disable_subsite_filter(true);
@ -434,6 +525,12 @@ class File extends DataObject {
return "$this->Name"; return "$this->Name";
} }
/**
* Returns an absolute filesystem path to the file.
* Use {@link getRelativePath()} to get the same path relative to the webroot.
*
* @return String
*/
function getFullPath() { function getFullPath() {
$baseFolder = Director::baseFolder(); $baseFolder = Director::baseFolder();
@ -447,7 +544,10 @@ class File extends DataObject {
} }
/** /**
* Returns * Returns path relative to webroot.
* If no {@link Folder} is set ("ParentID" property),
* defaults to a filename relative to the ASSETS_DIR (usually "assets/").
* Use {@link getFullPath()} to
* *
* @return String * @return String
*/ */
@ -463,6 +563,9 @@ class File extends DataObject {
} }
} }
/**
* @todo Coupling with cms module, remove this method.
*/
function DeleteLink() { function DeleteLink() {
return Director::absoluteBaseURL()."admin/assets/removefile/".$this->ID; return Director::absoluteBaseURL()."admin/assets/removefile/".$this->ID;
} }
@ -477,13 +580,20 @@ class File extends DataObject {
function setFilename($val) { function setFilename($val) {
$this->setField('Filename', $val); $this->setField('Filename', $val);
// "Filename" is the "master record" (existing on the filesystem),
// meaning we have to adjust the "Name" property in the database as well.
$this->setField('Name', basename($val)); $this->setField('Name', basename($val));
} }
/* /**
* FIXME This overrides getExtension() in DataObject, but it does something completely different. * Returns the file extension
*
* @todo This overrides getExtension() in DataObject, but it does something completely different.
* This should be renamed to getFileExtension(), but has not been yet as it may break * This should be renamed to getFileExtension(), but has not been yet as it may break
* legacy code. * legacy code.
*
* @return String
*/ */
function getExtension() { function getExtension() {
return self::get_file_extension($this->getField('Filename')); return self::get_file_extension($this->getField('Filename'));

View File

@ -1,6 +1,7 @@
<?php <?php
/** /**
* A collection of static methods for manipulating the filesystem. * A collection of static methods for manipulating the filesystem.
*
* @package sapphire * @package sapphire
* @subpackage filesystem * @subpackage filesystem
*/ */
@ -13,7 +14,12 @@ class Filesystem extends Object {
protected static $cache_folderModTime; protected static $cache_folderModTime;
/** /**
* Create a folder, recursively * Create a folder on the filesystem, recursively.
* Uses {@link Filesystem::$folder_create_mask} to set filesystem permissions.
* Use {@link Folder::findOrMake()} to create a {@link Folder} database
* record automatically.
*
* @param String $folder Absolute folder path
*/ */
static function makeFolder($folder) { static function makeFolder($folder) {
if(!file_exists($base = dirname($folder))) self::makeFolder($base); if(!file_exists($base = dirname($folder))) self::makeFolder($base);
@ -21,8 +27,10 @@ class Filesystem extends Object {
} }
/** /**
* Remove a directory and all subdirectories and files * Remove a directory and all subdirectories and files.
* @param $contentsOnly If this is true then the contents of the folder will be removed but not the folder itself *
* @param String $folder Absolute folder path
* @param Boolean $contentsOnly If this is true then the contents of the folder will be removed but not the folder itself
*/ */
static function removeFolder( $folder, $contentsOnly = false ) { static function removeFolder( $folder, $contentsOnly = false ) {
@ -58,10 +66,12 @@ class Filesystem extends Object {
echo "<p>Done!"; echo "<p>Done!";
} }
/* /**
* Return the most recent modification time of anything in the folder. * Return the most recent modification time of anything in the folder.
*
* @param $folder The folder, relative to the site root * @param $folder The folder, relative to the site root
* @param $extensionList An option array of file extensions to limit the search to * @param $extensionList An option array of file extensions to limit the search to
* @return String Same as filemtime() format.
*/ */
static function folderModTime($folder, $extensionList = null, $recursiveCall = false) { static function folderModTime($folder, $extensionList = null, $recursiveCall = false) {
//$cacheID = $folder . ',' . implode(',', $extensionList); //$cacheID = $folder . ',' . implode(',', $extensionList);
@ -93,7 +103,10 @@ class Filesystem extends Object {
/** /**
* Returns true if the given filename is an absolute file reference. * Returns true if the given filename is an absolute file reference.
* Works on Linux and Windows * Works on Linux and Windows.
*
* @param String $filename Absolute or relative filename, with or without path.
* @return Boolean
*/ */
static function isAbsolute($filename) { static function isAbsolute($filename) {
if($_ENV['OS'] == "Windows_NT" || $_SERVER['WINDIR']) return $filename[1] == ':' && $filename[2] == '/'; if($_ENV['OS'] == "Windows_NT" || $_SERVER['WINDIR']) return $filename[1] == ':' && $filename[2] == '/';
@ -107,6 +120,9 @@ class Filesystem extends Object {
* If the given Folder ID isn't found, or not specified at all, then everything will * If the given Folder ID isn't found, or not specified at all, then everything will
* be synchronised from the root folder (singleton Folder). * be synchronised from the root folder (singleton Folder).
* *
* See {@link File->updateFilesystem()} to sync properties of a single database record
* back to the equivalent filesystem record.
*
* @param int $folderID Folder ID to sync along with all it's children * @param int $folderID Folder ID to sync along with all it's children
*/ */
static function sync($folderID = null) { static function sync($folderID = null) {

View File

@ -1,6 +1,14 @@
<?php <?php
/** /**
* Represents a folder in the assets/ directory. * Represents a folder in the assets/ directory.
* The folder path is stored in the "Filename" property.
*
* Updating the "Name" or "Filename" properties on
* a folder object also updates all associated children
* (both {@link File} and {@link Folder} records).
*
* See {@link File} documentation for more details about the
* relationship between the database and filesystem in the sapphire file APIs.
* *
* @package sapphire * @package sapphire
* @subpackage filesystem * @subpackage filesystem
@ -45,10 +53,12 @@ class Folder extends File {
parent::onBeforeWrite(); parent::onBeforeWrite();
} }
/* /**
* Find the given folder or create it, recursively. * Find the given folder or create it both as {@link Folder} database records
* and on the filesystem. If necessary, creates parent folders as well.
* *
* @param $folderPath string Absolute or relative path to the file * @param $folderPath string Absolute or relative path to the file
* @return Folder
*/ */
static function findOrMake($folderPath) { static function findOrMake($folderPath) {
// Create assets directory, if it is missing // Create assets directory, if it is missing