mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API CHANGErefactored upload functionality from File into newly created Upload class
API CHANGE deprecated some File functions and attributes API CHANGE moved management function from File to Filesystem and added permission checks: sync(), loadContent(), fixfiles(), moverootfilesto() API CHANGE deprecated use of File->loadUploaded() ENHANCEMENT added filesize and extension validation to AssetAdmin and FileField FEATURE added tests for Upload class Merged revisions 47617 via svnmerge from svn://svn.silverstripe.com/silverstripe/modules/cms/branches/2.2.0-mesq ........ r47617 | ischommer | 2008-01-04 19:20:29 +1300 (Fri, 04 Jan 2008) | 5 lines git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@52205 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
5d1336a5a4
commit
b465a46bcc
@ -15,19 +15,9 @@
|
|||||||
* @subpackage filesystem
|
* @subpackage filesystem
|
||||||
*/
|
*/
|
||||||
class File extends DataObject {
|
class File extends DataObject {
|
||||||
|
|
||||||
static $default_sort = "Name";
|
static $default_sort = "Name";
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array Key is the extension, which has an array of MaxSize and WarnSize,
|
|
||||||
* e.g. array("jpg" => array("MaxSize"=>1000, "WarnSize=>500"))
|
|
||||||
*/
|
|
||||||
static $file_size_restrictions = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array Collection of extensions, e.g. array("jpg","gif")
|
|
||||||
*/
|
|
||||||
static $allowed_file_types = array();
|
|
||||||
|
|
||||||
static $singular_name = "File";
|
static $singular_name = "File";
|
||||||
|
|
||||||
static $plural_name = "Files";
|
static $plural_name = "Files";
|
||||||
@ -39,16 +29,20 @@ class File extends DataObject {
|
|||||||
"Content" => "Text",
|
"Content" => "Text",
|
||||||
"Sort" => "Int"
|
"Sort" => "Int"
|
||||||
);
|
);
|
||||||
|
|
||||||
static $indexes = array(
|
static $indexes = array(
|
||||||
"SearchFields" => "fulltext (Filename,Title,Content)",
|
"SearchFields" => "fulltext (Filename,Title,Content)",
|
||||||
);
|
);
|
||||||
|
|
||||||
static $has_one = array(
|
static $has_one = array(
|
||||||
"Parent" => "File",
|
"Parent" => "File",
|
||||||
"Owner" => "Member"
|
"Owner" => "Member"
|
||||||
);
|
);
|
||||||
|
|
||||||
static $extensions = array(
|
static $extensions = array(
|
||||||
"Hierarchy",
|
"Hierarchy",
|
||||||
);
|
);
|
||||||
|
|
||||||
static $belongs_many_many = array(
|
static $belongs_many_many = array(
|
||||||
"BackLinkTracking" => "SiteTree",
|
"BackLinkTracking" => "SiteTree",
|
||||||
);
|
);
|
||||||
@ -60,32 +54,41 @@ class File extends DataObject {
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $cache_file_fields = null;
|
protected static $cache_file_fields = null;
|
||||||
|
|
||||||
|
function Link($action = null) {
|
||||||
|
return Director::baseURL() . $this->RelativeLink($action);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RelativeLink($action = null){
|
||||||
|
return $this->Filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TreeTitle() {
|
||||||
|
return $this->Title;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the maximum
|
* Event handler called before deleting from the database.
|
||||||
|
* You can overload this to clean up or otherwise process data before delete this
|
||||||
|
* record. Don't forget to call parent::onBeforeDelete(), though!
|
||||||
*/
|
*/
|
||||||
static function setMaxFileSize( $maxSize, $warningSize, $extension = '*' ) {
|
protected function onBeforeDelete() {
|
||||||
self::$file_size_restrictions[$extension]['MaxSize'] = $maxSize;
|
parent::onBeforeDelete();
|
||||||
self::$file_size_restrictions[$extension]['WarnSize'] = $warningSize;
|
|
||||||
}
|
$this->autosetFilename();
|
||||||
|
if($this->Filename && $this->Name && file_exists($this->getFullPath()) && !is_dir($this->getFullPath())) {
|
||||||
static function getMaxFileSize($extension = '*') {
|
unlink($this->getFullPath());
|
||||||
if(!isset(self::$file_size_restrictions[$extension])) {
|
|
||||||
if(isset(self::$file_size_restrictions['*'])) {
|
|
||||||
$extension = '*';
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return array( self::$file_size_restrictions[$extension]['MaxSize'], self::$file_size_restrictions[$extension]['WarnSize'] );
|
if($brokenPages = $this->BackLinkTracking()) {
|
||||||
|
foreach($brokenPages as $brokenPage) {
|
||||||
|
Notifications::event("BrokenLink", $brokenPage, $brokenPage->OwnerID);
|
||||||
|
$brokenPage->HasBrokenFile = true;
|
||||||
|
$brokenPage->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static function allowedFileType( $extension ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find the given file
|
* Find the given file
|
||||||
*/
|
*/
|
||||||
@ -138,19 +141,6 @@ class File extends DataObject {
|
|||||||
function Icon() {
|
function Icon() {
|
||||||
$ext = $this->Extension;
|
$ext = $this->Extension;
|
||||||
if(!Director::fileExists("sapphire/images/app_icons/{$ext}_32.gif")) {
|
if(!Director::fileExists("sapphire/images/app_icons/{$ext}_32.gif")) {
|
||||||
/*switch($ext) {
|
|
||||||
case "aif": case "au": case "mid": case "midi": case "mp3": case "ra": case "ram": case "rm":
|
|
||||||
case "mp3": case "wav": case "m4a":
|
|
||||||
$ext = "audio"; break;
|
|
||||||
|
|
||||||
case "arc": case "rar": case "tar": case "gz": case "tgz": case "bz2": case "dmg":
|
|
||||||
$ext = "zip"; break;
|
|
||||||
|
|
||||||
case "bmp": case "gif": case "jpg": case "jpeg": case "pcx": case "tif": case "png":
|
|
||||||
$ext = "image"; break;
|
|
||||||
|
|
||||||
}*/
|
|
||||||
|
|
||||||
$ext = $this->appCategory();
|
$ext = $this->appCategory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,81 +152,20 @@ class File extends DataObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save an file passed from a form post into this object
|
* Save an file passed from a form post into this object.
|
||||||
|
* DEPRECATED Please instanciate an Upload-object instead and pass the file
|
||||||
|
* via {Upload->loadIntoFile()}.
|
||||||
|
*
|
||||||
|
* @param $tmpFile array Indexed array that PHP generated for every file it uploads.
|
||||||
|
* @return Boolean|string Either success or error-message.
|
||||||
*/
|
*/
|
||||||
function loadUploaded($tmpFile, $folderName = 'Uploads') {
|
function loadUploaded($tmpFile) {
|
||||||
if(!is_array($tmpFile)) user_error("File::loadUploaded() Not passed an array. Most likely, the form hasn't got the right enctype", E_USER_ERROR);
|
user_error('File::loadUploaded is deprecated, please use the Upload class directly.', E_USER_NOTICE);
|
||||||
if(!$tmpFile['size']) return;
|
|
||||||
|
|
||||||
|
$upload = new Upload();
|
||||||
|
$upload->loadIntoFile($tmpFile, $this);
|
||||||
|
|
||||||
// @TODO This puts a HUGE limitation on files especially when lots
|
return $upload->isError();
|
||||||
// have been uploaded.
|
|
||||||
$base = dirname(dirname($_SERVER['SCRIPT_FILENAME']));
|
|
||||||
$class = $this->class;
|
|
||||||
$parentFolder = Folder::findOrMake($folderName);
|
|
||||||
|
|
||||||
// Create a folder for uploading.
|
|
||||||
if(!file_exists("$base/assets")){
|
|
||||||
mkdir("$base/assets", Filesystem::$folder_create_mask);
|
|
||||||
}
|
|
||||||
if(!file_exists("$base/assets/$folderName")){
|
|
||||||
mkdir("$base/assets/$folderName", Filesystem::$folder_create_mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate default filename
|
|
||||||
$file = str_replace(' ', '-',$tmpFile['name']);
|
|
||||||
$file = ereg_replace('[^A-Za-z0-9+.-]+','',$file);
|
|
||||||
$file = ereg_replace('-+', '-',$file);
|
|
||||||
$file = basename($file);
|
|
||||||
|
|
||||||
$file = "assets/$folderName/$file";
|
|
||||||
|
|
||||||
while(file_exists("$base/$file")) {
|
|
||||||
$i = isset($i) ? ($i+1) : 2;
|
|
||||||
$oldFile = $file;
|
|
||||||
if(substr($file, strlen($file) - strlen('.tar.gz')) == '.tar.gz' ||
|
|
||||||
substr($file, strlen($file) - strlen('.tar.bz2')) == '.tar.bz2') {
|
|
||||||
$file = ereg_replace('[0-9]*(\.tar\.[^.]+$)',$i . '\\1', $file);
|
|
||||||
} else {
|
|
||||||
$file = ereg_replace('[0-9]*(\.[^.]+$)',$i . '\\1', $file);
|
|
||||||
}
|
|
||||||
if($oldFile == $file && $i > 2) user_error("Couldn't fix $file with $i", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], "$base/$file")) {
|
|
||||||
// Update with the new image
|
|
||||||
/*$this->Filename = */ // $this->Name = null;
|
|
||||||
// $this->Filename = $file;
|
|
||||||
|
|
||||||
// This is to prevent it from trying to rename the file
|
|
||||||
$this->record['Name'] = null;
|
|
||||||
$this->ParentID = $parentFolder->ID;
|
|
||||||
$this->Name = basename($file);
|
|
||||||
$this->write();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
user_error("File::loadUploaded: Couldn't copy '$tmpFile[tmp_name]' to '$file'", E_USER_ERROR);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function ensures the file table is correct with the files in the assets folder.
|
|
||||||
*/
|
|
||||||
static function sync() {
|
|
||||||
singleton('Folder')->syncChildren();
|
|
||||||
$finished = false;
|
|
||||||
while(!$finished) {
|
|
||||||
$orphans = DB::query("SELECT C.ID FROM File AS C LEFT JOIN File AS P ON C.ParentID = P.ID WHERE P.ID IS NULL AND C.ParentID > 0");
|
|
||||||
$finished = true;
|
|
||||||
if($orphans) foreach($orphans as $orphan) {
|
|
||||||
$finished = false;
|
|
||||||
// Delete the database record but leave the filesystem alone
|
|
||||||
$file = DataObject::get_by_id("File", $orphan['ID']);
|
|
||||||
$file->deleteDatabaseOnly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -246,79 +175,6 @@ class File extends DataObject {
|
|||||||
Debug::show(get_defined_functions());
|
Debug::show(get_defined_functions());
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadallcontent() {
|
|
||||||
ini_set("max_execution_time", 50000);
|
|
||||||
$allFiles = DataObject::get("File");
|
|
||||||
$total = $allFiles->TotalItems();
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
foreach($allFiles as $file) {
|
|
||||||
$i++;
|
|
||||||
$tmp = TEMP_FOLDER;
|
|
||||||
`echo "$i / $total" > $tmp/progress`;
|
|
||||||
$file->write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the content of this file and puts it in the field Content
|
|
||||||
*/
|
|
||||||
function loadContent() {
|
|
||||||
$filename = escapeshellarg($this->getFullPath());
|
|
||||||
switch(strtolower($this->getExtension())){
|
|
||||||
case 'pdf':
|
|
||||||
$content = `pdftotext $filename -`;
|
|
||||||
|
|
||||||
//echo("<pre>Content for $this->Filename:\n$content</pre>");
|
|
||||||
$this->Content = $content;
|
|
||||||
break;
|
|
||||||
case 'doc':
|
|
||||||
$content = `catdoc $filename`;
|
|
||||||
$this->Content = $content;
|
|
||||||
break;
|
|
||||||
case 'ppt':
|
|
||||||
$content = `catppt $filename`;
|
|
||||||
$this->Content = $content;
|
|
||||||
break;
|
|
||||||
case 'txt';
|
|
||||||
$content = file_get_contents($this->FileName);
|
|
||||||
$this->Content = $content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Link($action = null) {
|
|
||||||
return Director::baseURL() . $this->RelativeLink($action);
|
|
||||||
}
|
|
||||||
|
|
||||||
function RelativeLink($action = null){
|
|
||||||
return $this->Filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TreeTitle() {
|
|
||||||
if($this->hasMethod('alternateTreeTitle')) return $this->alternateTreeTitle();
|
|
||||||
else return $this->Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event handler called before deleting from the database.
|
|
||||||
* You can overload this to clean up or otherwise process data before delete this
|
|
||||||
* record. Don't forget to call parent::onBeforeDelete(), though!
|
|
||||||
*/
|
|
||||||
protected function onBeforeDelete() {
|
|
||||||
parent::onBeforeDelete();
|
|
||||||
|
|
||||||
$this->autosetFilename();
|
|
||||||
if($this->Filename && $this->Name && file_exists($this->getFullPath()) && !is_dir($this->getFullPath())) unlink($this->getFullPath());
|
|
||||||
|
|
||||||
if($brokenPages = $this->BackLinkTracking()) {
|
|
||||||
foreach($brokenPages as $brokenPage) {
|
|
||||||
Notifications::event("BrokenLink", $brokenPage, $brokenPage->OwnerID);
|
|
||||||
$brokenPage->HasBrokenFile = true;
|
|
||||||
$brokenPage->write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the database record (recursively for folders) without touching the filesystem
|
* Delete the database record (recursively for folders) without touching the filesystem
|
||||||
*/
|
*/
|
||||||
@ -343,8 +199,6 @@ class File extends DataObject {
|
|||||||
$brokenPage->write();
|
$brokenPage->write();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->loadContent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -549,6 +403,7 @@ class File extends DataObject {
|
|||||||
function getExtension() {
|
function getExtension() {
|
||||||
return strtolower(substr($this->getField('Filename'),strrpos($this->getField('Filename'),'.')+1));
|
return strtolower(substr($this->getField('Filename'),strrpos($this->getField('Filename'),'.')+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileType() {
|
function getFileType() {
|
||||||
$types = array(
|
$types = array(
|
||||||
'gif' => 'GIF Image - good for diagrams',
|
'gif' => 'GIF Image - good for diagrams',
|
||||||
@ -563,6 +418,7 @@ class File extends DataObject {
|
|||||||
'pdf' => 'Adobe Acrobat PDF file',
|
'pdf' => 'Adobe Acrobat PDF file',
|
||||||
);
|
);
|
||||||
$ext = $this->getExtension();
|
$ext = $this->getExtension();
|
||||||
|
|
||||||
return isset($types[$ext]) ? $types[$ext] : 'unknown';
|
return isset($types[$ext]) ? $types[$ext] : 'unknown';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,13 +427,16 @@ class File extends DataObject {
|
|||||||
*/
|
*/
|
||||||
function getSize() {
|
function getSize() {
|
||||||
$size = $this->getAbsoluteSize();
|
$size = $this->getAbsoluteSize();
|
||||||
if($size){
|
|
||||||
if($size < 1024) return $size . ' bytes';
|
return ($size) ? self::format_size($size) : false;
|
||||||
if($size < 1024*10) return (round($size/1024*10)/10). ' KB';
|
}
|
||||||
if($size < 1024*1024) return round($size/1024) . ' KB';
|
|
||||||
if($size < 1024*1024*10) return (round(($size/1024)/1024*10)/10) . ' MB';
|
public static function format_size($size) {
|
||||||
if($size < 1024*1024*1024) return round(($size/1024)/1024) . ' MB';
|
if($size < 1024) return $size . ' bytes';
|
||||||
}
|
if($size < 1024*10) return (round($size/1024*10)/10). ' KB';
|
||||||
|
if($size < 1024*1024) return round($size/1024) . ' KB';
|
||||||
|
if($size < 1024*1024*10) return (round(($size/1024)/1024*10)/10) . ' MB';
|
||||||
|
if($size < 1024*1024*1024) return round(($size/1024)/1024) . ' MB';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -592,33 +451,6 @@ class File extends DataObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------//
|
|
||||||
// Helper control functions
|
|
||||||
function moverootfilesto() {
|
|
||||||
if($folder = $this->urlParams[ID]) {
|
|
||||||
$newParent = Folder::findOrMake($folder);
|
|
||||||
$files = DataObject::get("File", "ClassName != 'Folder' AND ParentID = 0");
|
|
||||||
foreach($files as $file) {
|
|
||||||
echo "<li>" , $file->RelativePath;
|
|
||||||
$file->ParentID = $newParent->ID;
|
|
||||||
echo " -> " , $file->RelativePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup function to reset all the Filename fields. Visit File/fixfiles to call.
|
|
||||||
*/
|
|
||||||
function fixfiles() {
|
|
||||||
$files = DataObject::get("File");
|
|
||||||
foreach($files as $file) {
|
|
||||||
$file->resetFilename();
|
|
||||||
echo "<li>", $file->Filename;
|
|
||||||
$file->write();
|
|
||||||
}
|
|
||||||
echo "<p>Done!";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select clause for DataObject::get('File') operations/
|
* Select clause for DataObject::get('File') operations/
|
||||||
@ -676,7 +508,74 @@ class File extends DataObject {
|
|||||||
|
|
||||||
self::$cache_file_fields = null;
|
self::$cache_file_fields = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
static $file_size_restrictions = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
static $allowed_file_types = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
static function setMaxFileSize( $maxSize, $warningSize, $extension = '*' ) {
|
||||||
|
user_error('File::setMaxFileSize is deprecated',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
static function getMaxFileSize($extension = '*') {
|
||||||
|
user_error('File::getMaxFileSize is deprecated',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
static function sync() {
|
||||||
|
user_error('File::sync is deprecated - please use FileSystem::sync',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
function moverootfilesto() {
|
||||||
|
user_error('File::moverootfilesto is deprecated - please use FileSystem::moverootfilesto',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
function fixfiles() {
|
||||||
|
user_error('File::fixfiles is deprecated - please use FileSystem::fixfiles',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
function loadContent() {
|
||||||
|
user_error('File::loadContent deprecated',E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
|
public function loadallcontent() {
|
||||||
|
user_error('File::loadallcontent deprecated',E_USER_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* @package sapphire
|
|
||||||
* @subpackage filesystem
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
@ -16,6 +11,8 @@ class Filesystem extends Object {
|
|||||||
|
|
||||||
public static $folder_create_mask = 02775;
|
public static $folder_create_mask = 02775;
|
||||||
|
|
||||||
|
protected protected static $cache_folderModTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a folder, recursively
|
* Create a folder, recursively
|
||||||
*/
|
*/
|
||||||
@ -27,8 +24,7 @@ class Filesystem extends Object {
|
|||||||
/**
|
/**
|
||||||
* Remove a directory and all subdirectories and files
|
* Remove a directory and all subdirectories and files
|
||||||
*/
|
*/
|
||||||
static function removeFolder( $folder ) {
|
static function removeFolder($folder) {
|
||||||
|
|
||||||
// remove a file encountered by a recursive call.
|
// remove a file encountered by a recursive call.
|
||||||
if( !is_dir( $folder ) || is_link($folder) )
|
if( !is_dir( $folder ) || is_link($folder) )
|
||||||
unlink( $folder );
|
unlink( $folder );
|
||||||
@ -45,13 +41,41 @@ class Filesystem extends Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function moverootfilesto() {
|
||||||
|
if(!Permission::check('ADMIN')) Security::permissionFailure($this);
|
||||||
|
|
||||||
|
if($folder = $this->urlParams['ID']) {
|
||||||
|
$newParent = Folder::findOrMake($folder);
|
||||||
|
$files = DataObject::get("File", "ClassName != 'Folder' AND ParentID = 0");
|
||||||
|
foreach($files as $file) {
|
||||||
|
echo "<li>" , $file->RelativePath;
|
||||||
|
$file->ParentID = $newParent->ID;
|
||||||
|
echo " -> " , $file->RelativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup function to reset all the Filename fields. Visit File/fixfiles to call.
|
||||||
|
*/
|
||||||
|
public function fixfiles() {
|
||||||
|
if(!Permission::check('ADMIN')) Security::permissionFailure($this);
|
||||||
|
|
||||||
|
$files = DataObject::get("File");
|
||||||
|
foreach($files as $file) {
|
||||||
|
$file->resetFilename();
|
||||||
|
echo "<li>", $file->Filename;
|
||||||
|
$file->write();
|
||||||
|
}
|
||||||
|
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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected static $cache_folderModTime;
|
|
||||||
static function folderModTime($folder, $extensionList = null, $recursiveCall = false) {
|
static function folderModTime($folder, $extensionList = null, $recursiveCall = false) {
|
||||||
//$cacheID = $folder . ',' . implode(',', $extensionList);
|
//$cacheID = $folder . ',' . implode(',', $extensionList);
|
||||||
//if(!$recursiveCall && self::$cache_folderModTime[$cacheID]) return self::$cache_folderModTime[$cacheID];
|
//if(!$recursiveCall && self::$cache_folderModTime[$cacheID]) return self::$cache_folderModTime[$cacheID];
|
||||||
@ -89,7 +113,25 @@ class Filesystem extends Object {
|
|||||||
else return $filename[0] == '/';
|
else return $filename[0] == '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function ensures the file table is correct with the files in the assets folder.
|
||||||
|
*/
|
||||||
|
static function sync() {
|
||||||
|
singleton('Folder')->syncChildren();
|
||||||
|
$finished = false;
|
||||||
|
while(!$finished) {
|
||||||
|
$orphans = DB::query("SELECT C.ID FROM File AS C LEFT JOIN File AS P ON C.ParentID = P.ID WHERE P.ID IS NULL AND C.ParentID > 0");
|
||||||
|
$finished = true;
|
||||||
|
if($orphans) foreach($orphans as $orphan) {
|
||||||
|
$finished = false;
|
||||||
|
// Delete the database record but leave the filesystem alone
|
||||||
|
$file = DataObject::get_by_id("File", $orphan['ID']);
|
||||||
|
$file->deleteDatabaseOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
325
filesystem/Upload.php
Normal file
325
filesystem/Upload.php
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Manages uploads via HTML forms processed by PHP,
|
||||||
|
* uploads to Silverstripe's default upload directory,
|
||||||
|
* and either creates a new or uses an existing File-object
|
||||||
|
* for syncing with the database.
|
||||||
|
*
|
||||||
|
* @package sapphire
|
||||||
|
* @subpackage filesystem
|
||||||
|
*
|
||||||
|
* @todo Allow for non-database uploads
|
||||||
|
*/
|
||||||
|
class Upload extends Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A File object
|
||||||
|
*
|
||||||
|
* @var File
|
||||||
|
*/
|
||||||
|
protected $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about the temporary file produced
|
||||||
|
* by the PHP-runtime.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $tmpFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restrict filesize for either all filetypes
|
||||||
|
* or a specific extension, with extension-name
|
||||||
|
* as array-key and the size-restriction in bytes as array-value.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $allowedMaxFileSize = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Collection of extensions.
|
||||||
|
* Extension-names are treated case-insensitive.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* array("jpg","GIF")
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $allowedExtensions = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processing errors that can be evaluated,
|
||||||
|
* e.g. by Form-validation.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $errors = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A foldername relative to /assets,
|
||||||
|
* where all uploaded files are stored by default.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public static $uploads_folder = "Uploads";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save an file passed from a form post into this object.
|
||||||
|
*
|
||||||
|
* @param $tmpFile array Indexed array that PHP generated for every file it uploads.
|
||||||
|
* @param $folderPath string Folder path relative to /assets
|
||||||
|
* @return Boolean|string Either success or error-message.
|
||||||
|
*/
|
||||||
|
function load($tmpFile, $folderPath = false) {
|
||||||
|
$this->clearErrors();
|
||||||
|
|
||||||
|
if(!$folderPath) $folderPath = self::$uploads_folder;
|
||||||
|
|
||||||
|
if(!$this->file) $this->file = new File();
|
||||||
|
|
||||||
|
if(!is_array($tmpFile)) {
|
||||||
|
user_error("File::loadUploaded() Not passed an array. Most likely, the form hasn't got the right enctype", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$tmpFile['size']) {
|
||||||
|
$this->errors[] = _t('File.NOFILESIZE', 'Filesize is zero bytes.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid = $this->validate($tmpFile);
|
||||||
|
if(!$valid) return false;
|
||||||
|
|
||||||
|
// @TODO This puts a HUGE limitation on files especially when lots
|
||||||
|
// have been uploaded.
|
||||||
|
$base = Director::baseFolder();
|
||||||
|
$parentFolder = Folder::findOrMake($folderPath);
|
||||||
|
|
||||||
|
// Create a folder for uploading.
|
||||||
|
if(!file_exists("$base/assets")){
|
||||||
|
mkdir("$base/assets", Filesystem::$folder_create_mask);
|
||||||
|
}
|
||||||
|
if(!file_exists("$base/assets/" . $folderPath)){
|
||||||
|
mkdir("$base/assets/" . $folderPath, Filesystem::$folder_create_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate default filename
|
||||||
|
$fileName = str_replace(' ', '-',$tmpFile['name']);
|
||||||
|
$fileName = ereg_replace('[^A-Za-z0-9+.-]+','',$fileName);
|
||||||
|
$fileName = ereg_replace('-+', '-',$fileName);
|
||||||
|
$fileName = basename($fileName);
|
||||||
|
|
||||||
|
$relativeFilePath = "assets/" . $folderPath . "/$fileName";
|
||||||
|
|
||||||
|
// if filename already exists, version the filename (e.g. test.gif to test1.gif)
|
||||||
|
while(file_exists("$base/$relativeFilePath")) {
|
||||||
|
$i = isset($i) ? ($i+1) : 2;
|
||||||
|
$oldFilePath = $relativeFilePath;
|
||||||
|
// make sure archives retain valid extensions
|
||||||
|
if(substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.gz')) == '.tar.gz' ||
|
||||||
|
substr($relativeFilePath, strlen($relativeFilePath) - strlen('.tar.bz2')) == '.tar.bz2') {
|
||||||
|
$relativeFilePath = ereg_replace('[0-9]*(\.tar\.[^.]+$)',$i . '\\1', $relativeFilePath);
|
||||||
|
} else {
|
||||||
|
$relativeFilePath = ereg_replace('[0-9]*(\.[^.]+$)',$i . '\\1', $relativeFilePath);
|
||||||
|
}
|
||||||
|
if($oldFilePath == $relativeFilePath && $i > 2) user_error("Couldn't fix $relativeFilePath with $i tries", E_USER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file_exists($tmpFile['tmp_name']) && copy($tmpFile['tmp_name'], "$base/$relativeFilePath")) {
|
||||||
|
$this->file->ParentID = $parentFolder->ID;
|
||||||
|
// This is to prevent it from trying to rename the file
|
||||||
|
$this->file->Name = basename($relativeFilePath);
|
||||||
|
$this->file->write();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
$this->errors[] = _t('File.NOFILESIZE', 'Filesize is zero bytes.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load temporary PHP-upload into File-object.
|
||||||
|
*
|
||||||
|
* @param array $tmpFile
|
||||||
|
* @param File $file
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
public function loadIntoFile($tmpFile, $file) {
|
||||||
|
$this->file = $file;
|
||||||
|
|
||||||
|
return $this->load($tmpFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for all validation on the file
|
||||||
|
* (e.g. size and extension restrictions).
|
||||||
|
* Is NOT connected to the {Validator} classes,
|
||||||
|
* please have a look at {FileField->validate()}
|
||||||
|
* for an example implementation of external validation.
|
||||||
|
*
|
||||||
|
* @param array $tmpFile
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function validate($tmpFile) {
|
||||||
|
$pathInfo = pathinfo($tmpFile['name']);
|
||||||
|
// filesize validation
|
||||||
|
if(!$this->isValidSize($tmpFile)) {
|
||||||
|
$this->errors[] = sprintf(
|
||||||
|
_t(
|
||||||
|
'File.TOOLARGE',
|
||||||
|
'Filesize is too large, maximum %s allowed.',
|
||||||
|
PR_MEDIUM,
|
||||||
|
'Argument 1: Filesize (e.g. 1MB)'
|
||||||
|
),
|
||||||
|
File::format_size($this->getAllowedMaxFileSize($pathInfo['extension']))
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extension validation
|
||||||
|
if(!$this->isValidExtension($tmpFile)) {
|
||||||
|
$this->errors[] = sprintf(
|
||||||
|
_t(
|
||||||
|
'File.INVALIDEXTENSION',
|
||||||
|
'Extension is not allowed (valid: %s)',
|
||||||
|
PR_MEDIUM,
|
||||||
|
'Argument 1: Comma-separated list of valid extensions'
|
||||||
|
),
|
||||||
|
implode(',',$this->allowedExtensions)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file-object, either generated from {load()},
|
||||||
|
* or manually set.
|
||||||
|
*
|
||||||
|
* @return File
|
||||||
|
*/
|
||||||
|
public function getFile() {
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a file-object (similiar to {loadIntoFile()})
|
||||||
|
*
|
||||||
|
* @param File $file
|
||||||
|
*/
|
||||||
|
public function setFile($file) {
|
||||||
|
$this->file = $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maximum file size for all or specified file extension.
|
||||||
|
*
|
||||||
|
* @param string $ext
|
||||||
|
* @return int Filesize in bytes
|
||||||
|
*/
|
||||||
|
public function getAllowedMaxFileSize($ext = null) {
|
||||||
|
$ext = strtolower($ext);
|
||||||
|
if(isset($ext) && isset($this->allowedMaxFileSize[$ext])) {
|
||||||
|
return $this->allowedMaxFileSize[$ext];
|
||||||
|
} else {
|
||||||
|
return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set filesize maximums (in bytes).
|
||||||
|
* Automatically converts extensions to lowercase
|
||||||
|
* for easier matching.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* array('*' => 200, 'jpg' => 1000)
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param array|int $rules
|
||||||
|
*/
|
||||||
|
public function setAllowedMaxFileSize($rules) {
|
||||||
|
if(is_array($rules) && count($rules)) {
|
||||||
|
// make sure all extensions are lowercase
|
||||||
|
$rules = array_change_key_case($rules, CASE_LOWER);
|
||||||
|
$this->allowedMaxFileSize = $rules;
|
||||||
|
} elseif((int)$rules > 0) {
|
||||||
|
$this->allowedMaxFileSize['*'] = (int)$rules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAllowedExtensions() {
|
||||||
|
return $this->allowedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $rules
|
||||||
|
*/
|
||||||
|
public function setAllowedExtensions($rules) {
|
||||||
|
if(!is_array($rules)) return false;
|
||||||
|
|
||||||
|
// make sure all rules are lowercase
|
||||||
|
foreach($rules as &$rule) $rule = strtolower($rule);
|
||||||
|
|
||||||
|
$this->allowedExtensions = $rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the bytesize of an uploaded
|
||||||
|
* file is valid - can be defined on an
|
||||||
|
* extension-by-extension basis in {$allowedMaxFileSize}
|
||||||
|
*
|
||||||
|
* @param array $tmpFile
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isValidSize($tmpFile) {
|
||||||
|
$pathInfo = pathinfo($tmpFile['name']);
|
||||||
|
$maxSize = $this->getAllowedMaxFileSize(strtolower($pathInfo['extension']));
|
||||||
|
return (!$tmpFile['size'] || !$maxSize || (int)$tmpFile['size'] < $maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the temporary file has a valid extension
|
||||||
|
*
|
||||||
|
* @param array $tmpFile
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isValidExtension($tmpFile) {
|
||||||
|
$pathInfo = pathinfo($tmpFile['name']);
|
||||||
|
return (!count($this->allowedExtensions) || array_key_exists(strtolower($pathInfo['extension']), $this->allowedExtensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear out all errors (mostly set by {loadUploaded()})
|
||||||
|
*/
|
||||||
|
public function clearErrors() {
|
||||||
|
$this->errors = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines wether previous operations caused an error.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isError() {
|
||||||
|
return (count($this->errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return all errors that occurred while processing so far
|
||||||
|
* (mostly set by {loadUploaded()})
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getErrors() {
|
||||||
|
return $this->errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
@ -1,13 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package forms
|
|
||||||
* @subpackage fieldeditor
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EditableFileField
|
|
||||||
* Allows a user to add a field that can be used to upload a file
|
* Allows a user to add a field that can be used to upload a file
|
||||||
|
|
||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fieldeditor
|
* @subpackage fieldeditor
|
||||||
*/
|
*/
|
||||||
@ -18,19 +12,23 @@ class EditableFileField extends EditableFormField {
|
|||||||
"UploadedFile" => "File"
|
"UploadedFile" => "File"
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO Interface and properties for these properties
|
/**
|
||||||
static $file_size_restrictions = array();
|
* @see {Upload->allowedMaxFileSize}
|
||||||
static $allowed_file_types = array();
|
* @var int
|
||||||
|
*/
|
||||||
|
public static $allowed_max_file_size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see {Upload->allowedExtensions}
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public static $allowed_extensions = array();
|
||||||
|
|
||||||
static $singular_name = 'File field';
|
static $singular_name = 'File field';
|
||||||
static $plural_names = 'File fields';
|
static $plural_names = 'File fields';
|
||||||
|
|
||||||
function ExtraOptions() {
|
|
||||||
return parent::ExtraOptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFormField() {
|
function getFormField() {
|
||||||
if( $field = parent::getFormField() )
|
if($field = parent::getFormField())
|
||||||
return $field;
|
return $field;
|
||||||
return new FileField($this->Name, $this->Title, $this->getField('Default'));
|
return new FileField($this->Name, $this->Title, $this->getField('Default'));
|
||||||
// TODO We can't use the preview feature because FileIFrameField also shows the "From the file store" functionality
|
// TODO We can't use the preview feature because FileIFrameField also shows the "From the file store" functionality
|
||||||
@ -41,8 +39,8 @@ class EditableFileField extends EditableFormField {
|
|||||||
return new FileField($this->Name, $this->Title, $this->getField('Default'));
|
return new FileField($this->Name, $this->Title, $this->getField('Default'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSubmittedField($data, $submittedForm, $fieldClass = "SubmittedFileField" ) {
|
function createSubmittedField($data, $submittedForm, $fieldClass = "SubmittedFileField") {
|
||||||
if( !$_FILES[$this->Name] )
|
if(!$_FILES[$this->Name])
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
$submittedField = new $fieldClass();
|
$submittedField = new $fieldClass();
|
||||||
@ -51,12 +49,17 @@ class EditableFileField extends EditableFormField {
|
|||||||
$submittedField->ParentID = $submittedForm->ID;
|
$submittedField->ParentID = $submittedForm->ID;
|
||||||
|
|
||||||
// create the file from post data
|
// create the file from post data
|
||||||
$uploadedFile = new File();
|
$upload = new Upload();
|
||||||
$uploadedFile->set_stat('file_size_restrictions',$this->stat('file_size_restrictions'));
|
$upload->setAllowedExtensions(self::$allowed_extensions);
|
||||||
$uploadedFile->set_stat('allowed_file_types',$this->stat('allowed_file_types'));
|
$upload->setAllowedMaxFileSize(self::$allowed_max_file_size);
|
||||||
$uploadedFile->loadUploaded( $_FILES[$this->Name] );
|
|
||||||
|
// upload file
|
||||||
|
$upload->load($_FILES[$this->Name]);
|
||||||
|
|
||||||
|
$uploadedFile = $upload->getFile();
|
||||||
$submittedField->UploadedFileID = $uploadedFile->ID;
|
$submittedField->UploadedFileID = $uploadedFile->ID;
|
||||||
$submittedField->write();
|
$submittedField->write();
|
||||||
|
|
||||||
return $submittedField;
|
return $submittedField;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,71 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
|
||||||
* @package forms
|
|
||||||
* @subpackage fields-files
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a file type which can be added to a form.
|
* Represents a file type which can be added to a form.
|
||||||
|
* Automatically tries to save has_one-relations on the saved
|
||||||
|
* record.
|
||||||
|
*
|
||||||
|
* Please set a validator on the form-object to get feedback
|
||||||
|
* about imposed filesize/extension restrictions.
|
||||||
|
*
|
||||||
|
* CAUTION: Doesn't work in the CMS due to ajax submission, please use {@link FileIframeField} instead.
|
||||||
|
*
|
||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage fields-files
|
* @subpackage fields-files
|
||||||
*/
|
*/
|
||||||
class FileField extends FormField {
|
class FileField extends FormField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restrict filesize for either all filetypes
|
||||||
|
* or a specific extension, with extension-name
|
||||||
|
* as array-key and the size-restriction in bytes as array-value.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $allowedMaxFileSize = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Collection of extensions.
|
||||||
|
* Extension-names are treated case-insensitive.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* array("jpg","GIF")
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
public $allowedExtensions = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to automatically determine and save a has_one-relationship
|
||||||
|
* on the saved record (e.g. a "Player" has_one "PlayerImage" would
|
||||||
|
* trigger saving the ID of newly created file into "PlayerImageID"
|
||||||
|
* on the record).
|
||||||
|
*
|
||||||
|
* @var unknown_type
|
||||||
|
*/
|
||||||
|
public $relationAutoSetting = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload object (needed for validation
|
||||||
|
* and actually moving the temporary file
|
||||||
|
* created by PHP).
|
||||||
|
*
|
||||||
|
* @var Upload
|
||||||
|
*/
|
||||||
|
protected $upload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Partial filesystem path relative to /assets directory.
|
||||||
|
* Defaults to 'Uploads'.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $folderName = 'Uploads';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new file field.
|
* Create a new file field.
|
||||||
|
*
|
||||||
* @param string $name The internal field name, passed to forms.
|
* @param string $name The internal field name, passed to forms.
|
||||||
* @param string $title The field label.
|
* @param string $title The field label.
|
||||||
* @param int $value The value of the field.
|
* @param int $value The value of the field.
|
||||||
@ -20,33 +73,145 @@ class FileField extends FormField {
|
|||||||
* @param string $rightTitle Used in SmallFieldHolder() to force a right-aligned label
|
* @param string $rightTitle Used in SmallFieldHolder() to force a right-aligned label
|
||||||
* @param string $folderName Folder to upload files to
|
* @param string $folderName Folder to upload files to
|
||||||
*/
|
*/
|
||||||
function __construct($name, $title = null, $value = null, $form = null, $rightTitle = null, $folderName = 'Uploads') {
|
function __construct($name, $title = null, $value = null, $form = null, $rightTitle = null, $folderName = null) {
|
||||||
$this->folderName = $folderName;
|
if(isset($folderName)) $this->folderName = $folderName;
|
||||||
|
$this->upload = new Upload();
|
||||||
|
|
||||||
parent::__construct($name, $title, $value, $form, $rightTitle);
|
parent::__construct($name, $title, $value, $form, $rightTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Field() {
|
public function Field() {
|
||||||
return
|
return
|
||||||
$this->createTag("input", array("type" => "file", "name" => $this->name, "id" => $this->id())) .
|
$this->createTag("input",
|
||||||
$this->createTag("input", array("type" => "hidden", "name" => "MAX_FILE_SIZE", "value" => 30*1024*1024));
|
array(
|
||||||
|
"type" => "file",
|
||||||
|
"name" => $this->name,
|
||||||
|
"id" => $this->id(),
|
||||||
|
"tabindex" => $this->getTabIndex()
|
||||||
|
)
|
||||||
|
) .
|
||||||
|
$this->createTag("input",
|
||||||
|
array(
|
||||||
|
"type" => "hidden",
|
||||||
|
"name" => "MAX_FILE_SIZE",
|
||||||
|
"value" => $this->getAllowedMaxFileSize(),
|
||||||
|
"tabindex" => $this->getTabIndex()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveInto(DataObject $record) {
|
public function saveInto(DataObject $record) {
|
||||||
$fieldName = $this->name . 'ID';
|
if(!isset($_FILES[$this->name])) return false;
|
||||||
$hasOnes = $record->has_one($this->name);
|
|
||||||
|
|
||||||
// assume that the file is connected via a has-one
|
$this->upload->setAllowedExtensions($this->allowedExtensions);
|
||||||
if(!$hasOnes || !isset($_FILES[$this->name]) || !$_FILES[$this->name]['name']) return false;
|
$this->upload->setAllowedMaxFileSize($this->allowedMaxFileSize);
|
||||||
|
$this->upload->load($_FILES[$this->name]);
|
||||||
|
if($this->upload->isError()) return false;
|
||||||
|
|
||||||
$file = new File();
|
$file = $this->upload->getFile();
|
||||||
$file->loadUploaded($_FILES[$this->name], $this->folderName);
|
|
||||||
|
|
||||||
$record->$fieldName = $file->ID;
|
if($this->relationAutoSetting) {
|
||||||
|
$fieldName = $this->name . 'ID';
|
||||||
|
// assume that the file is connected via a has-one
|
||||||
|
$hasOnes = $record->has_one($this->name);
|
||||||
|
if(!$hasOnes) return false;
|
||||||
|
|
||||||
|
// save to record
|
||||||
|
$record->$fieldName = $file->ID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Value() {
|
public function Value() {
|
||||||
return $_FILES[$this->Name()];
|
return $_FILES[$this->Name()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get maximum file size for all or specified file extension.
|
||||||
|
* Falls back to the default filesize restriction ('*')
|
||||||
|
* if the extension was not found.
|
||||||
|
*
|
||||||
|
* @param string $ext
|
||||||
|
* @return int Filesize in bytes (0 means no filesize set)
|
||||||
|
*/
|
||||||
|
public function getAllowedMaxFileSize($ext = null) {
|
||||||
|
$ext = strtolower($ext);
|
||||||
|
if(isset($ext) && isset($this->allowedMaxFileSize[$ext])) {
|
||||||
|
return $this->allowedMaxFileSize[$ext];
|
||||||
|
} else {
|
||||||
|
return (isset($this->allowedMaxFileSize['*'])) ? $this->allowedMaxFileSize['*'] : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set filesize maximums (in bytes).
|
||||||
|
* Automatically converts extensions to lowercase
|
||||||
|
* for easier matching.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* array('*' => 200, 'jpg' => 1000)
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param unknown_type $rules
|
||||||
|
*/
|
||||||
|
public function setAllowedMaxFileSize($rules) {
|
||||||
|
if(is_array($rules)) {
|
||||||
|
// make sure all extensions are lowercase
|
||||||
|
$rules = array_change_key_case($rules, CASE_LOWER);
|
||||||
|
$this->allowedMaxFileSize = $rules;
|
||||||
|
} else {
|
||||||
|
$this->allowedMaxFileSize['*'] = (int)$rules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getAllowedExtensions() {
|
||||||
|
return $this->allowedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $rules
|
||||||
|
*/
|
||||||
|
public function setAllowedExtensions($rules) {
|
||||||
|
if(!is_array($rules)) return false;
|
||||||
|
|
||||||
|
// make sure all rules are lowercase
|
||||||
|
foreach($rules as &$rule) $rule = strtolower($rule);
|
||||||
|
|
||||||
|
$this->allowedExtensions = $rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $folderName
|
||||||
|
*/
|
||||||
|
public function setFolderName($folderName) {
|
||||||
|
$this->folderName = $folderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFolderName() {
|
||||||
|
return $folderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate($validator) {
|
||||||
|
$tmpFile = $_FILES[$this->name];
|
||||||
|
$this->upload->setAllowedExtensions($this->allowedExtensions);
|
||||||
|
$this->upload->setAllowedMaxFileSize($this->allowedMaxFileSize);
|
||||||
|
|
||||||
|
$valid = $this->upload->validate($tmpFile);
|
||||||
|
if(!$valid) {
|
||||||
|
$errors = $this->upload->getErrors();
|
||||||
|
if($errors) foreach($errors as $error) {
|
||||||
|
$validator->validationError($this->name, $error, "validation", false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
63
tests/filesystem/UploadTest.php
Normal file
63
tests/filesystem/UploadTest.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
class UploadTest extends SapphireTest {
|
||||||
|
|
||||||
|
function testUpload() {
|
||||||
|
// create tmp file
|
||||||
|
$tmpFileName = 'UploadTest_testUpload.txt';
|
||||||
|
$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
|
||||||
|
$tmpFileContent = '';
|
||||||
|
for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
|
||||||
|
file_put_contents($tmpFilePath, $tmpFileContent);
|
||||||
|
|
||||||
|
// emulates the $_FILES array
|
||||||
|
$tmpFile = array(
|
||||||
|
'name' => $tmpFileName,
|
||||||
|
'type' => 'text/plaintext',
|
||||||
|
'size' => filesize($tmpFilePath),
|
||||||
|
'tmp_name' => $tmpFilePath,
|
||||||
|
'extension' => 'txt',
|
||||||
|
'error' => UPLOAD_ERR_OK,
|
||||||
|
);
|
||||||
|
|
||||||
|
// test upload into default folder
|
||||||
|
$u1 = new Upload();
|
||||||
|
$u1->load($tmpFile);
|
||||||
|
$file1 = $u1->getFile();
|
||||||
|
$this->assertTrue(
|
||||||
|
file_exists($file1->getFullPath()),
|
||||||
|
'File upload to standard directory in /assets'
|
||||||
|
);
|
||||||
|
$this->assertTrue(
|
||||||
|
(strpos($file1->getFullPath(),Director::baseFolder() . '/assets/' . Upload::$uploads_folder) !== false),
|
||||||
|
'File upload to standard directory in /assets'
|
||||||
|
);
|
||||||
|
$file1->delete();
|
||||||
|
|
||||||
|
// test upload into custom folder
|
||||||
|
$customFolder = 'UploadTest_testUpload';
|
||||||
|
$u2 = new Upload();
|
||||||
|
$u2->load($tmpFile, $customFolder);
|
||||||
|
$file2 = $u2->getFile();
|
||||||
|
$this->assertTrue(
|
||||||
|
file_exists($file2->getFullPath()),
|
||||||
|
'File upload to custom directory in /assets'
|
||||||
|
);
|
||||||
|
$this->assertTrue(
|
||||||
|
(strpos($file2->getFullPath(),Director::baseFolder() . '/assets/' . $customFolder) !== false),
|
||||||
|
'File upload to custom directory in /assets'
|
||||||
|
);
|
||||||
|
$file2->delete();
|
||||||
|
|
||||||
|
unlink($tmpFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAllowedFilesize() {
|
||||||
|
// @todo
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAllowedExtensions() {
|
||||||
|
// @todo
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
Loading…
Reference in New Issue
Block a user