[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 // This file is part of Moodle - http://moodle.org/ 2 // 3 // Moodle is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // Moodle is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 15 16 /** 17 * Javascript library for enableing a drag and drop upload interface 18 * 19 * @package moodlecore 20 * @subpackage form 21 * @copyright 2011 Davo Smith 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 M.form_dndupload = {} 26 27 M.form_dndupload.init = function(Y, options) { 28 var dnduploadhelper = { 29 // YUI object. 30 Y: null, 31 // URL for upload requests 32 url: M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', 33 // options may include: itemid, acceptedtypes, maxfiles, maxbytes, clientid, repositoryid, author, contextid 34 options: {}, 35 // itemid used for repository upload 36 itemid: null, 37 // accepted filetypes accepted by this form passed to repository 38 acceptedtypes: [], 39 // maximum size of files allowed in this form 40 maxbytes: 0, 41 // Maximum combined size of files allowed in this form. {@link FILE_AREA_MAX_BYTES_UNLIMITED} 42 areamaxbytes: -1, 43 // unqiue id of this form field used for html elements 44 clientid: '', 45 // upload repository id, used for upload 46 repositoryid: 0, 47 // container which holds the node which recieves drag events 48 container: null, 49 // filemanager element we are working with 50 filemanager: null, 51 // callback to filepicker element to refesh when uploaded 52 callback: null, 53 // Nasty hack to distinguish between dragenter(first entry), 54 // dragenter+dragleave(moving between child elements) and dragleave (leaving element) 55 entercount: 0, 56 pageentercount: 0, 57 // Holds the progress bar elements for each file. 58 progressbars: {}, 59 60 /** 61 * Initalise the drag and drop upload interface 62 * Note: one and only one of options.filemanager and options.formcallback must be defined 63 * 64 * @param Y the YUI object 65 * @param object options { 66 * itemid: itemid used for repository upload in this form 67 * acceptdtypes: accepted filetypes by this form 68 * maxfiles: maximum number of files this form allows 69 * maxbytes: maximum size of files allowed in this form 70 * areamaxbytes: maximum combined size of files allowed in this form 71 * clientid: unqiue id of this form field used for html elements 72 * contextid: id of the current cotnext 73 * containerid: htmlid of container 74 * repositories: array of repository objects passed from filepicker 75 * filemanager: filemanager element we are working with 76 * formcallback: callback to filepicker element to refesh when uploaded 77 * } 78 */ 79 init: function(Y, options) { 80 this.Y = Y; 81 82 if (!this.browser_supported()) { 83 Y.one('body').addClass('dndnotsupported'); 84 return; // Browser does not support the required functionality 85 } 86 87 // try and retrieve enabled upload repository 88 this.repositoryid = this.get_upload_repositoryid(options.repositories); 89 90 if (!this.repositoryid) { 91 Y.one('body').addClass('dndnotsupported'); 92 return; // no upload repository is enabled to upload to 93 } 94 95 Y.one('body').addClass('dndsupported'); 96 97 this.options = options; 98 this.acceptedtypes = options.acceptedtypes; 99 this.clientid = options.clientid; 100 this.maxbytes = options.maxbytes; 101 this.areamaxbytes = options.areamaxbytes; 102 this.itemid = options.itemid; 103 this.author = options.author; 104 this.container = this.Y.one('#'+options.containerid); 105 106 if (options.filemanager) { 107 // Needed to tell the filemanager to redraw when files uploaded 108 // and to check how many files are already uploaded 109 this.filemanager = options.filemanager; 110 } else if (options.formcallback) { 111 112 // Needed to tell the filepicker to update when a new 113 // file is uploaded 114 this.callback = options.formcallback; 115 } else { 116 if (M.cfg.developerdebug) { 117 alert('dndupload: Need to define either options.filemanager or options.formcallback'); 118 } 119 return; 120 } 121 122 this.init_events(); 123 this.init_page_events(); 124 }, 125 126 /** 127 * Check the browser has the required functionality 128 * @return true if browser supports drag/drop upload 129 */ 130 browser_supported: function() { 131 132 if (typeof FileReader == 'undefined') { 133 return false; 134 } 135 if (typeof FormData == 'undefined') { 136 return false; 137 } 138 return true; 139 }, 140 141 /** 142 * Get upload repoistory from array of enabled repositories 143 * 144 * @param array repositories repository objects passed from filepicker 145 * @param returns int id of upload repository or false if not found 146 */ 147 get_upload_repositoryid: function(repositories) { 148 for (var i in repositories) { 149 if (repositories[i].type == "upload") { 150 return repositories[i].id; 151 } 152 } 153 154 return false; 155 }, 156 157 /** 158 * Initialise drag events on node container, all events need 159 * to be processed for drag and drop to work 160 */ 161 init_events: function() { 162 this.Y.on('dragenter', this.drag_enter, this.container, this); 163 this.Y.on('dragleave', this.drag_leave, this.container, this); 164 this.Y.on('dragover', this.drag_over, this.container, this); 165 this.Y.on('drop', this.drop, this.container, this); 166 }, 167 168 /** 169 * Initialise whole-page events (to show / hide the 'drop files here' 170 * message) 171 */ 172 init_page_events: function() { 173 this.Y.on('dragenter', this.drag_enter_page, 'body', this); 174 this.Y.on('dragleave', this.drag_leave_page, 'body', this); 175 }, 176 177 /** 178 * Check if the filemanager / filepicker is disabled 179 * @return bool - true if disabled 180 */ 181 is_disabled: function() { 182 return (this.container.ancestor('.fitem.disabled') != null); 183 }, 184 185 /** 186 * Show the 'drop files here' message when file(s) are dragged 187 * onto the page 188 */ 189 drag_enter_page: function(e) { 190 if (this.is_disabled()) { 191 return false; 192 } 193 if (!this.has_files(e)) { 194 return false; 195 } 196 197 this.pageentercount++; 198 if (this.pageentercount >= 2) { 199 this.pageentercount = 2; 200 return false; 201 } 202 203 this.show_drop_target(); 204 205 return false; 206 }, 207 208 /** 209 * Hide the 'drop files here' message when file(s) are dragged off 210 * the page again 211 */ 212 drag_leave_page: function(e) { 213 this.pageentercount--; 214 if (this.pageentercount == 1) { 215 return false; 216 } 217 this.pageentercount = 0; 218 219 this.hide_drop_target(); 220 221 return false; 222 }, 223 224 /** 225 * Check if the drag contents are valid and then call 226 * preventdefault / stoppropagation to let the browser know 227 * we will handle this drag/drop 228 * 229 * @param e event object 230 * @return boolean true if a valid file drag event 231 */ 232 check_drag: function(e) { 233 if (this.is_disabled()) { 234 return false; 235 } 236 if (!this.has_files(e)) { 237 return false; 238 } 239 240 e.preventDefault(); 241 e.stopPropagation(); 242 243 return true; 244 }, 245 246 /** 247 * Handle a dragenter event, highlight the destination node 248 * when a suitable drag event occurs 249 */ 250 drag_enter: function(e) { 251 if (!this.check_drag(e)) { 252 return true; 253 } 254 255 this.entercount++; 256 if (this.entercount >= 2) { 257 this.entercount = 2; // Just moved over a child element - nothing to do 258 return false; 259 } 260 261 // These lines are needed if the user has dragged something directly 262 // from application onto the 'fileupload' box, without crossing another 263 // part of the page first 264 this.pageentercount = 2; 265 this.show_drop_target(); 266 267 this.show_upload_ready(); 268 return false; 269 }, 270 271 /** 272 * Handle a dragleave event, Remove the highlight if dragged from 273 * node 274 */ 275 drag_leave: function(e) { 276 if (!this.check_drag(e)) { 277 return true; 278 } 279 280 this.entercount--; 281 if (this.entercount == 1) { 282 return false; // Just moved over a child element - nothing to do 283 } 284 285 this.entercount = 0; 286 this.hide_upload_ready(); 287 return false; 288 }, 289 290 /** 291 * Handle a dragover event. Required to intercept to prevent the browser from 292 * handling the drag and drop event as normal 293 */ 294 drag_over: function(e) { 295 if (!this.check_drag(e)) { 296 return true; 297 } 298 299 return false; 300 }, 301 302 /** 303 * Handle a drop event. Remove the highlight and then upload each 304 * of the files (until we reach the file limit, or run out of files) 305 */ 306 drop: function(e) { 307 if (!this.check_drag(e, true)) { 308 return true; 309 } 310 311 this.entercount = 0; 312 this.pageentercount = 0; 313 this.hide_upload_ready(); 314 this.hide_drop_target(); 315 316 var files = e._event.dataTransfer.files; 317 if (this.filemanager) { 318 var options = { 319 files: files, 320 options: this.options, 321 repositoryid: this.repositoryid, 322 currentfilecount: this.filemanager.filecount, // All files uploaded. 323 currentfiles: this.filemanager.options.list, // Only the current folder. 324 callback: Y.bind('update_filemanager', this), 325 callbackprogress: Y.bind('update_progress', this), 326 callbackcancel:Y.bind('hide_progress', this) 327 }; 328 this.clear_progress(); 329 this.show_progress(); 330 var uploader = new dnduploader(options); 331 uploader.start_upload(); 332 } else { 333 if (files.length >= 1) { 334 options = { 335 files:[files[0]], 336 options: this.options, 337 repositoryid: this.repositoryid, 338 currentfilecount: 0, 339 currentfiles: [], 340 callback: Y.bind('update_filemanager', this), 341 callbackprogress: Y.bind('update_progress', this), 342 callbackcancel:Y.bind('hide_progress', this) 343 }; 344 this.clear_progress(); 345 this.show_progress(); 346 uploader = new dnduploader(options); 347 uploader.start_upload(); 348 } 349 } 350 351 return false; 352 }, 353 354 /** 355 * Check to see if the drag event has any files in it 356 * 357 * @param e event object 358 * @return boolean true if event has files 359 */ 360 has_files: function(e) { 361 // In some browsers, dataTransfer.types may be null for a 362 // 'dragover' event, so ensure a valid Array is always 363 // inspected. 364 var types = e._event.dataTransfer.types || []; 365 for (var i=0; i<types.length; i++) { 366 if (types[i] == 'Files') { 367 return true; 368 } 369 } 370 return false; 371 }, 372 373 /** 374 * Highlight the area where files could be dropped 375 */ 376 show_drop_target: function() { 377 this.container.addClass('dndupload-ready'); 378 }, 379 380 hide_drop_target: function() { 381 this.container.removeClass('dndupload-ready'); 382 }, 383 384 /** 385 * Highlight the destination node (ready to drop) 386 */ 387 show_upload_ready: function() { 388 this.container.addClass('dndupload-over'); 389 }, 390 391 /** 392 * Remove highlight on destination node 393 */ 394 hide_upload_ready: function() { 395 this.container.removeClass('dndupload-over'); 396 }, 397 398 /** 399 * Show the element showing the upload in progress 400 */ 401 show_progress: function() { 402 this.container.addClass('dndupload-inprogress'); 403 }, 404 405 /** 406 * Hide the element showing upload in progress 407 */ 408 hide_progress: function() { 409 this.container.removeClass('dndupload-inprogress'); 410 }, 411 412 /** 413 * Tell the attached filemanager element (if any) to refresh on file 414 * upload 415 */ 416 update_filemanager: function(params) { 417 this.hide_progress(); 418 if (this.filemanager) { 419 // update the filemanager that we've uploaded the files 420 this.filemanager.filepicker_callback(); 421 } else if (this.callback) { 422 this.callback(params); 423 } 424 }, 425 426 /** 427 * Clear the current progress bars 428 */ 429 clear_progress: function() { 430 var filename; 431 for (filename in this.progressbars) { 432 if (this.progressbars.hasOwnProperty(filename)) { 433 this.progressbars[filename].progressouter.remove(true); 434 delete this.progressbars[filename]; 435 } 436 } 437 }, 438 439 /** 440 * Show the current progress of the uploaded file 441 */ 442 update_progress: function(filename, percent) { 443 if (this.progressbars[filename] === undefined) { 444 var dispfilename = filename; 445 if (dispfilename.length > 50) { 446 dispfilename = dispfilename.substr(0, 49)+'…'; 447 } 448 var progressouter = this.container.create('<span>'+dispfilename+': <span class="dndupload-progress-outer"><span class="dndupload-progress-inner"> </span></span><br /></span>'); 449 var progressinner = progressouter.one('.dndupload-progress-inner'); 450 var progresscontainer = this.container.one('.dndupload-progressbars'); 451 progresscontainer.appendChild(progressouter); 452 453 this.progressbars[filename] = { 454 progressouter: progressouter, 455 progressinner: progressinner 456 }; 457 } 458 459 this.progressbars[filename].progressinner.setStyle('width', percent + '%'); 460 } 461 }; 462 463 var dnduploader = function(options) { 464 dnduploader.superclass.constructor.apply(this, arguments); 465 }; 466 467 Y.extend(dnduploader, Y.Base, { 468 // The URL to send the upload data to. 469 api: M.cfg.wwwroot+'/repository/repository_ajax.php', 470 // Options passed into the filemanager/filepicker element. 471 options: {}, 472 // The function to call when all uploads complete. 473 callback: null, 474 // The function to call as the upload progresses 475 callbackprogress: null, 476 // The function to call if the upload is cancelled 477 callbackcancel: null, 478 // The list of files dropped onto the element. 479 files: null, 480 // The ID of the 'upload' repository. 481 repositoryid: 0, 482 // Array of files already in the current folder (to check for name clashes). 483 currentfiles: null, 484 // Total number of files already uploaded (to check for exceeding limits). 485 currentfilecount: 0, 486 // Total size of the files present in the area. 487 currentareasize: 0, 488 // The list of files to upload. 489 uploadqueue: [], 490 // This list of files with name clashes. 491 renamequeue: [], 492 // Size of the current queue. 493 queuesize: 0, 494 // Set to true if the user has clicked on 'overwrite all'. 495 overwriteall: false, 496 // Set to true if the user has clicked on 'rename all'. 497 renameall: false, 498 499 /** 500 * Initialise the settings for the dnduploader 501 * @param object params - includes: 502 * options (copied from the filepicker / filemanager) 503 * repositoryid - ID of the upload repository 504 * callback - the function to call when uploads are complete 505 * currentfiles - the list of files already in the current folder in the filemanager 506 * currentfilecount - the total files already in the filemanager 507 * files - the list of files to upload 508 * @return void 509 */ 510 initializer: function(params) { 511 this.options = params.options; 512 this.repositoryid = params.repositoryid; 513 this.callback = params.callback; 514 this.callbackprogress = params.callbackprogress; 515 this.callbackcancel = params.callbackcancel; 516 this.currentfiles = params.currentfiles; 517 this.currentfilecount = params.currentfilecount; 518 this.currentareasize = 0; 519 520 // Retrieve the current size of the area. 521 for (var i = 0; i < this.currentfiles.length; i++) { 522 this.currentareasize += this.currentfiles[i].size; 523 }; 524 525 if (!this.initialise_queue(params.files)) { 526 if (this.callbackcancel) { 527 this.callbackcancel(); 528 } 529 } 530 }, 531 532 /** 533 * Entry point for starting the upload process (starts by processing any 534 * renames needed) 535 */ 536 start_upload: function() { 537 this.process_renames(); // Automatically calls 'do_upload' once renames complete. 538 }, 539 540 /** 541 * Display a message in a popup 542 * @param string msg - the message to display 543 * @param string type - 'error' or 'info' 544 */ 545 print_msg: function(msg, type) { 546 var header = M.str.moodle.error; 547 if (type != 'error') { 548 type = 'info'; // one of only two types excepted 549 header = M.str.moodle.info; 550 } 551 if (!this.msg_dlg) { 552 this.msg_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.message); 553 this.msg_dlg_node.generateID(); 554 555 this.msg_dlg = new Y.Panel({ 556 srcNode : this.msg_dlg_node, 557 zIndex : 8000, 558 centered : true, 559 modal : true, 560 visible : false, 561 render : true 562 }); 563 this.msg_dlg.plug(Y.Plugin.Drag,{handles:['#'+this.msg_dlg_node.get('id')+' .yui3-widget-hd']}); 564 this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) { 565 e.preventDefault(); 566 this.msg_dlg.hide(); 567 }, this); 568 } 569 570 this.msg_dlg.set('headerContent', header); 571 this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type) 572 this.msg_dlg_node.one('.fp-msg-text').setContent(msg); 573 this.msg_dlg.show(); 574 }, 575 576 /** 577 * Check the size of each file and add to either the uploadqueue or, if there 578 * is a name clash, the renamequeue 579 * @param FileList files - the files to upload 580 * @return void 581 */ 582 initialise_queue: function(files) { 583 this.uploadqueue = []; 584 this.renamequeue = []; 585 this.queuesize = 0; 586 587 // Loop through the files and find any name clashes with existing files. 588 var i; 589 for (i=0; i<files.length; i++) { 590 if (this.options.maxbytes > 0 && files[i].size > this.options.maxbytes) { 591 // Check filesize before attempting to upload. 592 this.print_msg(M.util.get_string('maxbytesforfile', 'moodle', files[i].name), 'error'); 593 this.uploadqueue = []; // No uploads if one file is too big. 594 return; 595 } 596 597 if (this.has_name_clash(files[i].name)) { 598 this.renamequeue.push(files[i]); 599 } else { 600 if (!this.add_to_upload_queue(files[i], files[i].name, false)) { 601 return false; 602 } 603 } 604 this.queuesize += files[i].size; 605 } 606 return true; 607 }, 608 609 /** 610 * Add a single file to the uploadqueue, whilst checking the maxfiles limit 611 * @param File file - the file to add 612 * @param string filename - the name to give the file on upload 613 * @param bool overwrite - true to overwrite the existing file 614 * @return bool true if added successfully 615 */ 616 add_to_upload_queue: function(file, filename, overwrite) { 617 if (!overwrite) { 618 this.currentfilecount++; 619 } 620 // The value for "unlimited files" is -1, so 0 should mean 0. 621 if (this.options.maxfiles >= 0 && this.currentfilecount > this.options.maxfiles) { 622 // Too many files - abort entire upload. 623 this.uploadqueue = []; 624 this.renamequeue = []; 625 this.print_msg(M.util.get_string('maxfilesreached', 'moodle', this.options.maxfiles), 'error'); 626 return false; 627 } 628 // The new file will cause the area to reach its limit, we cancel the upload of all files. 629 // -1 is the value defined by FILE_AREA_MAX_BYTES_UNLIMITED. 630 if (this.options.areamaxbytes > -1) { 631 var sizereached = this.currentareasize + this.queuesize + file.size; 632 if (sizereached > this.options.areamaxbytes) { 633 this.uploadqueue = []; 634 this.renamequeue = []; 635 this.print_msg(M.util.get_string('maxareabytesreached', 'moodle'), 'error'); 636 return false; 637 } 638 } 639 this.uploadqueue.push({file:file, filename:filename, overwrite:overwrite}); 640 return true; 641 }, 642 643 /** 644 * Take the next file from the renamequeue and ask the user what to do with 645 * it. Called recursively until the queue is empty, then calls do_upload. 646 * @return void 647 */ 648 process_renames: function() { 649 if (this.renamequeue.length == 0) { 650 // All rename processing complete - start the actual upload. 651 this.do_upload(); 652 return; 653 } 654 var multiplefiles = (this.renamequeue.length > 1); 655 656 // Get the next file from the rename queue. 657 var file = this.renamequeue.shift(); 658 // Generate a non-conflicting name for it. 659 var newname = this.generate_unique_name(file.name); 660 661 // If the user has clicked on overwrite/rename ALL then process 662 // this file, as appropriate, then process the rest of the queue. 663 if (this.overwriteall) { 664 this.add_to_upload_queue(file, file.name, true); 665 this.process_renames(); 666 return; 667 } 668 if (this.renameall) { 669 this.add_to_upload_queue(file, newname, false); 670 this.process_renames(); 671 return; 672 } 673 674 // Ask the user what to do with this file. 675 var self = this; 676 677 var process_dlg_node; 678 if (multiplefiles) { 679 process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfilemultiple); 680 } else { 681 process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfile); 682 } 683 var node = process_dlg_node; 684 node.generateID(); 685 var process_dlg = new Y.Panel({ 686 srcNode : node, 687 headerContent: M.str.repository.fileexistsdialogheader, 688 zIndex : 8000, 689 centered : true, 690 modal : true, 691 visible : false, 692 render : true, 693 buttons : {} 694 }); 695 process_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']}); 696 697 // Overwrite original. 698 node.one('.fp-dlg-butoverwrite').on('click', function(e) { 699 e.preventDefault(); 700 process_dlg.hide(); 701 self.add_to_upload_queue(file, file.name, true); 702 self.process_renames(); 703 }, this); 704 705 // Rename uploaded file. 706 node.one('.fp-dlg-butrename').on('click', function(e) { 707 e.preventDefault(); 708 process_dlg.hide(); 709 self.add_to_upload_queue(file, newname, false); 710 self.process_renames(); 711 }, this); 712 713 // Cancel all uploads. 714 node.one('.fp-dlg-butcancel').on('click', function(e) { 715 e.preventDefault(); 716 process_dlg.hide(); 717 if (self.callbackcancel) { 718 self.callbackcancel(); 719 } 720 }, this); 721 722 // When we are at the file limit, only allow 'overwrite', not rename. 723 if (this.currentfilecount == this.options.maxfiles) { 724 node.one('.fp-dlg-butrename').setStyle('display', 'none'); 725 if (multiplefiles) { 726 node.one('.fp-dlg-butrenameall').setStyle('display', 'none'); 727 } 728 } 729 730 // If there are more files still to go, offer the 'overwrite/rename all' options. 731 if (multiplefiles) { 732 // Overwrite all original files. 733 node.one('.fp-dlg-butoverwriteall').on('click', function(e) { 734 e.preventDefault(); 735 process_dlg.hide(); 736 this.overwriteall = true; 737 self.add_to_upload_queue(file, file.name, true); 738 self.process_renames(); 739 }, this); 740 741 // Rename all new files. 742 node.one('.fp-dlg-butrenameall').on('click', function(e) { 743 e.preventDefault(); 744 process_dlg.hide(); 745 this.renameall = true; 746 self.add_to_upload_queue(file, newname, false); 747 self.process_renames(); 748 }, this); 749 } 750 node.one('.fp-dlg-text').setContent(M.util.get_string('fileexists', 'moodle', file.name)); 751 process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', newname)); 752 753 // Destroy the dialog once it has been hidden. 754 process_dlg.after('visibleChange', function(e) { 755 if (!process_dlg.get('visible')) { 756 process_dlg.destroy(true); 757 } 758 }); 759 760 process_dlg.show(); 761 }, 762 763 /** 764 * Checks if there is already a file with the given name in the current folder 765 * or in the list of already uploading files 766 * @param string filename - the name to test 767 * @return bool true if the name already exists 768 */ 769 has_name_clash: function(filename) { 770 // Check against the already uploaded files 771 var i; 772 for (i=0; i<this.currentfiles.length; i++) { 773 if (filename == this.currentfiles[i].filename) { 774 return true; 775 } 776 } 777 // Check against the uploading files that have already been processed 778 for (i=0; i<this.uploadqueue.length; i++) { 779 if (filename == this.uploadqueue[i].filename) { 780 return true; 781 } 782 } 783 return false; 784 }, 785 786 /** 787 * Gets a unique file name 788 * 789 * @param string filename 790 * @return string the unique filename generated 791 */ 792 generate_unique_name: function(filename) { 793 // Loop through increating numbers until a unique name is found. 794 while (this.has_name_clash(filename)) { 795 filename = increment_filename(filename); 796 } 797 return filename; 798 }, 799 800 /** 801 * Upload the next file from the uploadqueue - called recursively after each 802 * upload is complete, then handles the callback to the filemanager/filepicker 803 * @param lastresult - the last result from the server 804 */ 805 do_upload: function(lastresult) { 806 if (this.uploadqueue.length > 0) { 807 var filedetails = this.uploadqueue.shift(); 808 this.upload_file(filedetails.file, filedetails.filename, filedetails.overwrite); 809 } else { 810 this.uploadfinished(lastresult); 811 } 812 }, 813 814 /** 815 * Run the callback to the filemanager/filepicker 816 */ 817 uploadfinished: function(lastresult) { 818 this.callback(lastresult); 819 }, 820 821 /** 822 * Upload a single file via an AJAX call to the 'upload' repository. Automatically 823 * calls do_upload as each upload completes. 824 * @param File file - the file to upload 825 * @param string filename - the name to give the file 826 * @param bool overwrite - true if the existing file should be overwritten 827 */ 828 upload_file: function(file, filename, overwrite) { 829 830 // This would be an ideal place to use the Y.io function 831 // however, this does not support data encoded using the 832 // FormData object, which is needed to transfer data from 833 // the DataTransfer object into an XMLHTTPRequest 834 // This can be converted when the YUI issue has been integrated: 835 // http://yuilibrary.com/projects/yui3/ticket/2531274 836 var xhr = new XMLHttpRequest(); 837 var self = this; 838 839 // Update the progress bar 840 xhr.upload.addEventListener('progress', function(e) { 841 if (e.lengthComputable && self.callbackprogress) { 842 var percentage = Math.round((e.loaded * 100) / e.total); 843 self.callbackprogress(filename, percentage); 844 } 845 }, false); 846 847 xhr.onreadystatechange = function() { // Process the server response 848 if (xhr.readyState == 4) { 849 if (xhr.status == 200) { 850 var result = JSON.parse(xhr.responseText); 851 if (result) { 852 if (result.error) { 853 self.print_msg(result.error, 'error'); // TODO add filename? 854 self.uploadfinished(); 855 } else { 856 // Only update the filepicker if there were no errors 857 if (result.event == 'fileexists') { 858 // Do not worry about this, as we only care about the last 859 // file uploaded, with the filepicker 860 result.file = result.newfile.filename; 861 result.url = result.newfile.url; 862 } 863 result.client_id = self.options.clientid; 864 if (self.callbackprogress) { 865 self.callbackprogress(filename, 100); 866 } 867 } 868 } 869 self.do_upload(result); // continue uploading 870 } else { 871 self.print_msg(M.util.get_string('serverconnection', 'error'), 'error'); 872 self.uploadfinished(); 873 } 874 } 875 }; 876 877 // Prepare the data to send 878 var formdata = new FormData(); 879 formdata.append('action', 'upload'); 880 formdata.append('repo_upload_file', file); // The FormData class allows us to attach a file 881 formdata.append('sesskey', M.cfg.sesskey); 882 formdata.append('repo_id', this.repositoryid); 883 formdata.append('itemid', this.options.itemid); 884 if (this.options.author) { 885 formdata.append('author', this.options.author); 886 } 887 if (this.options.filemanager) { // Filepickers do not have folders 888 formdata.append('savepath', this.options.filemanager.currentpath); 889 } 890 formdata.append('title', filename); 891 if (overwrite) { 892 formdata.append('overwrite', 1); 893 } 894 if (this.options.contextid) { 895 formdata.append('ctx_id', this.options.contextid); 896 } 897 898 // Accepted types can be either a string or an array, but an array is 899 // expected in the processing script, so make sure we are sending an array 900 if (this.options.acceptedtypes.constructor == Array) { 901 for (var i=0; i<this.options.acceptedtypes.length; i++) { 902 formdata.append('accepted_types[]', this.options.acceptedtypes[i]); 903 } 904 } else { 905 formdata.append('accepted_types[]', this.options.acceptedtypes); 906 } 907 908 // Send the file & required details 909 xhr.open("POST", this.api, true); 910 xhr.send(formdata); 911 return true; 912 } 913 }); 914 915 dnduploadhelper.init(Y, options); 916 };
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |