MDL-33136 Dndupload filemanager - user asked to rename/overwrite file when droping...
authorDavo Smith <git@davosmith.co.uk>
Sat, 16 Jun 2012 14:42:07 +0000 (15:42 +0100)
committerDavo Smith <git@davosmith.co.uk>
Mon, 18 Jun 2012 07:55:41 +0000 (08:55 +0100)
files/renderer.php
lang/en/moodle.php
lang/en/repository.php
lib/form/dndupload.js
lib/outputrequirementslib.php
repository/upload/lib.php

index 2f5f5d4..21e2141 100644 (file)
@@ -802,7 +802,8 @@ class core_files_renderer extends plugin_renderer_base {
      * 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
+     * elements with classes 'fp-dlg-butoverwrite', 'fp-dlg-butrename',
+     * 'fp-dlg-butoverwriteall', 'fp-dlg-butrenameall' and 'fp-dlg-butcancel' will
      * hold onclick events;
      *
      * content of element with class 'fp-dlg-butrename' will be substituted with appropriate string
@@ -821,6 +822,33 @@ class core_files_renderer extends plugin_renderer_base {
         return preg_replace('/\{\!\}/', '', $rv);
     }
 
+    /**
+     * FilePicker JS template for popup dialogue window asking for action when file with the same name already exists (multiple-file version).
+     *
+     * 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
+     * hold onclick events;
+     *
+     * content of element with class 'fp-dlg-butrename' will be substituted with appropriate string
+     * (Note that it may have long text)
+     *
+     * @return string
+     */
+    private function fp_js_template_processexistingfilemultiple() {
+        $rv = '
+<div class="file-picker fp-dlg">
+    <p class="{!}fp-dlg-text"></p>
+    <a class="{!}fp-dlg-butoverwrite fp-panel-button" href="#">'.get_string('overwrite', 'repository').'</a>
+    <a class="{!}fp-dlg-butoverwriteall fp-panel-button" href="#">'.get_string('overwriteall', 'repository').'</a>
+    <a class="{!}fp-dlg-butcancel fp-panel-button" href="#">'.get_string('cancel').'</a>
+    <a class="{!}fp-dlg-butrename fp-panel-button" href="#"></a>
+    <a class="{!}fp-dlg-butrenameall fp-panel-button" href="#">'.get_string('renameall', 'repository').'</a>
+</div>';
+        return preg_replace('/\{\!\}/', '', $rv);
+    }
+
     /**
      * FilePicker JS template for repository login form including templates for each element type
      *
index 224b741..271f9a2 100644 (file)
@@ -684,6 +684,7 @@ $string['failedloginattempts'] = '{$a->attempts} failed logins since your last l
 $string['failedloginattemptsall'] = '{$a->attempts} failed logins for {$a->accounts} accounts';
 $string['feedback'] = 'Feedback';
 $string['file'] = 'File';
+$string['fileexists'] = 'There is already a file called {$a}';
 $string['filemissing'] = '{$a} is missing';
 $string['filetoolarge'] = 'is too large to upload';
 $string['files'] = 'Files';
@@ -1714,7 +1715,7 @@ $string['uploadedfiletoobig'] = 'Sorry, but that file is too big (limit is {$a}
 $string['uploadextension'] = 'File upload stopped by extension';
 $string['uploadfailednotrecovering'] = 'Your file upload has failed because there was a problem with one of the files, {$a->name}.<br /> Here is a log of the problems:<br />{$a->problem}<br />Not recovering.';
 $string['uploadfilelog'] = 'Upload log for file {$a}';
-$string['uploadformlimit'] = 'Uploaded file exceeded the maximum size limit set by the form';
+$string['uploadformlimit'] = 'Uploaded file {$a} exceeded the maximum size limit set by the form';
 $string['uploadlabel'] = 'Title:';
 $string['uploadnewfile'] = 'Upload new file';
 $string['uploadnofilefound'] = 'No file was found - are you sure you selected one to upload?';
index c0ca916..c409093 100644 (file)
@@ -162,6 +162,7 @@ $string['openpicker'] = 'Choose a file...';
 $string['operation'] = 'Operation';
 $string['on'] = 'Enabled and visible';
 $string['overwrite'] = 'Overwrite';
+$string['overwriteall'] = 'Overwrite all';
 $string['personalrepositories'] = 'Available repository instances';
 $string['plugin'] = 'Repository plug-ins';
 $string['pluginerror'] = 'Errors in repository plugin.';
@@ -174,6 +175,7 @@ $string['referenceslist'] = 'Aliases/Shortcuts';
 $string['refresh'] = 'Refresh';
 $string['refreshnonjsfilepicker'] = 'Please close this window and refresh non-javascript file picker';
 $string['removed'] = 'Repository removed';
+$string['renameall'] = 'Rename all';
 $string['renameto'] = 'Rename to "{$a}"';
 $string['repositories'] = 'Repositories';
 $string['repository'] = 'Repository';
index 4cba3ec..103d316 100644 (file)
@@ -36,8 +36,6 @@ M.form_dndupload.init = function(Y, options) {
         itemid: null,
         // accepted filetypes accepted by this form passed to repository
         acceptedtypes: [],
-        // maximum number of files this form allows
-        maxfiles: 0,
         // maximum size of files allowed in this form
         maxbytes: 0,
         // unqiue id of this form field used for html elements
@@ -91,7 +89,6 @@ M.form_dndupload.init = function(Y, options) {
             this.options = options;
             this.acceptedtypes = options.acceptedtypes;
             this.clientid = options.clientid;
-            this.maxfiles = options.maxfiles;
             this.maxbytes = options.maxbytes;
             this.itemid = options.itemid;
             this.author = options.author;
@@ -173,7 +170,7 @@ M.form_dndupload.init = function(Y, options) {
          * onto the page
          */
         drag_enter_page: function(e) {
-            if (!this.has_files(e) || this.reached_maxfiles()) {
+            if (!this.has_files(e)) {
                 return false;
             }
 
@@ -212,7 +209,7 @@ M.form_dndupload.init = function(Y, options) {
          * @param e event object
          * @return boolean true if a valid file drag event
          */
-        check_drag: function(e, maxfilesalert) {
+        check_drag: function(e) {
             if (!this.has_files(e)) {
                 return false;
             }
@@ -220,13 +217,6 @@ M.form_dndupload.init = function(Y, options) {
             e.preventDefault();
             e.stopPropagation();
 
-            if (this.reached_maxfiles()) {
-                if (typeof(maxfilesalert) != 'undefined' && maxfilesalert) {
-                    alert(M.util.get_string('maxfilesreached', 'moodle', this.maxfiles));
-                }
-                return false;
-            }
-
             return true;
         },
 
@@ -302,32 +292,28 @@ M.form_dndupload.init = function(Y, options) {
 
             var files = e._event.dataTransfer.files;
             if (this.filemanager) {
-                var currentfilecount = this.filemanager.filecount;
-                if (((currentfilecount + files.length) > this.maxfiles) && (this.maxfiles != -1)) {
-                    // TODO in case of overwrite we don't need it!
-                    alert(M.util.get_string('maxfilesreached', 'moodle', this.maxfiles)); // TODO change to YUI
-                    return false;
-                }
-                //this.show_progress_spinner();
                 var options = {
-                    files:files,
+                    files: files,
                     options: this.options,
                     repositoryid: this.repositoryid,
-                    callback:this.callback ? Y.bind('callback', this) : Y.bind('update_filemanager', this)
+                    currentfilecount: this.filemanager.filecount, // All files uploaded.
+                    currentfiles: this.filemanager.options.list, // Only the current folder.
+                    callback: Y.bind('update_filemanager', this)
                 };
                 var uploader = new dnduploader(options);
-                uploader.doupload();
+                uploader.start_upload();
             } else {
-                //this.show_progress_spinner();
                 if (files.length >= 1) {
                     options = {
                         files:[files[0]],
                         options: this.options,
                         repositoryid: this.repositoryid,
-                        callback:this.callback ? Y.bind('callback', this) : Y.bind('update_filemanager', this)
+                        currentfilecount: 0,
+                        currentfiles: [],
+                        callback: Y.bind('callback', this)
                     };
                     uploader = new dnduploader(options);
-                    uploader.doupload();
+                    uploader.start_upload();
                 }
             }
 
@@ -350,20 +336,6 @@ M.form_dndupload.init = function(Y, options) {
             return false;
         },
 
-        /**
-         * Check if reached the maximumum number of allowed files
-         *
-         * @return boolean true if reached maximum number of files
-         */
-        reached_maxfiles: function() {
-            if (this.filemanager) {
-                if (this.filemanager.filecount >= this.maxfiles && this.maxfiles != -1) {
-                    return true;
-                }
-            }
-            return false;
-        },
-
         /**
          * Highlight the area where files could be dropped
          */
@@ -389,20 +361,6 @@ M.form_dndupload.init = function(Y, options) {
             this.container.removeClass('dndupload-over');
         },
 
-        /**
-         * Display a progress spinner in the destination node
-         */
-        show_progress_spinner: function() {
-            this.container.addClass('dndupload-uploading');
-        },
-
-        /**
-         * Remove progress spinner in the destination node
-         */
-        hide_progress_spinner: function() {
-            this.container.removeClass('dndupload-uploading');
-        },
-
         /**
          * Tell the attached filemanager element (if any) to refresh on file
          * upload
@@ -420,50 +378,361 @@ M.form_dndupload.init = function(Y, options) {
     };
 
     Y.extend(dnduploader, Y.Base, {
+        // The URL to send the upload data to.
         api: M.cfg.wwwroot+'/repository/repository_ajax.php',
+        // Options passed into the filemanager/filepicker element.
         options: {},
+        // The function to call when all uploads complete.
         callback: null,
+        // The list of files dropped onto the element.
         files: null,
+        // The ID of the 'upload' repository.
         repositoryid: 0,
-        processedfiles: 0,
+        // Array of files already in the current folder (to check for name clashes).
+        currentfiles: null,
+        // Total number of files already uploaded (to check for exceeding limits).
+        currentfilecount: 0,
+        // The list of files to upload.
+        uploadqueue: [],
+        // This list of files with name clashes.
+        renamequeue: [],
+        // Set to true if the user has clicked on 'overwrite all'.
+        overwriteall: false,
+        // Set to true if the user has clicked on 'rename all'.
+        renameall: false,
 
+        /**
+         * Initialise the settings for the dnduploader
+         * @param object params - includes:
+         *                     options (copied from the filepicker / filemanager)
+         *                     repositoryid - ID of the upload repository
+         *                     callback - the function to call when uploads are complete
+         *                     currentfiles - the list of files already in the current folder in the filemanager
+         *                     currentfilecount - the total files already in the filemanager
+         *                     files - the list of files to upload
+         * @return void
+         */
         initializer: function(params) {
             this.options = params.options;
             this.repositoryid = params.repositoryid;
             this.callback = params.callback;
-            this.files = params.files; // TODO make copy?
+            this.currentfiles = params.currentfiles;
+            this.currentfilecount = params.currentfilecount;
+
+            this.initialise_queue(params.files);
         },
 
-        doupload: function(lastresult) {
-            console.log('doupload : '+this.processedfiles);
-            if (this.files && this.files.length > this.processedfiles) {
-                console.log('uploading file');
-                console.log(this.files[this.processedfiles])
-                this.upload_file(this.files[this.processedfiles]);
+        /**
+         * Entry point for starting the upload process (starts by processing any
+         * renames needed)
+         */
+        start_upload: function() {
+            this.process_renames(); // Automatically calls 'do_upload' once renames complete.
+        },
+
+        /**
+         * Display a message in a popup
+         * @param string msg - the message to display
+         * @param string type - 'error' or 'info'
+         */
+        print_msg: function(msg, type) {
+            var header = M.str.moodle.error;
+            if (type != 'error') {
+                type = 'info'; // one of only two types excepted
+                header = M.str.moodle.info;
+            }
+            if (!this.msg_dlg) {
+                this.msg_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.message);
+                this.msg_dlg_node.generateID();
+
+                this.msg_dlg = new Y.Panel({
+                    srcNode      : this.msg_dlg_node,
+                    zIndex       : 800000,
+                    centered     : true,
+                    modal        : true,
+                    visible      : false,
+                    render       : true
+                });
+                this.msg_dlg.plug(Y.Plugin.Drag,{handles:['#'+this.msg_dlg_node.get('id')+' .yui3-widget-hd']});
+                this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) {
+                    e.preventDefault();
+                    this.msg_dlg.hide();
+                }, this);
+            }
+
+            this.msg_dlg.set('headerContent', header);
+            this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type)
+            this.msg_dlg_node.one('.fp-msg-text').setContent(msg);
+            this.msg_dlg.show();
+        },
+
+        /**
+         * Check the size of each file and add to either the uploadqueue or, if there
+         * is a name clash, the renamequeue
+         * @param FileList files - the files to upload
+         * @return void
+         */
+        initialise_queue: function(files) {
+            this.uploadqueue = [];
+            this.renamequeue = [];
+
+            // Loop through the files and find any name clashes with existing files
+            var i;
+            for (i=0; i<files.length; i++) {
+                if (this.options.maxbytes > 0 && files[i].size > this.options.maxbytes) {
+                    // Check filesize before attempting to upload
+                    this.print_msg(M.util.get_string('uploadformlimit', 'moodle', files[i].name), 'error');
+                    this.uploadqueue = []; // No uploads if one file is too big.
+                    return;
+                }
+
+                if (this.has_name_clash(files[i].name)) {
+                    this.renamequeue.push(files[i]);
+                } else {
+                    if (!this.add_to_upload_queue(files[i], files[i].name, false)) {
+                        return;
+                    }
+                }
+            }
+        },
+
+        /**
+         * Add a single file to the uploadqueue, whilst checking the maxfiles limit
+         * @param File file - the file to add
+         * @param string filename - the name to give the file on upload
+         * @param bool overwrite - true to overwrite the existing file
+         * @return bool true if added successfully
+         */
+        add_to_upload_queue: function(file, filename, overwrite) {
+            if (!overwrite) {
+                this.currentfilecount++;
+            }
+            if (this.options.maxfiles > 0 && this.currentfilecount > this.options.maxfiles) {
+                // Too many files - abort entire upload.
+                this.uploadqueue = [];
+                this.renamequeue = [];
+                this.print_msg(M.util.get_string('maxfilesreached', 'moodle', this.options.maxfiles), 'error');
+                return false;
+            }
+            this.uploadqueue.push({file:file, filename:filename, overwrite:overwrite});
+            return true;
+        },
+
+        /**
+         * Take the next file from the renamequeue and ask the user what to do with
+         * it. Called recursively until the queue is empty, then calls do_upload.
+         * @return void
+         */
+        process_renames: function() {
+            if (this.renamequeue.length == 0) {
+                // All rename processing complete - start the actual upload.
+                this.do_upload();
+                return;
+            }
+            var multiplefiles = (this.renamequeue.length > 1);
+
+            // Get the next file from the rename queue.
+            var file = this.renamequeue.shift();
+            // Generate a non-conflicting name for it.
+            var newname = this.generate_unique_name(file.name);
+
+            // If the user has clicked on overwrite/rename ALL then process
+            // this file, as appropriate, then process the rest of the queue.
+            if (this.overwriteall) {
+                this.add_to_upload_queue(file, file.name, true);
+                this.process_renames();
+                return;
+            }
+            if (this.renameall) {
+                this.add_to_upload_queue(file, newname, false);
+                this.process_renames();
+                return;
+            }
+
+            // Ask the user what to do with this file.
+            var self = this;
+
+            var process_dlg_node;
+            if (multiplefiles) {
+                process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfilemultiple);
+            } else {
+                process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfile);
+            }
+            var node = process_dlg_node;
+            node.generateID();
+            var process_dlg = new Y.Panel({
+                srcNode      : node,
+                headerContent: M.str.repository.fileexistsdialogheader,
+                zIndex       : 800000,
+                centered     : true,
+                modal        : true,
+                visible      : false,
+                render       : true,
+                buttons      : {}
+            });
+            process_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']});
+
+            // Overwrite original.
+            node.one('.fp-dlg-butoverwrite').on('click', function(e) {
+                e.preventDefault();
+                process_dlg.hide();
+                self.add_to_upload_queue(file, file.name, true);
+                self.process_renames();
+            }, this);
+
+            // Rename uploaded file.
+            node.one('.fp-dlg-butrename').on('click', function(e) {
+                e.preventDefault();
+                process_dlg.hide();
+                self.add_to_upload_queue(file, newname, false);
+                self.process_renames();
+            }, this);
+
+            // Cancel all uploads.
+            node.one('.fp-dlg-butcancel').on('click', function(e) {
+                e.preventDefault();
+                process_dlg.hide();
+            }, this);
+
+            // When we are at the file limit, only allow 'overwrite', not rename.
+            if (this.currentfilecount == this.options.maxfiles) {
+                node.one('.fp-dlg-butrename').setStyle('display', 'none');
+                if (multiplefiles) {
+                    node.one('.fp-dlg-butrenameall').setStyle('display', 'none');
+                }
+            }
+
+            // If there are more files still to go, offer the 'overwrite/rename all' options.
+            if (multiplefiles) {
+                // Overwrite all original files.
+                node.one('.fp-dlg-butoverwriteall').on('click', function(e) {
+                    e.preventDefault();
+                    process_dlg.hide();
+                    this.overwriteall = true;
+                    self.add_to_upload_queue(file, file.name, true);
+                    self.process_renames();
+                }, this);
+
+                // Rename all new files.
+                node.one('.fp-dlg-butrenameall').on('click', function(e) {
+                    e.preventDefault();
+                    process_dlg.hide();
+                    this.renameall = true;
+                    self.add_to_upload_queue(file, newname, false);
+                    self.process_renames();
+                }, this);
+            }
+            node.one('.fp-dlg-text').setContent(M.util.get_string('fileexists', 'moodle', file.name));
+            process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', newname));
+
+            // Destroy the dialog once it has been hidden.
+            process_dlg.after('visibleChange', function(e) {
+                if (!process_dlg.get('visible')) {
+                    process_dlg.destroy(true);
+                }
+            });
+
+            process_dlg.show();
+        },
+
+        /**
+         * Checks if there is already a file with the given name in the current folder
+         * or in the list of already uploading files
+         * @param string filename - the name to test
+         * @return bool true if the name already exists
+         */
+        has_name_clash: function(filename) {
+            // Check against the already uploaded files
+            var i;
+            for (i=0; i<this.currentfiles.length; i++) {
+                if (filename == this.currentfiles[i].filename) {
+                    return true;
+                }
+            }
+            // Check against the uploading files that have already been processed
+            for (i=0; i<this.uploadqueue.length; i++) {
+                if (filename == this.uploadqueue[i].filename) {
+                    return true;
+                }
+            }
+            return false;
+        },
+
+        /**
+         * Adds _NUMBER to the end of the filename and increments this number until
+         * a unique name is found
+         * @param string filename
+         * @return string the unique filename generated
+         */
+        generate_unique_name: function(filename) {
+            // Split the filename into the basename + extension.
+            var extension;
+            var basename;
+            var dotpos = filename.lastIndexOf('.');
+            if (dotpos == -1) {
+                basename = filename;
+                extension = '';
+            } else {
+                basename = filename.substr(0, dotpos);
+                extension = filename.substr(dotpos, filename.length);
+            }
+
+            // Look to see if the name already has _NN at the end of it.
+            var number = 0;
+            var hasnumber = basename.match(/^(.*)_(\d+)$/);
+            if (hasnumber != null) {
+                // Note the current number & remove it from the basename.
+                number = parseInt(hasnumber[2]);
+                basename = hasnumber[1];
+            }
+
+            // Loop through increating numbers until a unique name is found.
+            var newname;
+            do {
+                number++;
+                newname = basename + '_' + number + extension;
+            } while (this.has_name_clash(newname));
+
+            return newname;
+        },
+
+        /**
+         * Upload the next file from the uploadqueue - called recursively after each
+         * upload is complete, then handles the callback to the filemanager/filepicker
+         * @param lastresult - the last result from the server
+         */
+        do_upload: function(lastresult) {
+            if (this.uploadqueue.length > 0) {
+                var filedetails = this.uploadqueue.shift();
+                this.upload_file(filedetails.file, filedetails.filename, filedetails.overwrite);
             } else {
                 this.uploadfinished(lastresult);
             }
         },
 
+        /**
+         * Run the callback to the filemanager/filepicker
+         */
         uploadfinished: function(lastresult) {
-            console.log('upload finished')
             this.callback(lastresult);
         },
 
+        /**
+         * Log any errors generated
+         */
         add_error: function(text, errorlevel) {
             // TODO
             console.log(text);
         },
 
         /**
-         * Upload a single file via an AJAX call to the 'upload' repository
+         * Upload a single file via an AJAX call to the 'upload' repository. Automatically
+         * calls do_upload as each upload completes.
+         * @param File file - the file to upload
+         * @param string filename - the name to give the file
+         * @param bool overwrite - true if the existing file should be overwritten
          */
-        upload_file: function(file) {
-            if (this.options.maxbytes > 0 && file.size > this.options.maxbytes) {
-                // Check filesize before attempting to upload
-                this.add_error(M.util.get_string('uploadformlimit', 'moodle')+"\n'"+file.name+"'"); //TODO proper string
-                return false;
-            }
+        upload_file: function(file, filename, overwrite) {
 
             // This would be an ideal place to use the Y.io function
             // however, this does not support data encoded using the
@@ -476,10 +745,8 @@ M.form_dndupload.init = function(Y, options) {
             xhr.onreadystatechange = function() { // Process the server response
                 if (xhr.readyState == 4) {
                     if (xhr.status == 200) {
-                        console.log(xhr.responseText)
                         var result = JSON.parse(xhr.responseText);
                         if (result) {
-                            console.log(result)
                             if (result.error) {
                                 self.add_error(result.error); // TODO add filename?
                             } else {
@@ -494,7 +761,7 @@ M.form_dndupload.init = function(Y, options) {
                             }
                         }
                         self.processedfiles++;
-                        self.doupload(result); // continue uploading
+                        self.do_upload(result); // continue uploading
                     } else {
                         self.add_error(M.util.get_string('serverconnection', 'error'));
                         this.uploadfinished();
@@ -515,8 +782,14 @@ M.form_dndupload.init = function(Y, options) {
             if (this.options.filemanager) { // Filepickers do not have folders
                 formdata.append('savepath', this.options.filemanager.currentpath);
             }
+            formdata.append('title', filename);
+            if (overwrite) {
+                formdata.append('overwrite', 1);
+            }
 
-            if (this.options.acceptedtypes.constructor == Array) { // TODO what is this???
+            // Accepted types can be either a string or an array, but an array is
+            // expected in the processing script, so make sure we are sending an array
+            if (this.options.acceptedtypes.constructor == Array) {
                 for (var i=0; i<this.options.acceptedtypes.length; i++) {
                     formdata.append('accepted_types[]', this.options.acceptedtypes[i]);
                 }
@@ -526,8 +799,6 @@ M.form_dndupload.init = function(Y, options) {
 
             // Send the file & required details
             xhr.open("POST", this.api, true);
-            console.log('formdata')
-            console.log(formdata)
             xhr.send(formdata);
             return true;
         }
index aa8755e..e9e8f05 100644 (file)
@@ -499,8 +499,8 @@ class page_requirements_manager {
                 case 'core_dndupload':
                     $module = array('name'     => 'core_dndupload',
                                     'fullpath' => '/lib/form/dndupload.js',
-                                    'requires' => array('node', 'event', 'json'),
-                                    'strings'  => array(array('uploadformlimit', 'moodle'), array('droptoupload', 'moodle'), array('maxfilesreached', 'moodle'), array('dndenabled_inbox', 'moodle')));
+                                    'requires' => array('node', 'event', 'json', 'core_filepicker'),
+                                    'strings'  => array(array('uploadformlimit', 'moodle'), array('droptoupload', 'moodle'), array('maxfilesreached', 'moodle'), array('dndenabled_inbox', 'moodle'), array('fileexists', 'moodle')));
                     break;
             }
 
index a307ab6..ad5f434 100644 (file)
@@ -57,8 +57,9 @@ class repository_upload extends repository {
         $itemid   = optional_param('itemid', 0, PARAM_INT);
         $license  = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT);
         $author   = optional_param('author', '', PARAM_TEXT);
+        $overwriteexisting = optional_param('overwrite', false, PARAM_BOOL);
 
-        return $this->process_upload($saveas_filename, $maxbytes, $types, $savepath, $itemid, $license, $author);
+        return $this->process_upload($saveas_filename, $maxbytes, $types, $savepath, $itemid, $license, $author, $overwriteexisting);
     }
 
     /**
@@ -70,9 +71,10 @@ class repository_upload extends repository {
      * @param int $itemid optional the ID for this item within the file area
      * @param string $license optional the license to use for this file
      * @param string $author optional the name of the author of this file
+     * @param bool $overwriteexisting optional user has asked to overwrite the existing file
      * @return object containing details of the file uploaded
      */
-    public function process_upload($saveas_filename, $maxbytes, $types = '*', $savepath = '/', $itemid = 0, $license = null, $author = '') {
+    public function process_upload($saveas_filename, $maxbytes, $types = '*', $savepath = '/', $itemid = 0, $license = null, $author = '', $overwriteexisting = false) {
         global $USER, $CFG;
 
         if ((is_array($types) and in_array('*', $types)) or $types == '*') {
@@ -189,32 +191,37 @@ class repository_upload extends repository {
         $record->source    = '';
 
         if (repository::draftfile_exists($record->itemid, $record->filepath, $record->filename)) {
-            $existingfilename = $record->filename;
-            $unused_filename = repository::get_unused_filename($record->itemid, $record->filepath, $record->filename);
-            $record->filename = $unused_filename;
-            $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
-            $event = array();
-            $event['event'] = 'fileexists';
-            $event['newfile'] = new stdClass;
-            $event['newfile']->filepath = $record->filepath;
-            $event['newfile']->filename = $unused_filename;
-            $event['newfile']->url = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $unused_filename)->out(false);
-
-            $event['existingfile'] = new stdClass;
-            $event['existingfile']->filepath = $record->filepath;
-            $event['existingfile']->filename = $existingfilename;
-            $event['existingfile']->url      = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $existingfilename)->out(false);
-            return $event;
-        } else {
-            $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
+            if ($overwriteexisting) {
+                repository::delete_tempfile_from_draft($record->itemid, $record->filepath, $record->filename);
+            } else {
+                $existingfilename = $record->filename;
+                $unused_filename = repository::get_unused_filename($record->itemid, $record->filepath, $record->filename);
+                $record->filename = $unused_filename;
+                $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
+                $event = array();
+                $event['event'] = 'fileexists';
+                $event['newfile'] = new stdClass;
+                $event['newfile']->filepath = $record->filepath;
+                $event['newfile']->filename = $unused_filename;
+                $event['newfile']->url = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $unused_filename)->out(false);
 
-            return array(
-                'url'=>moodle_url::make_draftfile_url($record->itemid, $record->filepath, $record->filename)->out(false),
-                'id'=>$record->itemid,
-                'file'=>$record->filename);
+                $event['existingfile'] = new stdClass;
+                $event['existingfile']->filepath = $record->filepath;
+                $event['existingfile']->filename = $existingfilename;
+                $event['existingfile']->url      = moodle_url::make_draftfile_url($record->itemid, $record->filepath, $existingfilename)->out(false);
+                return $event;
+            }
         }
+
+        $stored_file = $fs->create_file_from_pathname($record, $_FILES[$elname]['tmp_name']);
+
+        return array(
+            'url'=>moodle_url::make_draftfile_url($record->itemid, $record->filepath, $record->filename)->out(false),
+            'id'=>$record->itemid,
+            'file'=>$record->filename);
     }
 
+
     /**
      * Checks the contents of the given file is not completely NULL - this can happen if a
      * user drags & drops a folder onto a filemanager / filepicker element