allowedMaxFileSize}
* @var int
*/
public static $allowed_max_file_size;
/**
* @see {Upload->allowedExtensions}
* @var array
*/
public static $allowed_extensions = array();
static $allowed_actions = array(
'addfolder',
'deletefolder',
'deletemarked',
'deleteUnusedThumbnails',
'doUpload',
'getfile',
'getsubtree',
'movemarked',
'removefile',
'save',
'savefile',
'uploadiframe',
);
public function Link($action = null) {
if(!$action) $action = "index";
return "admin/assets/$action/" . $this->currentPageID();
}
/**
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
*/
public function currentPageID() {
if(isset($_REQUEST['ID']) && is_numeric($_REQUEST['ID'])) {
return $_REQUEST['ID'];
} elseif (is_numeric($this->urlParams['ID'])) {
return $this->urlParams['ID'];
} elseif(is_numeric(Session::get("{$this->class}.currentPage"))) {
return Session::get("{$this->class}.currentPage");
} else {
return "root";
}
}
/**
* Set up the controller, in particular, re-sync the File database with the assets folder./
*/
function init() {
parent::init();
if(!file_exists('../assets')) {
mkdir('../assets');
}
// needed for MemberTableField (Requirements not determined before Ajax-Call)
Requirements::javascript("sapphire/javascript/ComplexTableField.js");
Requirements::css("jsparty/greybox/greybox.css");
Requirements::css("sapphire/css/ComplexTableField.css");
Requirements::javascript("cms/javascript/AssetAdmin.js");
Requirements::javascript("cms/javascript/AssetAdmin_left.js");
Requirements::javascript("cms/javascript/AssetAdmin_right.js");
Requirements::javascript("cms/javascript/CMSMain_upload.js");
Requirements::javascript("cms/javascript/Upload.js");
Requirements::javascript("sapphire/javascript/Security_login.js");
Requirements::javascript("jsparty/SWFUpload/SWFUpload.js");
// Include the right JS]
// Hayden: This didn't appear to be used at all
/*$fileList = new FileList("Form_EditForm_Files", null);
$fileList->setClick_AjaxLoad('admin/assets/getfile/', 'Form_SubForm');
$fileList->FieldHolder();*/
Requirements::javascript("jsparty/greybox/AmiJS.js");
Requirements::javascript("jsparty/greybox/greybox.js");
Requirements::css("jsparty/greybox/greybox.css");
Requirements::css("cms/css/AssetAdmin.css");
}
function index() {
Filesystem::sync();
return array();
}
/**
* Show the content of the upload iframe. The form is specified by a template.
*/
function uploadiframe() {
Requirements::clear();
Requirements::javascript("jsparty/prototype.js");
Requirements::javascript("jsparty/loader.js");
Requirements::javascript("jsparty/behaviour.js");
Requirements::javascript("jsparty/prototype_improvements.js");
Requirements::javascript("jsparty/layout_helpers.js");
Requirements::javascript("cms/javascript/LeftAndMain.js");
Requirements::javascript("jsparty/multifile/multifile.js");
Requirements::css("jsparty/multifile/multifile.css");
Requirements::css("cms/css/typography.css");
Requirements::css("cms/css/layout.css");
Requirements::css("cms/css/cms_left.css");
Requirements::css("cms/css/cms_right.css");
if(isset($data['ID']) && $data['ID'] != 'root') $folder = DataObject::get_by_id("Folder", $data['ID']);
else $folder = singleton('Folder');
$canUpload = $folder->userCanEdit();
return array( 'CanUpload' => $canUpload );
}
/**
* Return the form object shown in the uploadiframe.
*/
function UploadForm() {
$form = new Form($this,'UploadForm', new FieldSet(
new HiddenField("ID", "", $this->currentPageID()),
// needed because the button-action is triggered outside the iframe
new HiddenField("action_doUpload", "", "1"),
new FileField("Files[0]" , _t('AssetAdmin.CHOOSEFILE','Choose file ')),
new LiteralField('UploadButton',"
"),
new LiteralField('MultifileCode',"
" . _t('AssetAdmin.FILESREADY','Files ready to upload:') ."
")
), new FieldSet(
));
// Makes ajax easier
$form->disableSecurityToken();
return $form;
}
/**
* This method processes the results of the UploadForm.
* It will save the uploaded files to /assets/ and create new File objects as required.
*/
function doUpload($data, $form) {
foreach($data['Files'] as $param => $files) {
if(!is_array($files)) $files = array($files);
foreach($files as $key => $value) {
$processedFiles[$key][$param] = $value;
}
}
if($data['ID'] && $data['ID'] != 'root') $folder = DataObject::get_by_id("Folder", $data['ID']);
else $folder = singleton('Folder');
$newFiles = array();
$fileSizeWarnings = '';
$uploadErrors = '';
foreach($processedFiles as $tmpFile) {
if($tmpFile['error'] == UPLOAD_ERR_NO_TMP_DIR) {
$status = 'bad';
$statusMessage = _t('AssetAdmin.NOTEMP', 'There is no temporary folder for uploads. Please set upload_tmp_dir in php.ini.');
break;
}
if($tmpFile['tmp_name']) {
// Workaround open_basedir problems
if(ini_get("open_basedir")) {
$newtmp = TEMP_FOLDER . '/' . $tmpFile['name'];
move_uploaded_file($tmpFile['tmp_name'], $newtmp);
$tmpFile['tmp_name'] = $newtmp;
}
// validate files (only if not logged in as admin)
if(Permission::check('ADMIN')) {
$valid = true;
} else {
$upload = new Upload();
$upload->setAllowedExtensions(self::$allowed_extensions);
$upload->setAllowedMaxFileSize(self::$allowed_max_file_size);
$valid = $upload->validate($tmpFile);
if(!$valid) {
$errors = $upload->getErrors();
if($errors) foreach($errors as $error) {
$jsErrors .= "alert('" . Convert::raw2js($error) . "');";
}
}
}
// move file to given folder
if($valid) $newFiles[] = $folder->addUploadToFolder($tmpFile);
}
}
if($newFiles) {
$numFiles = sizeof($newFiles);
$statusMessage = sprintf(_t('AssetAdmin.UPLOADEDX',"Uploaded %s files"),$numFiles) ;
$status = "good";
} else if($status != 'bad') {
$statusMessage = _t('AssetAdmin.NOTHINGTOUPLOAD','There was nothing to upload');
$status = "";
}
$fileIDs = array();
$fileNames = array();
foreach($newFiles as $newFile) {
$fileIDs[] = $newFile;
$fileObj = DataObject::get_one('File', "`File`.ID=$newFile");
$fileNames[] = $fileObj->Name;
}
$sFileIDs = implode(',', $fileIDs);
$sFileNames = implode(',', $fileNames);
echo <<
/* IDs: $sFileIDs */
/* Names: $sFileNames */
var form = parent.document.getElementById('Form_EditForm');
form.getPageFromServer(form.elements.ID.value);
parent.statusMessage("{$statusMessage}","{$status}");
$jsErrors
parent.document.getElementById('sitetree').getTreeNodeByIdx( "{$folder->ID}" ).getElementsByTagName('a')[0].className += ' contents';
HTML;
}
/**
* Needs to be overridden to make sure an ID with value "0" is still valid (rootfolder)
*/
/**
* Return the form that displays the details of a folder, including a file list and fields for editing the folder name.
*/
function getEditForm($id) {
if($id && $id != "root") {
$record = DataObject::get_by_id("File", $id);
} else {
$record = singleton("Folder");
}
if($record) {
$fields = $record->getCMSFields();
$actions = new FieldSet();
// Only show save button if not 'assets' folder
if( $record->userCanEdit() && $id != "root") {
$actions = new FieldSet(
new FormAction('save',_t('AssetAdmin.SAVEFOLDERNAME','Save folder name'))
);
}
$form = new Form($this, "EditForm", $fields, $actions);
if($record->ID) {
$form->loadDataFrom($record);
} else {
$form->loadDataFrom(array(
"ID" => "root",
"URL" => Director::absoluteBaseURL() . 'assets/',
));
}
// @todo: These workflow features aren't really appropriate for all projects
if( Member::currentUser()->_isAdmin() && project() == 'mot' ) {
$fields->addFieldsToTab( 'Root.Workflow', new DropdownField("Owner", _t('AssetAdmin.OWNER','Owner'), Member::map() ) );
$fields->addFieldsToTab( 'Root.Workflow', new TreeMultiselectField("CanUse", _t('AssetAdmin.CONTENTUSABLEBY','Content usable by')) );
$fields->addFieldsToTab( 'Root.Workflow', new TreeMultiselectField("CanEdit", _t('AssetAdmin.CONTENTMODBY','Content modifiable by')) );
}
if( !$record->userCanEdit() )
$form->makeReadonly();
return $form;
}
}
/**
* Perform the "move marked" action.
* Called and returns in same way as 'save' function
*/
public function movemarked($urlParams, $form) {
if($_REQUEST['DestFolderID'] && is_numeric($_REQUEST['DestFolderID'])) {
$destFolderID = $_REQUEST['DestFolderID'];
$fileList = "'" . ereg_replace(' *, *',"','",trim(addslashes($_REQUEST['FileIDs']))) . "'";
$numFiles = 0;
if($fileList != "''") {
$files = DataObject::get("File", "`File`.ID IN ($fileList)");
if($files) {
foreach($files as $file) {
if($file instanceof Image) {
$file->deleteFormattedImages();
}
$file->ParentID = $destFolderID;
$file->write();
$numFiles++;
}
} else {
user_error("No files in $fileList could be found!", E_USER_ERROR);
}
}
$message = sprintf(_t('AssetAdmin.MOVEDX','Moved %s files'),$numFiles);
FormResponse::status_message($message, "good");
FormResponse::add("$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value)");
return FormResponse::respond();
} else {
user_error("Bad data: $_REQUEST[DestFolderID]", E_USER_ERROR);
}
}
/**
* Perform the "delete marked" action.
* Called and returns in same way as 'save' function
*/
public function deletemarked($urlParams, $form) {
$fileList = "'" . ereg_replace(' *, *',"','",trim(addslashes($_REQUEST['FileIDs']))) . "'";
$numFiles = 0;
$folderID = 0;
$deleteList = '';
$brokenPageList = '';
if($fileList != "''") {
$files = DataObject::get("File", "`File`.ID IN ($fileList)");
if($files) {
foreach($files as $file) {
if($file instanceof Image) {
$file->deleteFormattedImages();
}
if( !$folderID )
$folderID = $file->ParentID;
// $deleteList .= "\$('Form_EditForm_Files').removeById($file->ID);\n";
$file->delete();
$numFiles++;
}
if($brokenPages = Notifications::getItems("BrokenLink")) {
$brokenPageList = " ". _t('AssetAdmin.NOWBROKEN',"These pages now have broken links:")."";
foreach($brokenPages as $brokenPage) {
$brokenPageList .= "
" . $brokenPage->Breadcrumbs(3, true) . "
";
}
$brokenPageList .= "";
Notifications::notifyByEmail("BrokenLink", "Page_BrokenLinkEmail");
} else {
$brokenPageList = '';
}
$deleteList = '';
if( $folderID ) {
$remaining = DB::query("SELECT COUNT(*) FROM `File` WHERE `ParentID`=$folderID")->value();
if( !$remaining )
$deleteList .= "Element.removeClassName(\$('sitetree').getTreeNodeByIdx( '$folderID' ).getElementsByTagName('a')[0],'contents');";
}
} else {
user_error("No files in $fileList could be found!", E_USER_ERROR);
}
}
$message = sprintf(_t('AssetAdmin.DELETEDX',"Deleted %s files.%s"),$numFiles,$brokenPageList) ;
FormResponse::add($deleteList);
FormResponse::status_message($message, "good");
FormResponse::add("$('Form_EditForm').getPageFromServer($('Form_EditForm_ID').value)");
return FormResponse::respond();
}
/**
* Returns the content to be placed in Form_SubForm when editing a file.
* Called using ajax.
*/
public function getfile() {
SSViewer::setOption('rewriteHashlinks', false);
// bdc: only try to return something if user clicked on an object
if (is_object($this->getSubForm($this->urlParams['ID']))) {
return $this->getSubForm($this->urlParams['ID'])->formHtmlContent();
}
else return null;
}
/**
* Action handler for the save button on the file subform.
* Saves the file
*/
public function savefile($data, $form) {
$record = DataObject::get_by_id("File", $data['ID']);
$form->saveInto($record);
$record->write();
$title = Convert::raw2js($record->Title);
$name = Convert::raw2js($record->Name);
$saved = sprintf(_t('AssetAdmin.SAVEDFILE','Saved file %s'),"#$data[ID]");
echo <<setMarkingFilter("ClassName", "Folder");
$obj->markPartialTree();
if($p = $this->currentPage()) $obj->markToExpose($p);
// getChildrenAsUL is a flexible and complex way of traversing the tree
$siteTree = $obj->getChildrenAsUL("",
' "
Their owners have been emailed and they will fix up those pages.";
*/
$size = sizeof($ids);
if($size > 1)
$message = $size.' '._t('AssetAdmin.FOLDERSDELETED', 'folders deleted.');
else
$message = $size.' '._t('AssetAdmin.FOLDERDELETED', 'folder deleted.');
if(isset($brokenPageList))
$message .= ' '._t('AssetAdmin.NOWBROKEN', 'The following pages now have broken links:').'
'.addslashes($brokenPageList).'
'.
_t('AssetAdmin.NOWBROKEN2', 'Their owners have been emailed and they will fix up those pages.');
$script .= "statusMessage('$message');";
echo $script;
}
public function removefile(){
if($fileID = $this->urlParams['ID']){
$file = DataObject::get_by_id('File', $fileID);
// Delete the temp verions of this file in assets/_resampled
if($file instanceof Image) {
$file->deleteFormattedImages();
}
$file->delete();
$file->destroy();
if(Director::is_ajax()) {
echo <<dataFieldByName('Title')->value = $form->dataFieldByName('Name')->value;
return parent::save($urlParams, $form);
}
/**
* #################################
* Garbage collection.
* #################################
*/
/**
* Removes all unused thumbnails, and echos status message to user.
*
* @returns null
*/
public function deleteUnusedThumbnails() {
foreach($this->getUnusedThumbnailsArray() as $file) {
unlink("../assets/" . $file);
}
echo "statusMessage('"._t('AssetAdmin.THUMBSDELETED', 'All unused thumbnails have been deleted')."','good')";
}
/**
* Looks for files used in system and create where clause which contains all ID's of files.
*
* @returns String where clause which will work as filter.
*/
private function getUsedFilesList() {
$result = DB::query("SELECT DISTINCT FileID FROM SiteTree_ImageTracking");
$usedFiles = array();
$where = "";
if($result->numRecords() > 0) {
while($nextResult = $result->next()){
$where .= $nextResult['FileID'] . ',';
}
}
$classes = ClassInfo::subclassesFor('SiteTree');
foreach($classes as $className) {
$query = singleton($className)->extendedSQL();
$ids = $query->execute()->column();
if(!count($ids)) continue;
foreach(singleton($className)->has_one() as $fieldName => $joinClass) {
if($joinClass == 'Image' || $joinClass == 'File') {
foreach($ids as $id) {
$object = DataObject::get_by_id($className, $id);
if($object->$fieldName != NULL) $usedFiles[] = $object->$fieldName;
unset($object);
}
} elseif($joinClass == 'Folder') {
// @todo
}
}
}
foreach($usedFiles as $file) {
$where .= $file->ID . ',';
}
if($where == "") return "(ClassName = 'File' OR ClassName = 'Image')";
$where = substr($where,0,strlen($where)-1);
$where = "`File`.ID NOT IN (" . $where . ") AND (ClassName = 'File' OR ClassName = 'Image')";
return $where;
}
/**
* Creates table for displaying unused files.
*
* @returns AssetTableField
*/
private function getAssetList() {
$where = $this->getUsedFilesList();
$assetList = new AssetTableField(
$this,
"AssetList",
"File",
array("Title" => _t('AssetAdmin.TITLE', "Title"), "LinkedURL" => _t('AssetAdmin.FILENAME', "Filename")),
"",
$where
);
$assetList->setPopupCaption(_t('AssetAdmin.VIEWASSET', "View Asset"));
$assetList->setPermissions(array("show","delete"));
$assetList->Markable = false;
return $assetList;
}
/**
* Creates array containg all unused thumbnails.
* Array is created in three steps:
* 1.Scan assets folder and retrieve all thumbnails
* 2.Scan all HTMLField in system and retrieve thumbnails from them.
* 3.Count difference between two sets (array_diff)
*
* @returns Array
*/
private function getUnusedThumbnailsArray() {
$allThumbnails = array();
$dirIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('../assets'));
foreach ($dirIterator as $file) {
if($file->isFile()) {
if(strpos($file->getPathname(),"_resampled") !== false) {
$pathInfo = pathinfo($file->getPathname());
if(in_array(strtolower($pathInfo['extension']),array('jpeg','jpg','jpe','png','gif'))) {
$path = str_replace('\\','/',$file->getPathname());
$allThumbnails[] = substr($path,strpos($path,'/assets/')+8);
}
}
}
}
$classes = ClassInfo::subclassesFor('SiteTree');
$usedThumbnails = array();
foreach($classes as $className) {
$sng = singleton($className);
$objects = DataObject::get($className);
if($objects !== NULL) {
foreach($objects as $object) {
foreach($sng->db() as $fieldName => $fieldType) {
if($fieldType == 'HTMLText') {
$url1 = HTTP::findByTagAndAttribute($object->$fieldName,array("img" => "src"));
if($url1 != NULL) $usedThumbnails[] = substr($url1[0],strpos($url1[0],'/assets/')+8);
if($object->latestPublished > 0) {
$object = Versioned::get_latest_version($className, $object->ID);
$url2 = HTTP::findByTagAndAttribute($object->$fieldName,array("img" => "src"));
if($url2 != NULL) $usedThumbnails[] = substr($url2[0],strpos($url2[0],'/assets/')+8);
}
}
}
}
}
}
return array_diff($allThumbnails,$usedThumbnails);
}
}