Skip to content

Commit

Permalink
MDL-32867 Working with external references in filemanager
Browse files Browse the repository at this point in the history
- Files that are references to external resources have special shortcut icon in filemanager
- When user selects a REF file in filemanager, he can see the 'Original' of the file in the way that original repository wants to show it, it is loaded dynamically via AJAX request
- Files that are themselves the source of references of other files in the system have 'link' icon in filemanager. When user tries to remove/rename/overwrite SRC file he is warned that all ## existing references will be updated/converted to copies.
- Changed confirmation messages for deleting, moving/renaming of the folders
- confirmation dialog in filemanager is using YUI3 now
  • Loading branch information
marinaglancy committed May 21, 2012
1 parent dd1f051 commit 9a62f77
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 13 deletions.
43 changes: 36 additions & 7 deletions files/renderer.php
Expand Up @@ -105,7 +105,10 @@ public function render_form_filemanager($fm) {
'strings' => array(
array('error', 'moodle'), array('info', 'moodle'), array('confirmdeletefile', 'repository'),
array('draftareanofiles', 'repository'), array('entername', 'repository'), array('enternewname', 'repository'),
array('invalidjson', 'repository'), array('popupblockeddownload', 'repository')
array('invalidjson', 'repository'), array('popupblockeddownload', 'repository'),
array('unknownoriginal', 'repository'), array('confirmdeletefolder', 'repository'),
array('confirmdeletefilewithhref', 'repository'), array('confirmrenamefolder', 'repository'),
array('confirmrenamefile', 'repository')
)
);
if (empty($filemanagertemplateloaded)) {
Expand Down Expand Up @@ -234,7 +237,10 @@ private function fm_print_generallayout($fm) {
private function fm_js_template_iconfilename() {
$rv = '<div class="fp-file" style="position:relative">
<a href="#">
<div style="position:relative;">
<div class="{!}fp-thumbnail"></div>
<div class="fp-reficons"></div>
</div>
<div class="{!}fp-filename"></div></a>
<a class="{!}fp-contextmenu" href="#">'.$this->pix_icon('i/menu', '▶').'</a>
</div>';
Expand Down Expand Up @@ -311,6 +317,9 @@ private function fm_js_template_message() {
* is unavailable. If there is information available, the content of embedded element
* with class 'fp-value' will be substituted with the value;
*
* The value of Original ('fp-original') is loaded in separate request. When it is applicable
* but not yet loaded the 'fp-original' element receives additional class 'fp-loading';
*
* Elements with classes 'fp-file-update', 'fp-file-download', 'fp-file-delete', 'fp-file-zip',
* 'fp-file-unzip', 'fp-file-setmain' and 'fp-file-cancel' will hold corresponding onclick
* events (there may be several elements with class 'fp-file-cancel');
Expand All @@ -326,6 +335,8 @@ private function fm_js_template_message() {
* @return string
*/
private function fm_js_template_fileselectlayout() {
$strloading = get_string('loading', 'repository');
$icon_progress = $this->pix_icon('i/loading_small', $strloading).'';
$rv = '<div class="filemanager fp-select">
<div class="fp-select-loading">
<img src="'.$this->pix_url('i/loading').'" />
Expand All @@ -343,7 +354,7 @@ private function fm_js_template_fileselectlayout() {
<tr class="{!}fp-path"><td class="mdl-right"><label>'.get_string('path', 'moodle').'</label>:</td>
<td class="mdl-left"><select></select></td></tr>
<tr class="{!}fp-original"><td class="mdl-right"><label>'.get_string('original', 'repository').'</label>:</td>
<td class="mdl-left"><span class="fp-value"/></td></tr>
<td class="mdl-left"><span class="fp-originloading">'.$icon_progress.' '.$strloading.'</span><span class="fp-value"/></td></tr>
</table>
<p><button class="{!}fp-file-update" >'.get_string('update', 'moodle').'</button>
<button class="{!}fp-file-download" >'.get_string('download').'</button>
Expand All @@ -362,6 +373,25 @@ private function fm_js_template_fileselectlayout() {
return preg_replace('/\{\!\}/', '', $rv);
}

/**
* FileManager JS template for popup confirm dialogue window.
*
* Must have one top element, CSS for this element must define width and height of the window;
*
* content of element with class 'fp-dlg-text' will be replaced with dialog text;
* elements with classes 'fp-dlg-butconfirm' and 'fp-dlg-butcancel' will
* hold onclick events;
*
* @return string
*/
private function fm_js_template_confirmdialog() {
$rv = '<div class="fp-dlg"><div class="{!}fp-dlg-text"></div>
<div class="fp-dlg-but"><button class="{!}fp-dlg-butconfirm" >'.get_string('ok').'</button></div>
<div class="fp-dlg-but"><button class="{!}fp-dlg-butcancel" >'.get_string('cancel').'</button></div>
</div>';
return preg_replace('/\{\!\}/', '', $rv);
}

/**
* Returns all FileManager JavaScript templates as an array.
*
Expand Down Expand Up @@ -686,7 +716,7 @@ private function fp_js_template_error() {
/**
* FilePicker JS template for error/info message displayed as a separate popup window.
*
* Must be wrapped in an element with class 'fp-msg', CSS for this element must define
* Must be wrapped in one element, CSS for this element must define
* width and height of the window. It will be assigned with an additional class 'fp-msg-error'
* or 'fp-msg-info' depending on message type;
*
Expand All @@ -697,7 +727,7 @@ private function fp_js_template_error() {
* @return string
*/
private function fp_js_template_message() {
$rv = '<div class="{!}fp-msg">
$rv = '<div class="fp-msg">
<div class="{!}fp-msg-text"></div>
<div><button class="{!}fp-msg-butok">'.get_string('ok').'</button></div>
</div>';
Expand All @@ -707,8 +737,7 @@ private function fp_js_template_message() {
/**
* FilePicker JS template for popup dialogue window asking for action when file with the same name already exists.
*
* Must be wrapped in an element with class 'fp-dlg', CSS for this element must define width
* and height of the window;
* Must have one top element, CSS for this element must define width and height of the window;
*
* content of element with class 'fp-dlg-text' will be replaced with dialog text;
* elements with classes 'fp-dlg-butoverwrite', 'fp-dlg-butrename' and 'fp-dlg-butcancel' will
Expand All @@ -720,7 +749,7 @@ private function fp_js_template_message() {
* @return string
*/
private function fp_js_template_processexistingfile() {
$rv = '<div class="{!}fp-dlg"><div class="{!}fp-dlg-text"></div>
$rv = '<div class="fp-dlg"><div class="{!}fp-dlg-text"></div>
<div class="fp-dlg-but"><button class="{!}fp-dlg-butoverwrite" >'.get_string('overwrite', 'repository').'</button></div>
<div class="fp-dlg-but"><button class="{!}fp-dlg-butrename" /></div>
<div class="fp-dlg-but"><button class="{!}fp-dlg-butcancel" >'.get_string('cancel').'</button></div>
Expand Down
5 changes: 5 additions & 0 deletions lang/en/repository.php
Expand Up @@ -63,7 +63,11 @@
$string['configsaved'] = 'Configuration saved!';
$string['confirmdelete'] = 'Are you sure you want to delete this repository - {$a}? If you choose "Continue and download", file references to external contents will be downloaded to moodle, but it could take long time to process.';
$string['confirmdeletefile'] = 'Are you sure you want to delete this file?';
$string['confirmrenamefile'] = 'Are you sure you want to rename/move this file? There are {$a} alias/shortcut files that use this file as their source. If you proceed then those aliases will be converted to true copies.';
$string['confirmdeletefilewithhref'] = 'Are you sure you want to delete this file? There are {$a} alias/shortcut files that use this file as their source. If you proceed then those aliases will be converted to true copies.';
$string['confirmdeletefolder'] = 'Are you sure you want to delete this folder? All files and subfolders will be deleted.';
$string['confirmremove'] = 'Are you sure you want to remove this repository plugin, its options and <strong>all of its instances</strong> - {$a}? If you choose "Continue and download", file references to external contents will be downloaded to moodle, but it could take long time to process.';
$string['confirmrenamefolder'] = ' Are you sure you want to move/rename this folder? Any alias/shortcut files that reference files in this folder will be converted into true copies.';
$string['continueuninstall'] = 'Continue';
$string['continueuninstallanddownload'] = 'Continue and download';
$string['copying'] = 'Copying';
Expand Down Expand Up @@ -191,6 +195,7 @@
$string['title'] = 'Choose a file...';
$string['type'] = 'Type';
$string['typenotvisible'] = 'Type not visible';
$string['unknownoriginal'] = 'Unknown';
$string['upload'] = 'Upload this file';
$string['uploading'] = 'Uploading...';
$string['uploadsucc'] = 'The file has been uploaded successfully';
Expand Down
3 changes: 3 additions & 0 deletions lib/filelib.php
Expand Up @@ -597,7 +597,10 @@ function file_get_drafarea_files($draftitemid, $filepath = '/') {
$item->license = $file->get_license();
$item->datemodified = $file->get_timemodified();
$item->datecreated = $file->get_timecreated();
$item->isref = $file->is_external_file();
$item->refcount = $fs->get_reference_count($file);

// TODO MDL-32900 this is not the correct way to check that it is archive, use filetype_parser instead
if ($icon == 'zip') {
$item->type = 'zip';
} else {
Expand Down
103 changes: 97 additions & 6 deletions lib/form/filemanager.js
Expand Up @@ -491,6 +491,12 @@ M.form_filemanager.init = function(Y, options) {
if (node.filename || node.filepath || (node.path && node.path != '/')) {
classname = classname + ' fp-hascontextmenu';
}
if (node.isref) {
classname = classname + ' fp-isreference';
}
if (node.refcount) {
classname = classname + ' fp-hasreferences';
}
if (node.sortorder == 1) { classname = classname + ' fp-mainfile';}
return Y.Lang.trim(classname);
}
Expand Down Expand Up @@ -554,7 +560,7 @@ M.form_filemanager.init = function(Y, options) {
set('value', list[i]).setContent(list[i]))
}
},
update_file: function() {
update_file: function(confirmed) {
var selectnode = this.selectnode;
var fileinfo = this.selectui.fileinfo;

Expand All @@ -572,12 +578,18 @@ M.form_filemanager.init = function(Y, options) {
var licensechanged = (newlicense != fileinfo.license);

var params, action;
var dialog_options = {callback:this.update_file, callbackargs:[true], scope:this};
if (fileinfo.type == 'folder') {
if (!newfilename) {
this.print_msg(M.str.repository.entername, 'error');
return;
}
if (filenamechanged || filepathchanged) {
if (!confirmed) {
dialog_options.message = M.str.repository.confirmrenamefolder;
this.show_confirm_dialog(dialog_options);
return;
}
params = {filepath:fileinfo.filepath, newdirname:newfilename, newfilepath:targetpath};
action = 'updatedir';
}
Expand All @@ -586,6 +598,11 @@ M.form_filemanager.init = function(Y, options) {
this.print_msg(M.str.repository.enternewname, 'error');
return;
}
if ((filenamechanged || filepathchanged) && !confirmed && fileinfo.refcount) {
dialog_options.message = M.util.get_string('confirmrenamefile', 'repository', fileinfo.refcount);
this.show_confirm_dialog(dialog_options);
return;
}
if (filenamechanged || filepathchanged || licensechanged || authorchanged) {
params = {filepath:fileinfo.filepath, filename:fileinfo.fullname,
newfilename:newfilename, newfilepath:targetpath,
Expand Down Expand Up @@ -617,6 +634,50 @@ M.form_filemanager.init = function(Y, options) {
}
});
},
/**
* Displays a confirmation dialog
* Expected attributes in dialog_options: message, callback, callbackargs(optional), scope(optional)
*/
show_confirm_dialog: function(dialog_options) {
// instead of M.util.show_confirm_dialog(e, dialog_options);
if (!this.confirm_dlg) {
this.confirm_dlg_node = Y.Node.create(M.form_filemanager.templates.confirmdialog);
var node = this.confirm_dlg_node;
node.generateID();
Y.one(document.body).appendChild(node);
this.confirm_dlg = new Y.Panel({
srcNode : node,
zIndex : 800000,
centered : true,
modal : true,
visible : false,
render : true,
buttons : {}
});
this.confirm_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']});
var handleConfirm = function(ev) {
var dlgopt = this.confirm_dlg.dlgopt;
ev.preventDefault();
this.confirm_dlg.hide();
if (dlgopt.callback) {
if (dlgopt.callbackargs) {
dlgopt.callback.apply(dlgopt.scope || this, dlgopt.callbackargs);
} else {
dlgopt.callback.apply(dlgopt.scope || this);
}
}
}
var handleCancel = function(ev) {
ev.preventDefault();
this.confirm_dlg.hide();
}
node.one('.fp-dlg-butconfirm').on('click', handleConfirm, this);
node.one('.fp-dlg-butcancel').on('click', handleCancel, this);
}
this.confirm_dlg.dlgopt = dialog_options;
this.confirm_dlg_node.one('.fp-dlg-text').setContent(dialog_options.message);
this.confirm_dlg.show();
},
setup_select_file: function() {
var selectnode = this.selectnode;
// bind labels with corresponding inputs
Expand All @@ -639,13 +700,19 @@ M.form_filemanager.init = function(Y, options) {
e.preventDefault();
var dialog_options = {};
var params = {};
dialog_options.message = M.str.repository.confirmdeletefile;
var fileinfo = this.selectui.fileinfo;
dialog_options.scope = this;
if (this.selectui.fileinfo.type == 'folder') {
params.filepath = fileinfo.filepath;
if (fileinfo.type == 'folder') {
params.filename = '.';
params.filepath = this.selectui.fileinfo.filepath;
dialog_options.message = M.str.repository.confirmdeletefolder;
} else {
params.filename = this.selectui.fileinfo.fullname;
params.filename = fileinfo.fullname;
if (fileinfo.refcount) {
dialog_options.message = M.util.get_string('confirmdeletefilewithhref', 'repository', fileinfo.refcount);
} else {
dialog_options.message = M.str.repository.confirmdeletefile;
}
}
dialog_options.callbackargs = [params];
dialog_options.callback = function(params) {
Expand All @@ -665,7 +732,7 @@ M.form_filemanager.init = function(Y, options) {
});
};
this.selectui.hide(); // TODO remove this after confirm dialog is replaced with YUI3
M.util.show_confirm_dialog(e, dialog_options);
this.show_confirm_dialog(dialog_options);
}, this);
selectnode.one('.fp-file-zip').on('click', function(e) {
e.preventDefault();
Expand Down Expand Up @@ -778,6 +845,30 @@ M.form_filemanager.init = function(Y, options) {
setStyle('maxHeight', ''+(node.thumbnail_height ? node.thumbnail_height : 90)+'px').
setStyle('maxWidth', ''+(node.thumbnail_width ? node.thumbnail_width : 90)+'px');
selectnode.one('.fp-thumbnail').setContent('').appendChild(imgnode);
// load original location if applicable
if (node.isref && !node.original) {
selectnode.one('.fp-original').removeClass('fp-unknown').addClass('fp-loading');
this.request({
action: 'getoriginal',
scope: this,
params: {'filepath':node.filepath,'filename':node.fullname},
callback: function(id, obj, args) {
// check if we did not select another file yet
var scope = args.scope;
if (scope.selectui.fileinfo && node &&
scope.selectui.fileinfo.filepath == node.filepath &&
scope.selectui.fileinfo.fullname == node.fullname) {
selectnode.one('.fp-original').removeClass('fp-loading');
if (obj.original) {
node.original = obj.original;
selectnode.one('.fp-original .fp-value').setContent(node.original);
} else {
selectnode.one('.fp-original .fp-value').setContent(M.str.repository.unknownsource);
}
}
}
}, false);
}
// show panel
this.selectui.show();
},
Expand Down
14 changes: 14 additions & 0 deletions repository/draftfiles_ajax.php
Expand Up @@ -383,6 +383,20 @@
}
die;

case 'getoriginal':
$filename = required_param('filename', PARAM_FILE);
$filepath = required_param('filepath', PARAM_PATH);

$fs = get_file_storage();
$file = $fs->get_file($user_context->id, 'user', 'draft', $draftid, $filepath, $filename);
if (!$file) {
echo json_encode(false);
} else {
$return = array('filename' => $filename, 'filepath' => $filepath, 'original' => $file->get_reference_details());
echo json_encode((object)$return);
}
die;

default:
// no/unknown action?
echo json_encode(false);
Expand Down
8 changes: 8 additions & 0 deletions theme/base/style/filemanager.css
Expand Up @@ -262,6 +262,14 @@ background: #CCC!important;filter: progid:DXImageTransform.Microsoft.gradient(st
.filemanager.fp-select.fp-cansetmain .fp-file-setmain {display:inline-block;}
.filemanager.fp-select.fp-folder .fp-file-download {display:none;} /* to be implemented */

.filemanager .fp-iconview .fp-reficons {position:absolute;height:100%;width:100%;top:0;left:0;z-index:1000;}
.filemanager .fp-iconview .fp-file.fp-hasreferences .fp-reficons {background: url([[pix:moodle|t/lock]]) no-repeat;background-position:bottom left;}
.filemanager .fp-iconview .fp-file.fp-isreference .fp-reficons {background: url([[pix:moodle|t/right]]) no-repeat;background-position:bottom right;}

.filemanager.fp-select .fp-original.fp-unknown {display:none;}
.filemanager.fp-select .fp-original .fp-originloading {display:none;}
.filemanager.fp-select .fp-original.fp-loading .fp-originloading {display:inline;}

/*
* Drag and drop support
*/
Expand Down

0 comments on commit 9a62f77

Please sign in to comment.