Implemented file and folder renaming
This commit is contained in:
parent
331276c244
commit
cd0b107a2b
|
|
@ -211,6 +211,79 @@ class MediaLibrary
|
|||
return $this->getStorageDisk()->put($fullPath, $contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file to another location.
|
||||
* @param string $oldPath Specifies the original path of the file.
|
||||
* @param string $newPath Specifies the new path of the file.
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveFile($oldPath, $newPath, $isRename = false)
|
||||
{
|
||||
$oldPath = self::validatePath($oldPath);
|
||||
$fullOldPath = $this->getMediaPath($oldPath);
|
||||
|
||||
$newPath = self::validatePath($newPath);
|
||||
$fullNewPath = $this->getMediaPath($newPath);
|
||||
|
||||
return $this->getStorageDisk()->move($fullOldPath, $fullNewPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a folder.
|
||||
* @param string $originalPath Specifies the original path of the folder.
|
||||
* @param string $newPath Specifies the new path of the folder.
|
||||
* @return boolean
|
||||
*/
|
||||
public function copyFolder($originalPath, $newPath)
|
||||
{
|
||||
$disk = $this->getStorageDisk();
|
||||
|
||||
$copyDirectory = function($srcPath, $destPath) use (&$copyDirectory, $disk) {
|
||||
$srcPath = self::validatePath($srcPath);
|
||||
$fullSrcPath = $this->getMediaPath($srcPath);
|
||||
|
||||
$destPath = self::validatePath($destPath);
|
||||
$fullDestPath = $this->getMediaPath($destPath);
|
||||
|
||||
if (!$disk->makeDirectory($fullDestPath))
|
||||
return false;
|
||||
|
||||
$folderContents = $this->scanFolderContents($fullSrcPath);
|
||||
|
||||
foreach ($folderContents['folders'] as $dirInfo) {
|
||||
if (!$copyDirectory($dirInfo->path, $destPath.'/'.basename($dirInfo->path)))
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($folderContents['files'] as $fileInfo) {
|
||||
$fullFileSrcPath = $this->getMediaPath($fileInfo->path);
|
||||
|
||||
if (!$disk->copy($fullFileSrcPath, $fullDestPath.'/'.basename($fileInfo->path)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return $copyDirectory($originalPath, $newPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a folder.
|
||||
* @param string $originalPath Specifies the original path of the folder.
|
||||
* @param string $newPath Specifies the new path of the folder.
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveFolder($originalPath, $newPath)
|
||||
{
|
||||
if (!$this->copyFolder($originalPath, $newPath))
|
||||
return false;
|
||||
|
||||
$this->deleteFolder($originalPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the Library cache.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -262,5 +262,6 @@ return [
|
|||
'no_files_found' => 'No files found by your request.',
|
||||
'delete_empty' => 'Please select files to delete.',
|
||||
'delete_confirm' => 'Do you really want to delete the selected file(s)?',
|
||||
'error_renaming_file' => 'Error renaming file.'
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ class MediaManager extends WidgetBase
|
|||
if (count($filesToDelete) > 0)
|
||||
$library->deleteFiles($filesToDelete);
|
||||
|
||||
MediaLibrary::instance()->resetCache();
|
||||
$library->resetCache();
|
||||
$this->prepareVars();
|
||||
|
||||
return [
|
||||
|
|
@ -219,6 +219,42 @@ class MediaManager extends WidgetBase
|
|||
];
|
||||
}
|
||||
|
||||
public function onLoadRenamePopup()
|
||||
{
|
||||
$path = Input::get('path');
|
||||
$path = MediaLibrary::validatePath($path);
|
||||
|
||||
$this->vars['originalPath'] = $path;
|
||||
$this->vars['name'] = basename($path);
|
||||
$this->vars['listId'] = Input::get('listId');
|
||||
$this->vars['type'] = Input::get('type');
|
||||
return $this->makePartial('rename_form');
|
||||
}
|
||||
|
||||
public function onApplyName()
|
||||
{
|
||||
$newName = trim(Input::get('name'));
|
||||
if (!strlen($newName))
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty'));
|
||||
|
||||
if (!$this->validateFileName($newName))
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name'));
|
||||
|
||||
$originalPath = Input::get('originalPath');
|
||||
$originalPath = MediaLibrary::validatePath($originalPath);
|
||||
|
||||
$newPath = dirname($originalPath).'/'.$newName;
|
||||
|
||||
$type = Input::get('type');
|
||||
|
||||
if ($type == MediaLibraryItem::TYPE_FILE)
|
||||
MediaLibrary::instance()->moveFile($originalPath, $newPath);
|
||||
else
|
||||
MediaLibrary::instance()->moveFolder($originalPath, $newPath);
|
||||
|
||||
MediaLibrary::instance()->resetCache();
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for th internal use
|
||||
//
|
||||
|
|
@ -338,14 +374,18 @@ class MediaManager extends WidgetBase
|
|||
protected function splitPathToSegments($path)
|
||||
{
|
||||
$path = MediaLibrary::validatePath($path, true);
|
||||
$path = explode('/', ltrim($path, '/'));
|
||||
|
||||
$path = ltrim($path, '/');
|
||||
$result = [];
|
||||
while (count($path) > 0) {
|
||||
$folder = array_pop($path);
|
||||
|
||||
$result = explode('/', $path);
|
||||
if (count($result) == 1 && $result[0] == '')
|
||||
$result = [];
|
||||
$result[$folder] = implode('/', $path).'/'.$folder;
|
||||
if (substr($result[$folder], 0, 1) != '/')
|
||||
$result[$folder] = '/'.$result[$folder];
|
||||
}
|
||||
|
||||
return $result;
|
||||
return array_reverse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -618,4 +658,15 @@ class MediaManager extends WidgetBase
|
|||
die();
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateFileName($name)
|
||||
{
|
||||
if (!preg_match('/^[0-9a-z\.\s_\-]+$/i', $name))
|
||||
return false;
|
||||
|
||||
if (strpos($name, '..') !== false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,9 +60,27 @@ div[data-control="media-manager"] .media-list li h4 {
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 15px;
|
||||
line-height: 150%;
|
||||
margin: 15px 0 5px 0;
|
||||
padding-right: 0;
|
||||
-webkit-transition: padding 0.1s;
|
||||
transition: padding 0.1s;
|
||||
position: relative;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list li h4 a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 15px;
|
||||
color: #2b3e50;
|
||||
display: none;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list li h4 a:hover {
|
||||
color: #0181b9;
|
||||
text-decoration: none;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list li:hover h4 a {
|
||||
display: block;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list li p.size {
|
||||
font-size: 12px;
|
||||
|
|
@ -147,6 +165,15 @@ div[data-control="media-manager"] .media-list.list li.selected h4 {
|
|||
div[data-control="media-manager"] .media-list.list li.selected .icon-container {
|
||||
border-right-color: #4da7e8 !important;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.list h4 {
|
||||
padding-right: 15px;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.list h4 a {
|
||||
right: 15px;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.list li:hover h4 {
|
||||
padding-right: 35px;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.tiles li {
|
||||
width: 167px;
|
||||
margin-bottom: 25px;
|
||||
|
|
@ -200,12 +227,18 @@ div[data-control="media-manager"] .media-list.tiles li.selected .icon-container
|
|||
div[data-control="media-manager"] .media-list.tiles li.selected h4 {
|
||||
color: #2581b8;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.tiles li:hover h4 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.tiles i.icon-chain-broken {
|
||||
margin-top: 47px;
|
||||
}
|
||||
div[data-control="media-manager"] .media-list.tiles p.size {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div[data-control="media-manager"] [data-control="sidebar-labels"] {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
div[data-control="media-manager"] .sidebar-image-placeholder-container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
|
@ -272,6 +305,32 @@ div[data-control="media-manager"] [data-control="item-list"] {
|
|||
position: relative;
|
||||
display: table-cell;
|
||||
}
|
||||
div[data-control="media-manager"] table.table {
|
||||
table-layout: fixed;
|
||||
white-space: nowrap;
|
||||
}
|
||||
div[data-control="media-manager"] table.table div.no-wrap-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
div[data-control="media-manager"] table.table div.item-title {
|
||||
position: relative;
|
||||
padding-right: 0;
|
||||
-webkit-transition: padding 0.1s;
|
||||
transition: padding 0.1s;
|
||||
}
|
||||
div[data-control="media-manager"] table.table div.item-title a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
div[data-control="media-manager"] table.table tr:hover div.item-title {
|
||||
padding-right: 25px;
|
||||
}
|
||||
div[data-control="media-manager"] table.table tr:hover div.item-title a {
|
||||
display: block;
|
||||
}
|
||||
div[data-control="media-manager"] div[data-control="selection-marker"] {
|
||||
position: absolute;
|
||||
z-index: 50;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
this.updateSearchResultsBound = this.updateSearchResults.bind(this)
|
||||
this.releaseNavigationAjaxBound = this.releaseNavigationAjax.bind(this)
|
||||
this.deleteConfirmationBound = this.deleteConfirmation.bind(this)
|
||||
this.refreshBound = this.refresh.bind(this)
|
||||
|
||||
// State properties
|
||||
this.selectTimer = null
|
||||
|
|
@ -83,6 +84,7 @@
|
|||
this.updateSearchResultsBound = null
|
||||
this.releaseNavigationAjaxBound = null
|
||||
this.deleteConfirmationBound = null
|
||||
this.refreshBound = null
|
||||
|
||||
this.sidebarPreviewElement = null
|
||||
this.itemListElement = null
|
||||
|
|
@ -111,6 +113,7 @@
|
|||
this.$el.on('click.item', '[data-type="media-item"]', this.itemClickHandler)
|
||||
this.$el.on('change', '[data-control="sorting"]', this.sortingChangedHandler)
|
||||
this.$el.on('keyup', '[data-control="search"]', this.searchChangedHandler)
|
||||
this.$el.on('mediarefresh', this.refreshBound)
|
||||
|
||||
if (this.itemListElement)
|
||||
this.itemListElement.addEventListener('mousedown', this.listMouseDownHandler)
|
||||
|
|
@ -760,6 +763,7 @@
|
|||
break;
|
||||
case 'set-filter':
|
||||
this.setFilter($(ev.currentTarget).data('filter'))
|
||||
break;
|
||||
case 'delete':
|
||||
this.deleteFiles()
|
||||
break;
|
||||
|
|
@ -769,7 +773,8 @@
|
|||
}
|
||||
|
||||
MediaManager.prototype.onItemClick = function(ev) {
|
||||
if (ev.currentTarget.hasAttribute('data-root'))
|
||||
// Don't select "Go up" folders and don't select items when the rename icon is clicked
|
||||
if (ev.currentTarget.hasAttribute('data-root') || ev.target.tagName == 'I')
|
||||
return
|
||||
|
||||
this.selectItem(ev.currentTarget, ev.shiftKey)
|
||||
|
|
|
|||
|
|
@ -119,9 +119,30 @@ div[data-control="media-manager"] {
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 15px;
|
||||
line-height: 150%;
|
||||
margin: 15px 0 5px 0;
|
||||
padding-right: 0;
|
||||
.transition(padding 0.1s);
|
||||
|
||||
position: relative;
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 15px;
|
||||
color: #2b3e50;
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
color: @color-link;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover h4 a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
p.size {
|
||||
|
|
@ -205,6 +226,18 @@ div[data-control="media-manager"] {
|
|||
li.selected {
|
||||
.media-selected-list();
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding-right: 15px;
|
||||
|
||||
a {
|
||||
right: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
li:hover h4 {
|
||||
padding-right: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
&.tiles {
|
||||
|
|
@ -247,6 +280,10 @@ div[data-control="media-manager"] {
|
|||
.media-selected-tiles();
|
||||
}
|
||||
|
||||
li:hover h4 {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
i.icon-chain-broken {
|
||||
margin-top: 47px;
|
||||
}
|
||||
|
|
@ -257,6 +294,10 @@ div[data-control="media-manager"] {
|
|||
}
|
||||
}
|
||||
|
||||
[data-control="sidebar-labels"] {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.sidebar-image-placeholder-container {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
|
@ -318,6 +359,37 @@ div[data-control="media-manager"] {
|
|||
display: table-cell;
|
||||
}
|
||||
|
||||
table.table {
|
||||
table-layout: fixed;
|
||||
white-space: nowrap;
|
||||
|
||||
div.no-wrap-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
div.item-title {
|
||||
position: relative;
|
||||
padding-right: 0;
|
||||
.transition(padding 0.1s);
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
tr:hover div.item-title{
|
||||
padding-right: 25px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div[data-control="selection-marker"] {
|
||||
position: absolute;
|
||||
z-index: 50;
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<li class="root"><a href="#" data-type="media-item" data-item-type="folder" data-path="/" data-clear-search="true"><?= e(trans('cms::lang.media.library')) ?></a></li>
|
||||
|
||||
<?php if (!$searchMode): ?>
|
||||
<?php foreach ($pathSegments as $segment): ?>
|
||||
<?php if ($segment != '/'): ?>
|
||||
<li><a href="#" data-type="media-item" data-item-type="folder" data-path="<?= e($segment) ?>"><?= basename($segment) ?></a></li>
|
||||
<?php foreach ($pathSegments as $folder=>$path): ?>
|
||||
<?php if ($path != '/'): ?>
|
||||
<li><a href="#" data-type="media-item" data-item-type="folder" data-path="<?= e($path) ?>"><?= basename($folder) ?></a></li>
|
||||
<?php endif ?>
|
||||
<?php endforeach?>
|
||||
<?php else: ?>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
<?php
|
||||
$listElementId = $this->getId('item-list');
|
||||
?>
|
||||
|
||||
<ul class="media-list <?= $listClass ?>">
|
||||
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||
<?php if (!$isRootFolder && !$searchMode): ?>
|
||||
|
|
@ -28,7 +32,17 @@
|
|||
<?= $this->makePartial('item-icon', ['itemType'=>$itemType, 'item'=>$item]) ?>
|
||||
|
||||
<div class="info">
|
||||
<h4 title="<?= e(basename($item->path)) ?>"><?= e(basename($item->path)) ?></h4>
|
||||
<h4 title="<?= e(basename($item->path)) ?>">
|
||||
<?= e(basename($item->path)) ?>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
data-rename
|
||||
data-control="popup"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><i class="icon-terminal"></i></a>
|
||||
</h4>
|
||||
<p class="size"><?= e($item->sizeToString()) ?></p>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
<?php
|
||||
$listElementId = $this->getId('item-list');
|
||||
?>
|
||||
|
||||
<table class="table data">
|
||||
<col />
|
||||
<col width="100px" />
|
||||
<col width="100px" />
|
||||
|
||||
<tbody class="icons clickable">
|
||||
<?php if (count($items) > 0 || !$isRootFolder): ?>
|
||||
<?php if (!$isRootFolder && !$searchMode): ?>
|
||||
|
|
@ -23,11 +31,25 @@
|
|||
data-document-type="<?= e($itemType) ?>"
|
||||
data-folder="<?= e(dirname($item->path)) ?>"
|
||||
>
|
||||
<td><i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i> <?= e(basename($item->path)) ?></td>
|
||||
<td>
|
||||
<div class="item-title no-wrap-text">
|
||||
<i class="<?= $this->itemTypeToIconClass($item, $itemType) ?>"></i> <?= e(basename($item->path)) ?>
|
||||
|
||||
<a
|
||||
href="#"
|
||||
data-rename
|
||||
data-control="popup"
|
||||
data-request-data="path: '<?= e($item->path) ?>', listId: '<?= $listElementId ?>', type: '<?= $item->type ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><i class="icon-terminal"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><?= e($item->sizeToString()) ?></td>
|
||||
<td><?= e($item->lastModifiedAsString()) ?></td>
|
||||
<?php if ($searchMode): ?>
|
||||
<td><?= e(dirname($item->path)) ?></td>
|
||||
<td title="<?= e(dirname($item->path)) ?>">
|
||||
<div class="no-wrap-text"><?= e(dirname($item->path)) ?></div>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<?= Form::ajax($this->getEventHandler('onApplyName'), [
|
||||
'success' => "\$el.trigger('close.oc.popup'); \$('#".$listId."').trigger('mediarefresh');",
|
||||
'data-stripe-load-indicator' => 1,
|
||||
'id' => 'media-rename-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.rename_popup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.rename_new_name')) ?></label>
|
||||
<input type="text" class="form-control" name="name" value="<?= e($name) ?>" />
|
||||
<input type="hidden" name="originalName" value="<?= e($name) ?>">
|
||||
<input type="hidden" name="type" value="<?= e($type) ?>">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="originalPath" value="<?= e($originalPath) ?>" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<script>
|
||||
setTimeout(
|
||||
function(){ $('#media-rename-popup-form input.form-control').focus() },
|
||||
310
|
||||
)
|
||||
|
||||
$('#media-rename-popup-form').on('oc.beforeRequest', function(ev){
|
||||
var originalName = $('#media-rename-popup-form [name=originalName]').val(),
|
||||
newName = $.trim($('#media-rename-popup-form [name=name]').val())
|
||||
|
||||
if (originalName == newName || newName.length == 0) {
|
||||
alert('Please enter a new name')
|
||||
|
||||
ev.preventDefault()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
Loading…
Reference in New Issue