Permalink
Browse files

MDL-32867 Working with external references in filemanager

- 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...
1 parent dd1f051 commit 9a62f7799bb3dd654057af2f49b7e399d914b3b6 @marinaglancy marinaglancy committed May 10, 2012
Showing with 163 additions and 13 deletions.
  1. +36 −7 files/renderer.php
  2. +5 −0 lang/en/repository.php
  3. +3 −0 lib/filelib.php
  4. +97 −6 lib/form/filemanager.js
  5. +14 −0 repository/draftfiles_ajax.php
  6. +8 −0 theme/base/style/filemanager.css
View
@@ -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)) {
@@ -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>';
@@ -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');
@@ -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').'" />
@@ -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>
@@ -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.
*
@@ -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;
*
@@ -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>';
@@ -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
@@ -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>
View
@@ -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';
@@ -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';
View
@@ -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 {
View
@@ -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);
}
@@ -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;
@@ -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';
}
@@ -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,
@@ -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
@@ -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) {
@@ -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();
@@ -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();
},
@@ -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);
@@ -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
*/

0 comments on commit 9a62f77

Please sign in to comment.