silverstripe-reports/code/controllers/AssetAdmin.php

357 lines
10 KiB
PHP
Raw Normal View History

<?php
/**
* AssetAdmin is the 'file store' section of the CMS.
* It provides an interface for maniupating the File and Folder objects in the system.
*
* @package cms
* @subpackage assets
*/
class AssetAdmin extends LeftAndMain {
static $url_segment = 'assets';
static $url_rule = '/$Action/$ID';
2011-03-31 11:10:44 +02:00
static $menu_title = 'Files';
public static $tree_class = 'Folder';
/**
* @see Upload->allowedMaxFileSize
* @var int
*/
public static $allowed_max_file_size;
public static $allowed_actions = array(
'addfolder',
'DeleteItemsForm',
'getsubtree',
'movemarked',
'removefile',
'savefile',
'deleteUnusedThumbnails' => 'ADMIN',
2011-03-15 10:30:28 +01:00
'SyncForm',
);
/**
* 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./
*/
public function init() {
parent::init();
// Create base folder if it doesnt exist already
if(!file_exists(ASSETS_PATH)) Filesystem::makeFolder(ASSETS_PATH);
Requirements::javascript(CMS_DIR . "/javascript/AssetAdmin.js");
Requirements::css(CMS_DIR . "/css/AssetAdmin.css");
Requirements::customScript(<<<JS
_TREE_ICONS = {};
_TREE_ICONS['Folder'] = {
fileIcon: 'sapphire/javascript/tree/images/page-closedfolder.gif',
openFolderIcon: 'sapphire/javascript/tree/images/page-openfolder.gif',
closedFolderIcon: 'sapphire/javascript/tree/images/page-closedfolder.gif'
};
JS
);
CMSBatchActionHandler::register('delete', 'AssetAdmin_DeleteBatchAction', 'Folder');
}
public function getEditForm($id = null, $fields = null) {
$form = parent::getEditForm($id, $fields);
$fields = $form->Fields();
$fields->findOrMakeTab('Root.TreeView', _t('AssetAdmin.TreeView', 'Tree View'));
$fields->addFieldToTab('Root.TreeView',
// TODO Replace with lazy loading on client to avoid performance hit of rendering potentially unused views
new LiteralField(
'Tree',
FormField::createTag(
'div',
array(
'class' => 'cms-tree',
'data-url' => $this->Link('getsubtree'),
'data-url-savetreenode' => $this->Link('savetreenode')
),
$this->SiteTreeAsUL()
)
)
);
$form->addExtraClass('cms-edit-form');
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
// TODO Can't merge $FormAttributes in template at the moment
$form->addExtraClass('center ss-tabset ' . $this->BaseCSSClasses());
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
return $form;
}
2011-03-15 10:30:28 +01:00
public function AddForm() {
$form = parent::AddForm();
$form->Actions()->fieldByName('action_doAdd')->setTitle(_t('AssetAdmin.ActionAdd', 'Add folder'));
return $form;
}
2011-03-15 10:30:28 +01:00
/**
* Add a new group and return its details suitable for ajax.
*
* @todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
*/
public function doAdd($data, $form) {
$class = $this->stat('tree_class');
// check create permissions
if(!singleton($class)->canCreate()) return Security::permissionFailure($this);
2011-03-15 10:30:28 +01:00
// check addchildren permissions
if(
singleton($class)->hasExtension('Hierarchy')
2011-03-15 10:30:28 +01:00
&& isset($data['ParentID'])
&& is_numeric($data['ParentID'])
) {
$parentRecord = DataObject::get_by_id($class, $data['ParentID']);
if(
$parentRecord->hasMethod('canAddChildren')
&& !$parentRecord->canAddChildren()
) return Security::permissionFailure($this);
}
$parent = (isset($data['ParentID']) && is_numeric($data['ParentID'])) ? (int)$data['ParentID'] : 0;
$name = (isset($data['Name'])) ? basename($data['Name']) : _t('AssetAdmin.NEWFOLDER',"NewFolder");
if(!isset($parentRecord) || !$parentRecord->ID) $parent = 0;
// Get the folder to be created
if(isset($parentRecord->ID)) $filename = $parentRecord->FullPath . $name;
else $filename = ASSETS_PATH . '/' . $name;
// Actually create
if(!file_exists(ASSETS_PATH)) {
mkdir(ASSETS_PATH);
}
$record = new Folder();
$record->ParentID = $parent;
// Ensure uniqueness
$i = 2;
$baseFilename = substr($record->Filename, 0, -1) . '-';
while(file_exists($record->FullPath)) {
$record->Filename = $baseFilename . $i . '/';
$i++;
}
$record->Name = $record->Title = basename($record->Filename);
$record->write();
mkdir($record->FullPath);
chmod($record->FullPath, Filesystem::$file_create_mask);
// Used in TinyMCE inline folder creation
if(isset($data['returnID'])) {
return $record->ID;
} else if($this->isAjax()) {
$form = $this->getEditForm($record->ID);
return $form->forTemplate();
2011-03-15 10:30:28 +01:00
} else {
return $this->redirect(Controller::join_links($this->Link('show'), $record->ID));
}
}
/**
* Custom currentPage() method to handle opening the 'root' folder
*/
public function currentPage() {
$id = $this->currentPageID();
if($id && is_numeric($id)) {
return DataObject::get_by_id('File', $id);
} else if($id == 'root') {
return singleton('File');
}
}
function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
if (!$childrenMethod) $childrenMethod = 'ChildFolders';
return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount);
}
public function getCMSTreeTitle() {
return Director::absoluteBaseURL() . "assets";
}
public function SiteTreeAsUL() {
2011-03-15 10:30:28 +01:00
return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders');
}
//------------------------------------------------------------------------------------------//
// Data saving handlers
/**
* @return Form
*/
public function SyncForm() {
$form = new Form(
$this,
'SyncForm',
new FieldList(
),
new FieldList(
$btn = new FormAction('doSync', _t('FILESYSTEMSYNC','Look for new files'))
)
);
$form->setFormMethod('GET');
$btn->describe(_t('AssetAdmin_left.ss.FILESYSTEMSYNC_DESC', 'SilverStripe maintains its own database of the files &amp; images stored in your assets/ folder. Click this button to update that database, if files are added to the assets/ folder from outside SilverStripe, for example, if you have uploaded files via FTP.'));
return $form;
}
public function doSync($data, $form) {
return Filesystem::sync();
}
/**
* #################################
* Garbage collection.
* #################################
*/
/**
* Removes all unused thumbnails from the file store
* and returns the status of the process to the user.
*/
public function deleteunusedthumbnails($request) {
// Protect against CSRF on destructive action
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
$count = 0;
$thumbnails = $this->getUnusedThumbnails();
if($thumbnails) {
foreach($thumbnails as $thumbnail) {
unlink(ASSETS_PATH . "/" . $thumbnail);
$count++;
}
}
$message = sprintf(_t('AssetAdmin.THUMBSDELETED', '%s unused thumbnails have been deleted'), $count);
FormResponse::status_message($message, 'good');
echo FormResponse::respond();
}
/**
* 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)
*
* @return array
*/
private function getUnusedThumbnails() {
$allThumbnails = array();
$usedThumbnails = array();
$dirIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(ASSETS_PATH));
$classes = ClassInfo::subclassesFor('SiteTree');
if($dirIterator) {
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);
}
}
}
}
}
if($classes) {
foreach($classes as $className) {
$SNG_class = singleton($className);
$objects = DataObject::get($className);
if($objects !== NULL) {
foreach($objects as $object) {
foreach($SNG_class->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);
}
}
/**
* Delete multiple {@link Folder} records (and the associated filesystem nodes).
* Usually used through the {@link AssetAdmin} interface.
*
* @package cms
* @subpackage batchactions
*/
class AssetAdmin_DeleteBatchAction extends CMSBatchAction {
public function getActionTitle() {
// _t('AssetAdmin_left.ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
}
public function run(SS_List $records) {
$status = array(
'modified'=>array(),
'deleted'=>array()
);
foreach($records as $record) {
$id = $record->ID;
// Perform the action
if($record->canDelete()) $record->delete();
$status['deleted'][$id] = array();
$record->destroy();
unset($record);
}
return Convert::raw2json($status);
}
}