2007-07-19 12:40:05 +02:00
< ? php
/**
* AssetAdmin is the 'file store' section of the CMS .
2013-10-28 03:48:31 +01:00
* It provides an interface for manipulating the File and Folder objects in the system .
2008-04-06 10:20:13 +02:00
*
2008-02-25 03:10:37 +01:00
* @ package cms
* @ subpackage assets
2007-07-19 12:40:05 +02:00
*/
2012-03-05 16:07:53 +01:00
class AssetAdmin extends LeftAndMain implements PermissionProvider {
2008-04-06 10:20:13 +02:00
2013-03-18 11:47:15 +01:00
private static $url_segment = 'assets' ;
2008-11-02 22:27:55 +01:00
2013-03-18 11:47:15 +01:00
private static $url_rule = '/$Action/$ID' ;
2008-11-02 22:27:55 +01:00
2013-03-18 11:47:15 +01:00
private static $menu_title = 'Files' ;
2008-11-02 22:27:55 +01:00
2013-03-18 11:47:15 +01:00
private static $tree_class = 'Folder' ;
2008-04-06 10:20:13 +02:00
2014-04-28 05:22:17 +02:00
/**
* Amount of results showing on a single page .
*
* @ config
* @ var int
*/
private static $page_length = 15 ;
2008-04-06 10:20:13 +02:00
/**
2013-03-18 11:47:15 +01:00
* @ config
2008-10-02 02:34:41 +02:00
* @ see Upload -> allowedMaxFileSize
2008-04-06 10:20:13 +02:00
* @ var int
*/
2013-03-18 11:47:15 +01:00
private static $allowed_max_file_size ;
2008-04-06 10:20:13 +02:00
2013-03-18 11:47:15 +01:00
private static $allowed_actions = array (
2008-02-25 03:10:37 +01:00
'addfolder' ,
2012-06-14 20:21:04 +02:00
'delete' ,
2013-02-26 01:39:48 +01:00
'AddForm' ,
2009-01-06 03:18:33 +01:00
'DeleteItemsForm' ,
2013-06-21 00:54:28 +02:00
'SearchForm' ,
2008-02-25 03:10:37 +01:00
'getsubtree' ,
'movemarked' ,
'removefile' ,
'savefile' ,
2009-11-21 04:19:05 +01:00
'deleteUnusedThumbnails' => 'ADMIN' ,
2012-03-06 06:21:21 +01:00
'doSync' ,
2012-02-21 04:02:54 +01:00
'filter' ,
2008-02-25 03:10:37 +01:00
);
2007-07-19 12:40:05 +02:00
/**
* Return fake - ID " root " if no ID is found ( needed to upload files into the root - folder )
*/
public function currentPageID () {
2012-03-08 22:18:07 +01:00
if ( is_numeric ( $this -> request -> requestVar ( 'ID' ))) {
2012-02-28 18:49:31 +01:00
return $this -> request -> requestVar ( 'ID' );
2007-07-19 12:40:05 +02:00
} elseif ( is_numeric ( $this -> urlParams [ 'ID' ])) {
return $this -> urlParams [ 'ID' ];
2012-02-28 18:49:31 +01:00
} elseif ( Session :: get ( " { $this -> class } .currentPage " )) {
return Session :: get ( " { $this -> class } .currentPage " );
2007-07-19 12:40:05 +02:00
} else {
2012-03-08 22:18:07 +01:00
return 0 ;
2007-07-19 12:40:05 +02:00
}
}
/**
* Set up the controller , in particular , re - sync the File database with the assets folder ./
*/
2012-02-03 00:11:11 +01:00
public function init () {
2007-07-19 12:40:05 +02:00
parent :: init ();
2007-10-29 03:10:59 +01:00
2009-11-21 04:20:32 +01:00
// Create base folder if it doesnt exist already
if ( ! file_exists ( ASSETS_PATH )) Filesystem :: makeFolder ( ASSETS_PATH );
ENHANCEMENT Introduced constants for system paths like /sapphire in preparation for a more flexible directory reorganisation. Instead of hardcoding your path, please use the following constants: BASE_PATH, BASE_URL, SAPPHIRE_DIR, SAPPHIRE_PATH, CMS_DIR, CMS_PATH, THIRDPARTY_DIR, THIRDPARTY_PATH, ASSETS_DIR, ASSETS_PATH, THEMES_DIR, THEMES_PATH
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@63154 467b73ca-7a2a-4603-9d3b-597d59a354a9
2008-09-27 18:02:38 +02:00
Requirements :: javascript ( CMS_DIR . " /javascript/AssetAdmin.js " );
2012-02-28 03:05:30 +01:00
Requirements :: javascript ( CMS_DIR . '/javascript/CMSMain.GridField.js' );
2012-02-23 18:39:05 +01:00
Requirements :: add_i18n_javascript ( CMS_DIR . '/javascript/lang' , false , true );
2012-02-23 20:19:03 +01:00
Requirements :: css ( CMS_DIR . " /css/screen.css " );
2012-04-12 10:36:25 +02:00
$frameworkDir = FRAMEWORK_DIR ;
2008-11-22 04:47:56 +01:00
Requirements :: customScript ( <<< JS
_TREE_ICONS = {};
_TREE_ICONS [ 'Folder' ] = {
2012-04-12 10:36:25 +02:00
fileIcon : '$frameworkDir/javascript/tree/images/page-closedfolder.gif' ,
openFolderIcon : '$frameworkDir/javascript/tree/images/page-openfolder.gif' ,
closedFolderIcon : '$frameworkDir/javascript/tree/images/page-closedfolder.gif'
2008-11-22 04:47:56 +01:00
};
JS
);
2009-11-21 04:19:05 +01:00
CMSBatchActionHandler :: register ( 'delete' , 'AssetAdmin_DeleteBatchAction' , 'Folder' );
2007-07-19 12:40:05 +02:00
}
2012-02-15 19:35:11 +01:00
2012-03-02 16:14:07 +01:00
/**
* Returns the files and subfolders contained in the currently selected folder ,
* defaulting to the root node . Doubles as search results , if any search parameters
* are set through { @ link SearchForm ()} .
*
* @ return SS_List
*/
public function getList () {
$folder = $this -> currentPage ();
$context = $this -> getSearchContext ();
2012-04-11 16:18:38 +02:00
// Overwrite name filter to search both Name and Title attributes
$context -> removeFilterByName ( 'Name' );
2012-03-02 16:14:07 +01:00
$params = $this -> request -> requestVar ( 'q' );
$list = $context -> getResults ( $params );
2012-06-05 13:42:38 +02:00
// Don't filter list when a detail view is requested,
// to avoid edge cases where the filtered list wouldn't contain the requested
// record due to faulty session state (current folder not always encoded in URL, see #7408).
2014-04-11 07:49:17 +02:00
if ( ! $folder -> ID
&& $this -> request -> requestVar ( 'ID' ) === null
&& ( $this -> request -> param ( 'ID' ) == 'field' )
) {
2012-06-05 13:42:38 +02:00
return $list ;
}
2012-04-11 16:18:38 +02:00
// Re-add previously removed "Name" filter as combined filter
// TODO Replace with composite SearchFilter once that API exists
2014-04-11 07:49:17 +02:00
if ( ! empty ( $params [ 'Name' ])) {
$list = $list -> filterAny ( array (
'Name:PartialMatch' => $params [ 'Name' ],
'Title:PartialMatch' => $params [ 'Name' ]
2012-04-11 16:18:38 +02:00
));
}
2012-03-02 16:14:07 +01:00
// Always show folders at the top
2012-06-15 05:30:08 +02:00
$list = $list -> sort ( '(CASE WHEN "File"."ClassName" = \'Folder\' THEN 0 ELSE 1 END), "Name"' );
2012-03-02 16:14:07 +01:00
// If a search is conducted, check for the "current folder" limitation.
// Otherwise limit by the current folder as denoted by the URL.
2014-04-11 07:49:17 +02:00
if ( empty ( $params ) || ! empty ( $params [ 'CurrentFolderOnly' ])) {
2012-06-15 05:30:08 +02:00
$list = $list -> filter ( 'ParentID' , $folder -> ID );
2012-03-02 16:14:07 +01:00
}
// Category filter
2014-04-11 07:49:17 +02:00
if ( ! empty ( $params [ 'AppCategory' ])
&& ! empty ( File :: config () -> app_categories [ $params [ 'AppCategory' ]])
) {
$exts = File :: config () -> app_categories [ $params [ 'AppCategory' ]];
$list = $list -> filter ( 'Name:PartialMatch' , $exts );
}
// Date filter
if ( ! empty ( $params [ 'CreatedFrom' ])) {
$fromDate = new DateField ( null , null , $params [ 'CreatedFrom' ]);
2014-08-06 03:26:23 +02:00
$list = $list -> filter ( " Created:GreaterThanOrEqual " , $fromDate -> dataValue () . ' 00:00:00' );
2014-04-11 07:49:17 +02:00
}
if ( ! empty ( $params [ 'CreatedTo' ])) {
$toDate = new DateField ( null , null , $params [ 'CreatedTo' ]);
2014-08-06 03:26:23 +02:00
$list = $list -> filter ( " Created:LessThanOrEqual " , $toDate -> dataValue () . ' 23:59:59' );
2012-03-02 16:14:07 +01:00
}
return $list ;
}
2012-02-15 19:35:11 +01:00
public function getEditForm ( $id = null , $fields = null ) {
$form = parent :: getEditForm ( $id , $fields );
2012-02-23 18:38:48 +01:00
$folder = ( $id && is_numeric ( $id )) ? DataObject :: get_by_id ( 'Folder' , $id , false ) : $this -> currentPage ();
2012-02-15 19:35:11 +01:00
$fields = $form -> Fields ();
2012-05-16 07:26:04 +02:00
$title = ( $folder && $folder -> exists ()) ? $folder -> Title : _t ( 'AssetAdmin.FILES' , 'Files' );
2012-06-14 20:21:28 +02:00
$fields -> push ( new HiddenField ( 'ID' , false , $folder ? $folder -> ID : null ));
2012-02-23 18:38:48 +01:00
// File listing
$gridFieldConfig = GridFieldConfig :: create () -> addComponents (
2012-05-16 07:26:04 +02:00
new GridFieldToolbarHeader (),
2012-02-23 18:38:48 +01:00
new GridFieldSortableHeader (),
2014-04-28 05:22:17 +02:00
new GridFieldFilterHeader (),
2012-03-09 02:08:43 +01:00
new GridFieldDataColumns (),
2014-04-28 05:22:17 +02:00
new GridFieldPaginator ( self :: config () -> page_length ),
2012-03-09 00:54:30 +01:00
new GridFieldEditButton (),
2012-03-11 23:10:25 +01:00
new GridFieldDeleteAction (),
2012-06-26 05:53:39 +02:00
new GridFieldDetailForm (),
GridFieldLevelup :: create ( $folder -> ID ) -> setLinkSpec ( 'admin/assets/show/%d' )
2012-02-23 18:38:48 +01:00
);
2012-05-16 07:26:04 +02:00
2013-05-25 03:56:02 +02:00
$gridField = GridField :: create ( 'File' , $title , $this -> getList (), $gridFieldConfig );
2012-04-19 01:53:46 +02:00
$columns = $gridField -> getConfig () -> getComponentByType ( 'GridFieldDataColumns' );
$columns -> setDisplayFields ( array (
2012-03-02 16:14:07 +01:00
'StripThumbnail' => '' ,
2012-02-23 18:38:48 +01:00
// 'Parent.FileName' => 'Folder',
'Title' => _t ( 'File.Name' ),
'Created' => _t ( 'AssetAdmin.CREATED' , 'Date' ),
'Size' => _t ( 'AssetAdmin.SIZE' , 'Size' ),
));
2012-04-19 01:53:46 +02:00
$columns -> setFieldCasting ( array (
2012-02-23 18:38:48 +01:00
'Created' => 'Date->Nice'
));
$gridField -> setAttribute (
'data-url-folder-template' ,
Controller :: join_links ( $this -> Link ( 'show' ), '%s' )
);
if ( $folder -> canCreate ()) {
$uploadBtn = new LiteralField (
'UploadButton' ,
sprintf (
2012-05-30 15:05:17 +02:00
'<a class="ss-ui-button ss-ui-action-constructive cms-panel-link" data-pjax-target="Content" data-icon="drive-upload" href="%s">%s</a>' ,
2012-02-23 18:38:48 +01:00
Controller :: join_links ( singleton ( 'CMSFileAddController' ) -> Link (), '?ID=' . $folder -> ID ),
_t ( 'Folder.UploadFilesButton' , 'Upload' )
)
);
} else {
$uploadBtn = null ;
}
if ( ! $folder -> hasMethod ( 'canAddChildren' ) || ( $folder -> hasMethod ( 'canAddChildren' ) && $folder -> canAddChildren ())) {
// TODO Will most likely be replaced by GridField logic
$addFolderBtn = new LiteralField (
'AddFolderButton' ,
sprintf (
2012-03-08 17:26:02 +01:00
'<a class="ss-ui-button ss-ui-action-constructive cms-add-folder-link" data-icon="add" data-url="%s" href="%s">%s</a>' ,
Controller :: join_links ( $this -> Link ( 'AddForm' ), '?' . http_build_query ( array (
'action_doAdd' => 1 ,
'ParentID' => $folder -> ID ,
'SecurityID' => $form -> getSecurityToken () -> getValue ()
))),
2012-02-23 18:38:48 +01:00
Controller :: join_links ( $this -> Link ( 'addfolder' ), '?ParentID=' . $folder -> ID ),
_t ( 'Folder.AddFolderButton' , 'Add folder' )
)
);
} else {
$addFolderBtn = '' ;
}
2012-03-06 06:21:21 +01:00
if ( $folder -> canEdit ()) {
$syncButton = new LiteralField (
'SyncButton' ,
sprintf (
2012-05-03 02:17:49 +02:00
'<a class="ss-ui-button ss-ui-action ui-button-text-icon-primary ss-ui-button-ajax" data-icon="arrow-circle-double" title="%s" href="%s">%s</a>' ,
2012-03-06 11:25:41 +01:00
_t ( 'AssetAdmin.FILESYSTEMSYNCTITLE' , 'Update the CMS database entries of files on the filesystem. Useful when new files have been uploaded outside of the CMS, e.g. through FTP.' ),
2012-03-06 06:21:21 +01:00
$this -> Link ( 'doSync' ),
2012-08-26 23:04:17 +02:00
_t ( 'AssetAdmin.FILESYSTEMSYNC' , 'Sync files' )
2012-03-06 06:21:21 +01:00
)
);
} else {
$syncButton = null ;
}
2012-02-23 18:38:48 +01:00
// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
// Required to keep Folder->getCMSFields() simple and reuseable,
// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
if ( ! $fields -> hasTabset ()) {
$tabs = new TabSet ( 'Root' ,
$tabList = new Tab ( 'ListView' , _t ( 'AssetAdmin.ListView' , 'List View' )),
$tabTree = new Tab ( 'TreeView' , _t ( 'AssetAdmin.TreeView' , 'Tree View' ))
);
2012-11-06 10:53:34 +01:00
$tabList -> addExtraClass ( " content-listview cms-tabset-icon list " );
$tabTree -> addExtraClass ( " content-treeview cms-tabset-icon tree " );
2012-02-23 18:38:48 +01:00
if ( $fields -> Count () && $folder -> exists ()) {
$tabs -> push ( $tabDetails = new Tab ( 'DetailsView' , _t ( 'AssetAdmin.DetailsView' , 'Details' )));
2012-11-06 10:53:34 +01:00
$tabDetails -> addExtraClass ( " content-galleryview cms-tabset-icon edit " );
2012-02-23 18:38:48 +01:00
foreach ( $fields as $field ) {
2012-04-04 02:05:38 +02:00
$fields -> removeByName ( $field -> getName ());
2012-02-23 18:38:48 +01:00
$tabDetails -> push ( $field );
}
}
$fields -> push ( $tabs );
}
2012-03-06 06:21:21 +01:00
2013-08-27 04:30:39 +02:00
// we only add buttons if they're available. User might not have permission and therefore
// the button shouldn't be available. Adding empty values into a ComposteField breaks template rendering.
$actionButtonsComposite = CompositeField :: create () -> addExtraClass ( 'cms-actions-row' );
if ( $uploadBtn ) $actionButtonsComposite -> push ( $uploadBtn );
if ( $addFolderBtn ) $actionButtonsComposite -> push ( $addFolderBtn );
if ( $syncButton ) $actionButtonsComposite -> push ( $syncButton );
2012-02-23 18:38:48 +01:00
// List view
$fields -> addFieldsToTab ( 'Root.ListView' , array (
2012-04-04 16:59:22 +02:00
$actionsComposite = CompositeField :: create (
2013-08-27 04:30:39 +02:00
$actionButtonsComposite
2012-02-23 18:38:48 +01:00
) -> addExtraClass ( 'cms-content-toolbar field' ),
$gridField
));
2012-04-12 03:04:01 +02:00
$treeField = new LiteralField ( 'Tree' , '' );
2012-02-23 18:38:48 +01:00
// Tree view
$fields -> addFieldsToTab ( 'Root.TreeView' , array (
clone $actionsComposite ,
2012-02-15 19:35:11 +01:00
// TODO Replace with lazy loading on client to avoid performance hit of rendering potentially unused views
new LiteralField (
'Tree' ,
2012-11-15 02:32:52 +01:00
FormField :: create_tag (
2012-02-15 19:35:11 +01:00
'div' ,
array (
'class' => 'cms-tree' ,
2012-02-23 18:39:05 +01:00
'data-url-tree' => $this -> Link ( 'getsubtree' ),
2012-02-15 19:35:11 +01:00
'data-url-savetreenode' => $this -> Link ( 'savetreenode' )
),
$this -> SiteTreeAsUL ()
)
)
2012-02-23 18:38:48 +01:00
));
2012-02-15 19:35:11 +01:00
2012-05-02 17:50:08 +02:00
// Move actions to "details" tab (they don't make sense on list/tree view)
$actions = $form -> Actions ();
$saveBtn = $actions -> fieldByName ( 'action_save' );
$deleteBtn = $actions -> fieldByName ( 'action_delete' );
2015-04-17 01:16:38 +02:00
$actions -> removeByName ( 'action_save' );
$actions -> removeByName ( 'action_delete' );
2012-05-02 17:50:08 +02:00
if (( $saveBtn || $deleteBtn ) && $fields -> fieldByName ( 'Root.DetailsView' )) {
$fields -> addFieldToTab (
'Root.DetailsView' ,
CompositeField :: create ( $saveBtn , $deleteBtn ) -> addExtraClass ( 'Actions' )
);
}
2012-02-23 18:38:48 +01:00
$fields -> setForm ( $form );
2012-02-15 19:35:11 +01:00
$form -> setTemplate ( $this -> getTemplatesWithSuffix ( '_EditForm' ));
// TODO Can't merge $FormAttributes in template at the moment
2012-03-07 17:02:08 +01:00
$form -> addExtraClass ( 'cms-edit-form cms-panel-padded center ' . $this -> BaseCSSClasses ());
2012-05-30 15:05:17 +02:00
$form -> setAttribute ( 'data-pjax-fragment' , 'CurrentForm' );
2012-02-23 18:38:48 +01:00
$form -> Fields () -> findOrMakeTab ( 'Root' ) -> setTemplate ( 'CMSTabSet' );
$this -> extend ( 'updateEditForm' , $form );
2012-02-15 19:35:11 +01:00
return $form ;
}
2012-02-23 18:39:05 +01:00
public function addfolder ( $request ) {
$obj = $this -> customise ( array (
'EditForm' => $this -> AddForm ()
));
2012-04-05 14:45:26 +02:00
if ( $request -> isAjax ()) {
2012-02-23 18:39:05 +01:00
// Rendering is handled by template, which will call EditForm() eventually
$content = $obj -> renderWith ( $this -> getTemplatesWithSuffix ( '_Content' ));
} else {
$content = $obj -> renderWith ( $this -> getViewer ( 'show' ));
}
return $content ;
}
2012-03-02 16:14:07 +01:00
2012-06-14 20:21:04 +02:00
public function delete ( $data , $form ) {
$className = $this -> stat ( 'tree_class' );
$record = DataObject :: get_by_id ( $className , Convert :: raw2sql ( $data [ 'ID' ]));
if ( $record && ! $record -> canDelete ()) return Security :: permissionFailure ();
if ( ! $record || ! $record -> ID ) throw new HTTPResponse_Exception ( " Bad record ID # " . ( int ) $data [ 'ID' ], 404 );
$parentID = $record -> ParentID ;
$record -> delete ();
$this -> setCurrentPageID ( null );
$this -> response -> addHeader ( 'X-Status' , rawurlencode ( _t ( 'LeftAndMain.DELETED' , 'Deleted.' )));
$this -> response -> addHeader ( 'X-Pjax' , 'Content' );
return $this -> redirect ( Controller :: join_links ( $this -> Link ( 'show' ), $parentID ? $parentID : 0 ));
}
2014-08-06 03:26:23 +02:00
/**
* Get the search context
*
* @ return SearchContext
*/
2012-03-02 16:14:07 +01:00
public function getSearchContext () {
$context = singleton ( 'File' ) -> getDefaultSearchContext ();
// Namespace fields, for easier detection if a search is present
foreach ( $context -> getFields () as $field ) $field -> setName ( sprintf ( 'q[%s]' , $field -> getName ()));
foreach ( $context -> getFilters () as $filter ) $filter -> setFullName ( sprintf ( 'q[%s]' , $filter -> getFullName ()));
2012-05-23 12:43:42 +02:00
// Customize fields
2014-04-11 07:49:17 +02:00
$context -> addField (
new HeaderField ( 'q[Date]' , _t ( 'CMSSearch.FILTERDATEHEADING' , 'Date' ), 4 )
);
$context -> addField (
DateField :: create (
'q[CreatedFrom]' ,
_t ( 'CMSSearch.FILTERDATEFROM' , 'From' )
) -> setConfig ( 'showcalendar' , true )
);
$context -> addField (
DateField :: create (
'q[CreatedTo]' ,
_t ( 'CMSSearch.FILTERDATETO' , 'To' )
) -> setConfig ( 'showcalendar' , true )
);
2012-03-02 16:14:07 +01:00
$appCategories = array (
'image' => _t ( 'AssetAdmin.AppCategoryImage' , 'Image' ),
'audio' => _t ( 'AssetAdmin.AppCategoryAudio' , 'Audio' ),
'mov' => _t ( 'AssetAdmin.AppCategoryVideo' , 'Video' ),
2012-04-13 21:54:56 +02:00
'flash' => _t ( 'AssetAdmin.AppCategoryFlash' , 'Flash' , 'The fileformat' ),
'zip' => _t ( 'AssetAdmin.AppCategoryArchive' , 'Archive' , 'A collection of files' ),
2012-09-04 01:26:09 +02:00
'doc' => _t ( 'AssetAdmin.AppCategoryDocument' , 'Document' )
2012-03-02 16:14:07 +01:00
);
$context -> addField (
2012-05-23 12:43:42 +02:00
$typeDropdown = new DropdownField (
2012-03-02 16:14:07 +01:00
'q[AppCategory]' ,
_t ( 'AssetAdmin.Filetype' , 'File type' ),
2012-05-23 12:43:42 +02:00
$appCategories
2012-03-02 16:14:07 +01:00
)
);
2012-05-23 12:43:42 +02:00
$typeDropdown -> setEmptyString ( ' ' );
2012-03-02 16:14:07 +01:00
$context -> addField (
2012-05-23 12:43:42 +02:00
new CheckboxField ( 'q[CurrentFolderOnly]' , _t ( 'AssetAdmin.CurrentFolderOnly' , 'Limit to current folder?' ))
2012-03-02 16:14:07 +01:00
);
$context -> getFields () -> removeByName ( 'q[Title]' );
return $context ;
}
2011-03-15 10:30:28 +01:00
2012-02-21 04:02:54 +01:00
/**
2012-03-02 16:14:07 +01:00
* Returns a form for filtering of files and assets gridfield .
* Result filtering takes place in { @ link getList ()} .
2012-02-21 04:02:54 +01:00
*
* @ return Form
* @ see AssetAdmin . js
*/
2012-03-02 16:14:07 +01:00
public function SearchForm () {
$folder = $this -> currentPage ();
$context = $this -> getSearchContext ();
$fields = $context -> getSearchFields ();
2012-02-29 21:06:21 +01:00
$actions = new FieldList (
2013-08-03 20:18:28 +02:00
FormAction :: create ( 'doSearch' , _t ( 'CMSMain_left_ss.APPLY_FILTER' , 'Apply Filter' ))
2014-04-11 07:49:17 +02:00
-> addExtraClass ( 'ss-ui-action-constructive' ),
2013-08-03 20:18:28 +02:00
Object :: create ( 'ResetFormAction' , 'clear' , _t ( 'CMSMain_left_ss.RESET' , 'Reset' ))
2012-02-29 21:06:21 +01:00
);
2012-02-21 04:02:54 +01:00
$form = new Form ( $this , 'filter' , $fields , $actions );
2012-03-02 16:14:07 +01:00
$form -> setFormMethod ( 'GET' );
2012-03-08 22:18:07 +01:00
$form -> setFormAction ( Controller :: join_links ( $this -> Link ( 'show' ), $folder -> ID ));
2012-03-02 16:14:07 +01:00
$form -> addExtraClass ( 'cms-search-form' );
$form -> loadDataFrom ( $this -> request -> getVars ());
$form -> disableSecurityToken ();
2012-02-21 04:02:54 +01:00
// This have to match data-name attribute on the gridfield so that the javascript selectors work
$form -> setAttribute ( 'data-gridfield' , 'File' );
return $form ;
}
2012-02-03 00:11:11 +01:00
public function AddForm () {
2012-02-23 18:39:05 +01:00
$folder = singleton ( 'Folder' );
2013-05-10 15:00:57 +02:00
$form = CMSForm :: create (
2012-03-09 23:35:43 +01:00
$this ,
'AddForm' ,
new FieldList (
new TextField ( " Name " , _t ( 'File.Name' )),
new HiddenField ( 'ParentID' , false , $this -> request -> getVar ( 'ParentID' ))
),
new FieldList (
2013-08-03 20:18:28 +02:00
FormAction :: create ( 'doAdd' , _t ( 'AssetAdmin_left_ss.GO' , 'Go' ))
2012-03-09 23:35:43 +01:00
-> addExtraClass ( 'ss-ui-action-constructive' ) -> setAttribute ( 'data-icon' , 'accept' )
-> setTitle ( _t ( 'AssetAdmin.ActionAdd' , 'Add folder' ))
)
2013-05-10 15:00:57 +02:00
) -> setHTMLID ( 'Form_AddForm' );
$form -> setResponseNegotiator ( $this -> getResponseNegotiator ());
2012-02-23 18:39:05 +01:00
$form -> setTemplate ( $this -> getTemplatesWithSuffix ( '_EditForm' ));
2012-03-08 17:26:02 +01:00
// TODO Can't merge $FormAttributes in template at the moment
2012-03-09 23:35:43 +01:00
$form -> addExtraClass ( 'add-form cms-add-form cms-edit-form cms-panel-padded center ' . $this -> BaseCSSClasses ());
2011-04-17 10:01:54 +02:00
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-04-19 11:16:03 +02:00
2011-03-15 10:30:28 +01:00
// check addchildren permissions
if (
2011-04-19 11:16:03 +02:00
singleton ( $class ) -> hasExtension ( 'Hierarchy' )
2011-03-15 10:30:28 +01:00
&& isset ( $data [ 'ParentID' ])
&& is_numeric ( $data [ 'ParentID' ])
2012-03-08 17:26:02 +01:00
&& $data [ 'ParentID' ]
2011-03-15 10:30:28 +01:00
) {
$parentRecord = DataObject :: get_by_id ( $class , $data [ 'ParentID' ]);
if (
$parentRecord -> hasMethod ( 'canAddChildren' )
&& ! $parentRecord -> canAddChildren ()
) return Security :: permissionFailure ( $this );
2012-03-08 17:26:02 +01:00
} else {
$parentRecord = null ;
2011-03-15 10:30:28 +01:00
}
2012-02-23 18:39:05 +01:00
2011-03-15 10:30:28 +01:00
$parent = ( isset ( $data [ 'ParentID' ]) && is_numeric ( $data [ 'ParentID' ])) ? ( int ) $data [ 'ParentID' ] : 0 ;
$name = ( isset ( $data [ 'Name' ])) ? basename ( $data [ 'Name' ]) : _t ( 'AssetAdmin.NEWFOLDER' , " NewFolder " );
2012-03-08 17:26:02 +01:00
if ( ! $parentRecord || ! $parentRecord -> ID ) $parent = 0 ;
2011-03-15 10:30:28 +01:00
// Get the folder to be created
2012-03-08 17:26:02 +01:00
if ( $parentRecord && $parentRecord -> ID ) $filename = $parentRecord -> FullPath . $name ;
2011-03-15 10:30:28 +01:00
else $filename = ASSETS_PATH . '/' . $name ;
// Actually create
if ( ! file_exists ( ASSETS_PATH )) {
mkdir ( ASSETS_PATH );
}
$record = new Folder ();
$record -> ParentID = $parent ;
2012-02-23 18:39:05 +01:00
$record -> Name = $record -> Title = basename ( $filename );
2011-03-15 10:30:28 +01:00
// 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 );
2013-03-18 11:47:15 +01:00
chmod ( $record -> FullPath , Filesystem :: config () -> file_create_mask );
2011-03-15 10:30:28 +01:00
2012-03-26 22:54:47 +02:00
if ( $parentRecord ) {
return $this -> redirect ( Controller :: join_links ( $this -> Link ( 'show' ), $parentRecord -> ID ));
} else {
return $this -> redirect ( $this -> Link ());
}
2011-03-15 10:30:28 +01:00
}
2009-04-29 03:44:28 +02:00
/**
* Custom currentPage () method to handle opening the 'root' folder
*/
public function currentPage () {
$id = $this -> currentPageID ();
2012-03-08 22:18:07 +01:00
if ( $id && is_numeric ( $id ) && $id > 0 ) {
2012-09-21 05:35:53 +02:00
$folder = DataObject :: get_by_id ( 'Folder' , $id );
if ( $folder && $folder -> exists ()) {
return $folder ;
}
2009-04-29 03:44:28 +02:00
}
2013-05-27 07:21:07 +02:00
$this -> setCurrentPageID ( null );
2012-09-21 05:35:53 +02:00
return new Folder ();
2009-04-29 03:44:28 +02:00
}
2009-11-21 04:21:00 +01:00
2012-09-19 12:07:46 +02:00
public function getSiteTreeFor ( $className , $rootID = null , $childrenMethod = null , $numChildrenMethod = null , $filterFunction = null , $minNodeCount = 30 ) {
2009-11-21 04:21:03 +01:00
if ( ! $childrenMethod ) $childrenMethod = 'ChildFolders' ;
2010-04-12 11:22:43 +02:00
return parent :: getSiteTreeFor ( $className , $rootID , $childrenMethod , $numChildrenMethod , $filterFunction , $minNodeCount );
2009-11-21 04:21:03 +01:00
}
2009-11-21 04:21:00 +01:00
public function getCMSTreeTitle () {
return Director :: absoluteBaseURL () . " assets " ;
}
2007-07-19 12:40:05 +02:00
public function SiteTreeAsUL () {
2011-03-15 10:30:28 +01:00
return $this -> getSiteTreeFor ( $this -> stat ( 'tree_class' ), null , 'ChildFolders' );
2007-07-19 12:40:05 +02:00
}
//------------------------------------------------------------------------------------------//
// Data saving handlers
2012-03-06 06:21:21 +01:00
2009-11-21 04:19:02 +01:00
/**
2012-03-06 06:21:21 +01:00
* Can be queried with an ajax request to trigger the filesystem sync . It returns a FormResponse status message
* to display in the CMS
2009-11-21 04:19:02 +01:00
*/
2012-03-06 06:21:21 +01:00
public function doSync () {
$message = Filesystem :: sync ();
2012-05-14 15:11:35 +02:00
$this -> response -> addHeader ( 'X-Status' , rawurlencode ( $message ));
2012-03-24 02:29:37 +01:00
2012-03-09 23:20:09 +01:00
return ;
2011-03-15 22:13:49 +01:00
}
2007-10-02 23:34:57 +02:00
/**
* #################################
* Garbage collection .
* #################################
*/
/**
2009-02-03 04:46:15 +01:00
* Removes all unused thumbnails from the file store
* and returns the status of the process to the user .
2008-12-04 23:38:58 +01:00
*/
2010-11-01 02:29:02 +01:00
public function deleteunusedthumbnails ( $request ) {
// Protect against CSRF on destructive action
if ( ! SecurityToken :: inst () -> checkRequest ( $request )) return $this -> httpError ( 400 );
2009-02-03 04:46:15 +01:00
$count = 0 ;
$thumbnails = $this -> getUnusedThumbnails ();
if ( $thumbnails ) {
foreach ( $thumbnails as $thumbnail ) {
unlink ( ASSETS_PATH . " / " . $thumbnail );
$count ++ ;
}
}
2012-05-01 21:43:43 +02:00
$message = _t (
'AssetAdmin.THUMBSDELETED' ,
'{count} unused thumbnails have been deleted' ,
array ( 'count' => $count )
);
2012-05-14 15:11:35 +02:00
$this -> response -> addHeader ( 'X-Status' , rawurlencode ( $message ));
2012-03-09 23:20:09 +01:00
return ;
2007-10-02 23:34:57 +02:00
}
/**
2008-12-04 23:38:58 +01:00
* Creates array containg all unused thumbnails .
*
* Array is created in three steps :
2009-02-03 04:46:15 +01:00
* 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 )
2008-12-04 23:38:58 +01:00
*
* @ return array
*/
2009-02-03 04:46:15 +01:00
private function getUnusedThumbnails () {
2008-12-04 23:38:58 +01:00
$allThumbnails = array ();
$usedThumbnails = array ();
$dirIterator = new RecursiveIteratorIterator ( new RecursiveDirectoryIterator ( ASSETS_PATH ));
2009-02-03 04:46:15 +01:00
$classes = ClassInfo :: subclassesFor ( 'SiteTree' );
2008-12-04 23:38:58 +01:00
2009-02-03 04:46:15 +01:00
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 );
}
2008-12-04 23:38:58 +01:00
}
}
}
}
2009-02-03 04:46:15 +01:00
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 );
}
}
2008-12-04 23:38:58 +01:00
}
}
}
}
}
}
2009-02-03 04:46:15 +01:00
return array_diff ( $allThumbnails , $usedThumbnails );
2008-12-04 23:38:58 +01:00
}
2012-02-28 20:11:07 +01:00
/**
2014-02-10 21:35:13 +01:00
* @ param bool $unlinked
2012-02-28 20:11:07 +01:00
* @ return ArrayList
*/
public function Breadcrumbs ( $unlinked = false ) {
$items = parent :: Breadcrumbs ( $unlinked );
2012-03-08 22:18:07 +01:00
// The root element should explicitly point to the root node.
// Uses session state for current record otherwise.
$items [ 0 ] -> Link = Controller :: join_links ( singleton ( 'AssetAdmin' ) -> Link ( 'show' ), 0 );
2012-03-02 16:14:07 +01:00
// If a search is in progress, don't show the path
if ( $this -> request -> requestVar ( 'q' )) {
2012-03-09 01:53:28 +01:00
$items = $items -> limit ( 1 );
2012-03-02 16:14:07 +01:00
$items -> push ( new ArrayData ( array (
'Title' => _t ( 'LeftAndMain.SearchResults' , 'Search Results' ),
'Link' => Controller :: join_links ( $this -> Link (), '?' . http_build_query ( array ( 'q' => $this -> request -> requestVar ( 'q' ))))
)));
}
2012-03-08 17:26:02 +01:00
// If we're adding a folder, note that in breadcrumbs as well
if ( $this -> request -> param ( 'Action' ) == 'addfolder' ) {
$items -> push ( new ArrayData ( array (
'Title' => _t ( 'Folder.AddFolderButton' , 'Add folder' ),
'Link' => false
)));
}
2012-02-28 20:11:07 +01:00
return $items ;
}
2012-03-05 16:07:53 +01:00
2012-09-19 12:07:46 +02:00
public function providePermissions () {
2012-03-05 16:07:53 +01:00
$title = _t ( " AssetAdmin.MENUTITLE " , LeftAndMain :: menu_title_for_class ( $this -> class ));
return array (
" CMS_ACCESS_AssetAdmin " => array (
2012-05-01 21:43:43 +02:00
'name' => _t ( 'CMSMain.ACCESS' , " Access to ' { title}' section " , array ( 'title' => $title )),
2012-03-05 16:07:53 +01:00
'category' => _t ( 'Permission.CMS_ACCESS_CATEGORY' , 'CMS Access' )
)
);
}
2009-02-03 04:46:15 +01:00
2007-11-01 21:58:28 +01:00
}
2009-11-21 04:19:05 +01:00
/**
* 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 {
2012-02-03 00:11:11 +01:00
public function getActionTitle () {
2013-08-03 20:18:28 +02:00
// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
2009-11-21 04:19:05 +01:00
return _t ( 'AssetAdmin_DeleteBatchAction.TITLE' , 'Delete folders' );
}
2012-02-03 00:11:11 +01:00
public function run ( SS_List $records ) {
2009-11-21 04:19:05 +01:00
$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 );
}
}
2012-02-13 21:40:49 +01:00