[ Index ] |
PHP Cross Reference of Unnamed Project |
[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.util.get_string('error', 'moodle'); 547 if (type != 'error') { 548 type = 'info'; // one of only two types excepted 549 header = M.util.get_string('info', 'moodle'); 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 var maxbytesdisplay = this.display_size(this.options.maxbytes); 593 this.print_msg(M.util.get_string('maxbytesfile', 'error', { 594 file: files[i].name, 595 size: maxbytesdisplay 596 }), 'error'); 597 this.uploadqueue = []; // No uploads if one file is too big. 598 return; 599 } 600 601 if (this.has_name_clash(files[i].name)) { 602 this.renamequeue.push(files[i]); 603 } else { 604 if (!this.add_to_upload_queue(files[i], files[i].name, false)) { 605 return false; 606 } 607 } 608 this.queuesize += files[i].size; 609 } 610 return true; 611 }, 612 613 /** 614 * Generate the display for file size 615 * @param int size The size to convert to human readable form 616 * @return string 617 */ 618 display_size: function(size) { 619 // This is snippet of code (with some changes) is from the display_size function in moodlelib. 620 var gb = M.util.get_string('sizegb', 'moodle'), 621 mb = M.util.get_string('sizemb', 'moodle'), 622 kb = M.util.get_string('sizekb', 'moodle'), 623 b = M.util.get_string('sizeb', 'moodle'); 624 625 if (size >= 1073741824) { 626 size = Math.round(size / 1073741824 * 10) / 10 + gb; 627 } else if (size >= 1048576) { 628 size = Math.round(size / 1048576 * 10) / 10 + mb; 629 } else if (size >= 1024) { 630 size = Math.round(size / 1024 * 10) / 10 + kb; 631 } else { 632 size = parseInt(size, 10) + ' ' + b; 633 } 634 635 return size; 636 }, 637 638 /** 639 * Add a single file to the uploadqueue, whilst checking the maxfiles limit 640 * @param File file - the file to add 641 * @param string filename - the name to give the file on upload 642 * @param bool overwrite - true to overwrite the existing file 643 * @return bool true if added successfully 644 */ 645 add_to_upload_queue: function(file, filename, overwrite) { 646 if (!overwrite) { 647 this.currentfilecount++; 648 } 649 // The value for "unlimited files" is -1, so 0 should mean 0. 650 if (this.options.maxfiles >= 0 && this.currentfilecount > this.options.maxfiles) { 651 // Too many files - abort entire upload. 652 this.uploadqueue = []; 653 this.renamequeue = []; 654 this.print_msg(M.util.get_string('maxfilesreached', 'moodle', this.options.maxfiles), 'error'); 655 return false; 656 } 657 // The new file will cause the area to reach its limit, we cancel the upload of all files. 658 // -1 is the value defined by FILE_AREA_MAX_BYTES_UNLIMITED. 659 if (this.options.areamaxbytes > -1) { 660 var sizereached = this.currentareasize + this.queuesize + file.size; 661 if (sizereached > this.options.areamaxbytes) { 662 this.uploadqueue = []; 663 this.renamequeue = []; 664 this.print_msg(M.util.get_string('maxareabytesreached', 'moodle'), 'error'); 665 return false; 666 } 667 } 668 this.uploadqueue.push({file:file, filename:filename, overwrite:overwrite}); 669 return true; 670 }, 671 672 /** 673 * Take the next file from the renamequeue and ask the user what to do with 674 * it. Called recursively until the queue is empty, then calls do_upload. 675 * @return void 676 */ 677 process_renames: function() { 678 if (this.renamequeue.length == 0) { 679 // All rename processing complete - start the actual upload. 680 this.do_upload(); 681 return; 682 } 683 var multiplefiles = (this.renamequeue.length > 1); 684 685 // Get the next file from the rename queue. 686 var file = this.renamequeue.shift(); 687 // Generate a non-conflicting name for it. 688 var newname = this.generate_unique_name(file.name); 689 690 // If the user has clicked on overwrite/rename ALL then process 691 // this file, as appropriate, then process the rest of the queue. 692 if (this.overwriteall) { 693 this.add_to_upload_queue(file, file.name, true); 694 this.process_renames(); 695 return; 696 } 697 if (this.renameall) { 698 this.add_to_upload_queue(file, newname, false); 699 this.process_renames(); 700 return; 701 } 702 703 // Ask the user what to do with this file. 704 var self = this; 705 706 var process_dlg_node; 707 if (multiplefiles) { 708 process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfilemultiple); 709 } else { 710 process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfile); 711 } 712 var node = process_dlg_node; 713 node.generateID(); 714 var process_dlg = new Y.Panel({ 715 srcNode : node, 716 headerContent: M.util.get_string('fileexistsdialogheader', 'repository'), 717 zIndex : 8000, 718 centered : true, 719 modal : true, 720 visible : false, 721 render : true, 722 buttons : {} 723 }); 724 process_dlg.plug(Y.Plugin.Drag,{handles:['#'+node.get('id')+' .yui3-widget-hd']}); 725 726 // Overwrite original. 727 node.one('.fp-dlg-butoverwrite').on('click', function(e) { 728 e.preventDefault(); 729 process_dlg.hide(); 730 self.add_to_upload_queue(file, file.name, true); 731 self.process_renames(); 732 }, this); 733 734 // Rename uploaded file. 735 node.one('.fp-dlg-butrename').on('click', function(e) { 736 e.preventDefault(); 737 process_dlg.hide(); 738 self.add_to_upload_queue(file, newname, false); 739 self.process_renames(); 740 }, this); 741 742 // Cancel all uploads. 743 node.one('.fp-dlg-butcancel').on('click', function(e) { 744 e.preventDefault(); 745 process_dlg.hide(); 746 if (self.callbackcancel) { 747 self.callbackcancel(); 748 } 749 }, this); 750 751 // When we are at the file limit, only allow 'overwrite', not rename. 752 if (this.currentfilecount == this.options.maxfiles) { 753 node.one('.fp-dlg-butrename').setStyle('display', 'none'); 754 if (multiplefiles) { 755 node.one('.fp-dlg-butrenameall').setStyle('display', 'none'); 756 } 757 } 758 759 // If there are more files still to go, offer the 'overwrite/rename all' options. 760 if (multiplefiles) { 761 // Overwrite all original files. 762 node.one('.fp-dlg-butoverwriteall').on('click', function(e) { 763 e.preventDefault(); 764 process_dlg.hide(); 765 this.overwriteall = true; 766 self.add_to_upload_queue(file, file.name, true); 767 self.process_renames(); 768 }, this); 769 770 // Rename all new files. 771 node.one('.fp-dlg-butrenameall').on('click', function(e) { 772 e.preventDefault(); 773 process_dlg.hide(); 774 this.renameall = true; 775 self.add_to_upload_queue(file, newname, false); 776 self.process_renames(); 777 }, this); 778 } 779 node.one('.fp-dlg-text').setContent(M.util.get_string('fileexists', 'moodle', file.name)); 780 process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', newname)); 781 782 // Destroy the dialog once it has been hidden. 783 process_dlg.after('visibleChange', function(e) { 784 if (!process_dlg.get('visible')) { 785 process_dlg.destroy(true); 786 } 787 }); 788 789 process_dlg.show(); 790 }, 791 792 /** 793 * Checks if there is already a file with the given name in the current folder 794 * or in the list of already uploading files 795 * @param string filename - the name to test 796 * @return bool true if the name already exists 797 */ 798 has_name_clash: function(filename) { 799 // Check against the already uploaded files 800 var i; 801 for (i=0; i<this.currentfiles.length; i++) { 802 if (filename == this.currentfiles[i].filename) { 803 return true; 804 } 805 } 806 // Check against the uploading files that have already been processed 807 for (i=0; i<this.uploadqueue.length; i++) { 808 if (filename == this.uploadqueue[i].filename) { 809 return true; 810 } 811 } 812 return false; 813 }, 814 815 /** 816 * Gets a unique file name 817 * 818 * @param string filename 819 * @return string the unique filename generated 820 */ 821 generate_unique_name: function(filename) { 822 // Loop through increating numbers until a unique name is found. 823 while (this.has_name_clash(filename)) { 824 filename = increment_filename(filename); 825 } 826 return filename; 827 }, 828 829 /** 830 * Upload the next file from the uploadqueue - called recursively after each 831 * upload is complete, then handles the callback to the filemanager/filepicker 832 * @param lastresult - the last result from the server 833 */ 834 do_upload: function(lastresult) { 835 if (this.uploadqueue.length > 0) { 836 var filedetails = this.uploadqueue.shift(); 837 this.upload_file(filedetails.file, filedetails.filename, filedetails.overwrite); 838 } else { 839 this.uploadfinished(lastresult); 840 } 841 }, 842 843 /** 844 * Run the callback to the filemanager/filepicker 845 */ 846 uploadfinished: function(lastresult) { 847 this.callback(lastresult); 848 }, 849 850 /** 851 * Upload a single file via an AJAX call to the 'upload' repository. Automatically 852 * calls do_upload as each upload completes. 853 * @param File file - the file to upload 854 * @param string filename - the name to give the file 855 * @param bool overwrite - true if the existing file should be overwritten 856 */ 857 upload_file: function(file, filename, overwrite) { 858 859 // This would be an ideal place to use the Y.io function 860 // however, this does not support data encoded using the 861 // FormData object, which is needed to transfer data from 862 // the DataTransfer object into an XMLHTTPRequest 863 // This can be converted when the YUI issue has been integrated: 864 // http://yuilibrary.com/projects/yui3/ticket/2531274 865 var xhr = new XMLHttpRequest(); 866 var self = this; 867 868 // Update the progress bar 869 xhr.upload.addEventListener('progress', function(e) { 870 if (e.lengthComputable && self.callbackprogress) { 871 var percentage = Math.round((e.loaded * 100) / e.total); 872 self.callbackprogress(filename, percentage); 873 } 874 }, false); 875 876 xhr.onreadystatechange = function() { // Process the server response 877 if (xhr.readyState == 4) { 878 if (xhr.status == 200) { 879 var result = JSON.parse(xhr.responseText); 880 if (result) { 881 if (result.error) { 882 self.print_msg(result.error, 'error'); // TODO add filename? 883 self.uploadfinished(); 884 } else { 885 // Only update the filepicker if there were no errors 886 if (result.event == 'fileexists') { 887 // Do not worry about this, as we only care about the last 888 // file uploaded, with the filepicker 889 result.file = result.newfile.filename; 890 result.url = result.newfile.url; 891 } 892 result.client_id = self.options.clientid; 893 if (self.callbackprogress) { 894 self.callbackprogress(filename, 100); 895 } 896 } 897 } 898 self.do_upload(result); // continue uploading 899 } else { 900 self.print_msg(M.util.get_string('serverconnection', 'error'), 'error'); 901 self.uploadfinished(); 902 } 903 } 904 }; 905 906 // Prepare the data to send 907 var formdata = new FormData(); 908 formdata.append('repo_upload_file', file); // The FormData class allows us to attach a file 909 formdata.append('sesskey', M.cfg.sesskey); 910 formdata.append('repo_id', this.repositoryid); 911 formdata.append('itemid', this.options.itemid); 912 if (this.options.author) { 913 formdata.append('author', this.options.author); 914 } 915 if (this.options.filemanager) { // Filepickers do not have folders 916 formdata.append('savepath', this.options.filemanager.currentpath); 917 } 918 formdata.append('title', filename); 919 if (overwrite) { 920 formdata.append('overwrite', 1); 921 } 922 if (this.options.contextid) { 923 formdata.append('ctx_id', this.options.contextid); 924 } 925 926 // Accepted types can be either a string or an array, but an array is 927 // expected in the processing script, so make sure we are sending an array 928 if (this.options.acceptedtypes.constructor == Array) { 929 for (var i=0; i<this.options.acceptedtypes.length; i++) { 930 formdata.append('accepted_types[]', this.options.acceptedtypes[i]); 931 } 932 } else { 933 formdata.append('accepted_types[]', this.options.acceptedtypes); 934 } 935 936 // Send the file & required details. 937 var uploadUrl = this.api; 938 if (uploadUrl.indexOf('?') !== -1) { 939 uploadUrl += '&action=upload'; 940 } else { 941 uploadUrl += '?action=upload'; 942 } 943 xhr.open("POST", uploadUrl, true); 944 xhr.send(formdata); 945 return true; 946 } 947 }); 948 949 dnduploadhelper.init(Y, options); 950 };
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |