mirror of
https://github.com/moodle/moodle.git
synced 2025-05-08 17:26:13 +02:00
MDL-22504 Drag and drop upload course - enables upload of files, text and urls to a course page
This commit is contained in:
parent
aa753ac24f
commit
32528f94e4
course
lang/en
lib
mod
folder
page
resource
url
repository/upload
theme/base/style
@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die;
|
||||
|
||||
require_once($CFG->libdir.'/completionlib.php');
|
||||
require_once($CFG->libdir.'/filelib.php');
|
||||
require_once($CFG->libdir.'/dnduploadlib.php');
|
||||
|
||||
define('COURSE_MAX_LOGS_PER_PAGE', 1000); // records
|
||||
define('COURSE_MAX_RECENT_PERIOD', 172800); // Two days, in seconds
|
||||
@ -4557,4 +4558,7 @@ function include_course_ajax($course, $modules = array(), $config = null) {
|
||||
foreach ($modules as $module => $modname) {
|
||||
$PAGE->requires->string_for_js('pluginname', $module);
|
||||
}
|
||||
|
||||
// Load drag and drop upload AJAX.
|
||||
dndupload_add_to_course($course);
|
||||
}
|
||||
|
39
lang/en/dndupload.php
Normal file
39
lang/en/dndupload.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Strings for component 'dndupload', language 'en', branch 'master'
|
||||
*
|
||||
* @package core
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['actionchoice'] = 'What do you want to do with the file \'{$a}\'?';
|
||||
$string['addfilehere'] = 'Add file(s) here';
|
||||
$string['addlinkhere'] = 'Add link here';
|
||||
$string['addpagehere'] = 'Add page here';
|
||||
$string['dndworking'] = 'Drag and drop files, text or links onto course sections to upload them';
|
||||
$string['errorcreatingactivity'] = 'Unable to create an instance of activity \'{$a}\'';
|
||||
$string['errorfiletoobig'] = 'The file was bigger than the limit of {$a} bytes';
|
||||
$string['errornouploadrepo'] = 'There is no upload repository enabled for this site';
|
||||
$string['filetoolarge'] = 'is too large to upload';
|
||||
$string['nameforlink'] = 'What do you want to call this link?';
|
||||
$string['nameforpage'] = 'What do you want to call this page?';
|
||||
$string['noajax'] = 'AJAX course editing must be enabled for drag and drop upload to work';
|
||||
$string['nofilereader'] = 'Your browser does not support drag and drop upload of files';
|
||||
$string['noscript'] = 'Javascript must be enabled for drag and drop upload to work';
|
||||
$string['servererror'] = 'An error occurred whilst communicating with the server';
|
879
lib/ajax/dndupload.js
Normal file
879
lib/ajax/dndupload.js
Normal file
@ -0,0 +1,879 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Javascript library for enableing a drag and drop upload to courses
|
||||
*
|
||||
* @package moodlecore
|
||||
* @subpackage course
|
||||
* @copyright 2012 Davo Smith
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
M.course_dndupload = {
|
||||
// YUI object.
|
||||
Y: null,
|
||||
// URL for upload requests
|
||||
url: M.cfg.wwwroot + '/lib/ajax/dndupload.php',
|
||||
// maximum size of files allowed in this form
|
||||
maxbytes: 0,
|
||||
// ID of the course we are on
|
||||
courseid: null,
|
||||
// Data about the different file/data handlers that are available
|
||||
handlers: null,
|
||||
// Nasty hack to distinguish between dragenter(first entry),
|
||||
// dragenter+dragleave(moving between child elements) and dragleave (leaving element)
|
||||
entercount: 0,
|
||||
// Used to keep track of the section we are dragging across - to make
|
||||
// spotting movement between sections more reliable
|
||||
currentsection: null,
|
||||
// Used to store the pending uploads whilst the user is being asked for further input
|
||||
uploadqueue: null,
|
||||
// True if the there is currently a dialog being shown (asking for a name, or giving a
|
||||
// choice of file handlers)
|
||||
uploaddialog: false,
|
||||
// An array containing the last selected file handler for each file type
|
||||
lastselected: null,
|
||||
|
||||
// The following are used to identify specific parts of the course page
|
||||
|
||||
// The type of HTML element that is a course section
|
||||
sectiontypename: 'li',
|
||||
// The classes that an element must have to be identified as a course section
|
||||
sectionclasses: ['section', 'main'],
|
||||
// The ID of the main content area of the page (for adding the 'status' div)
|
||||
pagecontentid: 'page-content',
|
||||
// The selector identifying the list of modules within a section (note changing this may require
|
||||
// changes to the get_mods_element function)
|
||||
modslistselector: 'ul.section',
|
||||
|
||||
/**
|
||||
* Initalise the drag and drop upload interface
|
||||
* Note: one and only one of options.filemanager and options.formcallback must be defined
|
||||
*
|
||||
* @param Y the YUI object
|
||||
* @param object options {
|
||||
* courseid: ID of the course we are on
|
||||
* maxbytes: maximum size of files allowed in this form
|
||||
* handlers: Data about the different file/data handlers that are available
|
||||
* }
|
||||
*/
|
||||
init: function(Y, options) {
|
||||
this.Y = Y;
|
||||
|
||||
if (!this.browser_supported()) {
|
||||
return; // Browser does not support the required functionality
|
||||
}
|
||||
|
||||
this.maxbytes = options.maxbytes;
|
||||
this.courseid = options.courseid;
|
||||
this.handlers = options.handlers;
|
||||
this.uploadqueue = new Array();
|
||||
this.lastselected = new Array();
|
||||
|
||||
var sectionselector = this.sectiontypename + '.' + this.sectionclasses.join('.');
|
||||
var sections = this.Y.all(sectionselector);
|
||||
if (sections.isEmpty()) {
|
||||
return; // No sections - incompatible course format or front page.
|
||||
}
|
||||
sections.each( function(el) {
|
||||
this.add_preview_element(el);
|
||||
this.init_events(el);
|
||||
}, this);
|
||||
|
||||
var div = this.add_status_div();
|
||||
div.setContent(M.util.get_string('dndworking', 'core_dndupload'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a div element to tell the user that drag and drop upload
|
||||
* is available (or to explain why it is not available)
|
||||
* @return the DOM element to add messages to
|
||||
*/
|
||||
add_status_div: function() {
|
||||
var div = document.createElement('div');
|
||||
div.id = 'dndupload-status';
|
||||
var coursecontents = document.getElementById(this.pagecontentid);
|
||||
if (coursecontents) {
|
||||
coursecontents.insertBefore(div, coursecontents.firstChild);
|
||||
}
|
||||
return this.Y.one(div);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the browser has the required functionality
|
||||
* @return true if browser supports drag/drop upload
|
||||
*/
|
||||
browser_supported: function() {
|
||||
|
||||
if (typeof FileReader == 'undefined') {
|
||||
return false;
|
||||
}
|
||||
if (typeof FormData == 'undefined') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialise drag events on node container, all events need
|
||||
* to be processed for drag and drop to work
|
||||
* @param el the element to add events to
|
||||
*/
|
||||
init_events: function(el) {
|
||||
this.Y.on('dragenter', this.drag_enter, el, this);
|
||||
this.Y.on('dragleave', this.drag_leave, el, this);
|
||||
this.Y.on('dragover', this.drag_over, el, this);
|
||||
this.Y.on('drop', this.drop, el, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Work out which course section a given element is in
|
||||
* @param el the child DOM element within the section
|
||||
* @return the DOM element representing the section
|
||||
*/
|
||||
get_section: function(el) {
|
||||
var sectionclasses = this.sectionclasses;
|
||||
return el.ancestor( function(test) {
|
||||
var i;
|
||||
for (i=0; i<sectionclasses.length; i++) {
|
||||
if (!test.hasClass(sectionclasses[i])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Work out the number of the section we have been dropped on to, from the section element
|
||||
* @param DOMElement section the selected section
|
||||
* @return int the section number
|
||||
*/
|
||||
get_section_number: function(section) {
|
||||
var sectionid = section.get('id').split('-');
|
||||
if (sectionid.length < 2 || sectionid[0] != 'section') {
|
||||
return false;
|
||||
}
|
||||
return parseInt(sectionid[1]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the event includes data of the given type
|
||||
* @param e the event details
|
||||
* @param type the data type to check for
|
||||
* @return true if the data type is found in the event data
|
||||
*/
|
||||
types_includes: function(e, type) {
|
||||
var i;
|
||||
var types = e._event.dataTransfer.types;
|
||||
for (i=0; i<types.length; i++) {
|
||||
if (types[i] == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Look through the event data, checking it against the registered data types
|
||||
* (in order of priority) and return details of the first matching data type
|
||||
* @param e the event details
|
||||
* @return mixed false if not found or an object {
|
||||
* realtype: the type as given by the browser
|
||||
* addmessage: the message to show to the user during dragging
|
||||
* namemessage: the message for requesting a name for the resource from the user
|
||||
* type: the identifier of the type (may match several 'realtype's)
|
||||
* }
|
||||
*/
|
||||
drag_type: function(e) {
|
||||
if (this.types_includes(e, 'Files')) {
|
||||
return {
|
||||
realtype: 'Files',
|
||||
addmessage: M.util.get_string('addfilehere', 'core_dndupload'),
|
||||
namemessage: null, // Should not be asked for anyway
|
||||
type: 'Files'
|
||||
};
|
||||
}
|
||||
|
||||
// Check each of the registered types
|
||||
var types = this.handlers.types;
|
||||
for (var i=0; i<types.length; i++) {
|
||||
// Check each of the different identifiers for this type
|
||||
var dttypes = types[i].datatransfertypes;
|
||||
for (var j=0; j<dttypes.length; j++) {
|
||||
if (this.types_includes(e, dttypes[j])) {
|
||||
return {
|
||||
realtype: dttypes[j],
|
||||
addmessage: types[i].addmessage,
|
||||
namemessage: types[i].namemessage,
|
||||
type: types[i].identifier,
|
||||
handlers: types[i].handlers
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // No types we can handle
|
||||
},
|
||||
|
||||
/**
|
||||
* Check the content of the drag/drop includes a type we can handle, then, if
|
||||
* it is, notify the browser that we want to handle it
|
||||
* @param event e
|
||||
* @return string type of the event or false
|
||||
*/
|
||||
check_drag: function(e) {
|
||||
var type = this.drag_type(e);
|
||||
if (type) {
|
||||
// Notify browser that we will handle this drag/drop
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return type;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a dragenter event: add a suitable 'add here' message
|
||||
* when a drag event occurs, containing a registered data type
|
||||
* @param e event data
|
||||
* @return false to prevent the event from continuing to be processed
|
||||
*/
|
||||
drag_enter: function(e) {
|
||||
if (!(type = this.check_drag(e))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var section = this.get_section(e.currentTarget);
|
||||
if (!section) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.currentsection && this.currentsection != section) {
|
||||
this.currentsection = section;
|
||||
this.entercount = 1;
|
||||
} else {
|
||||
this.entercount++;
|
||||
if (this.entercount > 2) {
|
||||
this.entercount = 2;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.show_preview_element(section, type);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a dragleave event: remove the 'add here' message (if present)
|
||||
* @param e event data
|
||||
* @return false to prevent the event from continuing to be processed
|
||||
*/
|
||||
drag_leave: function(e) {
|
||||
if (!this.check_drag(e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.entercount--;
|
||||
if (this.entercount == 1) {
|
||||
return false;
|
||||
}
|
||||
this.entercount = 0;
|
||||
this.currentsection = null;
|
||||
|
||||
this.hide_preview_element();
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a dragover event: just prevent the browser default (necessary
|
||||
* to allow drag and drop handling to work)
|
||||
* @param e event data
|
||||
* @return false to prevent the event from continuing to be processed
|
||||
*/
|
||||
drag_over: function(e) {
|
||||
this.check_drag(e);
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a drop event: hide the 'add here' message, check the attached
|
||||
* data type and start the upload process
|
||||
* @param e event data
|
||||
* @return false to prevent the event from continuing to be processed
|
||||
*/
|
||||
drop: function(e) {
|
||||
if (!(type = this.check_drag(e))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.hide_preview_element();
|
||||
|
||||
// Work out the number of the section we are on (from its id)
|
||||
var section = this.get_section(e.currentTarget);
|
||||
var sectionnumber = this.get_section_number(section);
|
||||
|
||||
// Process the file or the included data
|
||||
if (type.type == 'Files') {
|
||||
var files = e._event.dataTransfer.files;
|
||||
for (var i=0, f; f=files[i]; i++) {
|
||||
this.handle_file(f, section, sectionnumber);
|
||||
}
|
||||
} else {
|
||||
var contents = e._event.dataTransfer.getData(type.realtype);
|
||||
if (contents) {
|
||||
this.handle_item(type, contents, section, sectionnumber);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find or create the 'ul' element that contains all of the module
|
||||
* instances in this section
|
||||
* @param section the DOM element representing the section
|
||||
* @return false to prevent the event from continuing to be processed
|
||||
*/
|
||||
get_mods_element: function(section) {
|
||||
// Find the 'ul' containing the list of mods
|
||||
var modsel = section.one(this.modslistselector);
|
||||
if (!modsel) {
|
||||
// Create the above 'ul' if it doesn't exist
|
||||
var modsel = document.createElement('ul');
|
||||
modsel.className = 'section img-text';
|
||||
var contentel = section.get('children').pop();
|
||||
var brel = contentel.get('children').pop();
|
||||
contentel.insertBefore(modsel, brel);
|
||||
modsel = this.Y.one(modsel);
|
||||
}
|
||||
|
||||
return modsel;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new dummy item to the list of mods, to be replaced by a real
|
||||
* item & link once the AJAX upload call has completed
|
||||
* @param name the label to show in the element
|
||||
* @param section the DOM element reperesenting the course section
|
||||
* @return DOM element containing the new item
|
||||
*/
|
||||
add_resource_element: function(name, section) {
|
||||
var modsel = this.get_mods_element(section);
|
||||
|
||||
var resel = {
|
||||
parent: modsel,
|
||||
li: document.createElement('li'),
|
||||
div: document.createElement('div'),
|
||||
a: document.createElement('a'),
|
||||
icon: document.createElement('img'),
|
||||
namespan: document.createElement('span'),
|
||||
progressouter: document.createElement('span'),
|
||||
progress: document.createElement('span')
|
||||
};
|
||||
|
||||
resel.li.className = 'activity resource modtype_resource';
|
||||
|
||||
resel.div.className = 'mod-indent';
|
||||
resel.li.appendChild(resel.div);
|
||||
|
||||
resel.a.href = '#';
|
||||
resel.div.appendChild(resel.a);
|
||||
|
||||
resel.icon.src = M.util.image_url('i/ajaxloader');
|
||||
resel.a.appendChild(resel.icon);
|
||||
|
||||
resel.a.appendChild(document.createTextNode(' '));
|
||||
|
||||
resel.namespan.className = 'instancename';
|
||||
resel.namespan.innerHTML = name;
|
||||
resel.a.appendChild(resel.namespan);
|
||||
|
||||
resel.div.appendChild(document.createTextNode(' '));
|
||||
|
||||
resel.progressouter.className = 'dndupload-progress-outer';
|
||||
resel.progress.className = 'dndupload-progress-inner';
|
||||
resel.progress.innerHTML = ' ';
|
||||
resel.progressouter.appendChild(resel.progress);
|
||||
resel.div.appendChild(resel.progressouter);
|
||||
|
||||
modsel.insertBefore(resel.li, modsel.get('children').pop()); // Leave the 'preview element' at the bottom
|
||||
|
||||
return resel;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide any visible dndupload-preview elements on the page
|
||||
*/
|
||||
hide_preview_element: function() {
|
||||
this.Y.all('li.dndupload-preview').addClass('dndupload-hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Unhide the preview element for the given section and set it to display
|
||||
* the correct message
|
||||
* @param section the YUI node representing the selected course section
|
||||
* @param type the details of the data type detected in the drag (including the message to display)
|
||||
*/
|
||||
show_preview_element: function(section, type) {
|
||||
this.hide_preview_element();
|
||||
var preview = section.one('li.dndupload-preview').removeClass('dndupload-hidden');
|
||||
preview.one('span').setContent(type.addmessage);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add the preview element to a course section. Note: this needs to be done before 'addEventListener'
|
||||
* is called, otherwise Firefox will ignore events generated when the mouse is over the preview
|
||||
* element (instead of passing them up to the parent element)
|
||||
* @param section the YUI node representing the selected course section
|
||||
*/
|
||||
add_preview_element: function(section) {
|
||||
var modsel = this.get_mods_element(section);
|
||||
var preview = {
|
||||
li: document.createElement('li'),
|
||||
div: document.createElement('div'),
|
||||
icon: document.createElement('img'),
|
||||
namespan: document.createElement('span')
|
||||
};
|
||||
|
||||
preview.li.className = 'dndupload-preview activity resource modtype_resource dndupload-hidden';
|
||||
|
||||
preview.div.className = 'mod-indent';
|
||||
preview.li.appendChild(preview.div);
|
||||
|
||||
preview.icon.src = M.util.image_url('t/addfile');
|
||||
preview.div.appendChild(preview.icon);
|
||||
|
||||
preview.div.appendChild(document.createTextNode(' '));
|
||||
|
||||
preview.namespan.className = 'instancename';
|
||||
preview.namespan.innerHTML = M.util.get_string('addfilehere', 'core_dndupload');
|
||||
preview.div.appendChild(preview.namespan);
|
||||
|
||||
modsel.appendChild(preview.li);
|
||||
},
|
||||
|
||||
/**
|
||||
* Find the registered handler for the given file type. If there is more than one, ask the
|
||||
* user which one to use. Then upload the file to the server
|
||||
* @param file the details of the file, taken from the FileList in the drop event
|
||||
* @param section the DOM element representing the selected course section
|
||||
* @param sectionnumber the number of the selected course section
|
||||
*/
|
||||
handle_file: function(file, section, sectionnumber) {
|
||||
var handlers = new Array();
|
||||
var filehandlers = this.handlers.filehandlers;
|
||||
var extension = '';
|
||||
var dotpos = file.name.lastIndexOf('.');
|
||||
if (dotpos != -1) {
|
||||
extension = file.name.substr(dotpos+1, file.name.length);
|
||||
}
|
||||
|
||||
for (var i=0; i<filehandlers.length; i++) {
|
||||
if (filehandlers[i].extension == '*' || filehandlers[i].extension == extension) {
|
||||
handlers.push(filehandlers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (handlers.length == 0) {
|
||||
// No handlers at all (not even 'resource'?)
|
||||
return;
|
||||
}
|
||||
|
||||
if (handlers.length == 1) {
|
||||
this.upload_file(file, section, sectionnumber, handlers[0].module);
|
||||
return;
|
||||
}
|
||||
|
||||
this.file_handler_dialog(handlers, extension, file, section, sectionnumber);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a dialog box, allowing the user to choose what to do with the file they are uploading
|
||||
* @param handlers the available handlers to choose between
|
||||
* @param extension the extension of the file being uploaded
|
||||
* @param file the File object being uploaded
|
||||
* @param section the DOM element of the section being uploaded to
|
||||
* @param sectionnumber the number of the selected course section
|
||||
*/
|
||||
file_handler_dialog: function(handlers, extension, file, section, sectionnumber) {
|
||||
if (this.uploaddialog) {
|
||||
var details = new Object();
|
||||
details.isfile = true;
|
||||
details.handlers = handlers;
|
||||
details.extension = extension;
|
||||
details.file = file;
|
||||
details.section = section;
|
||||
details.sectionnumber = sectionnumber;
|
||||
this.uploadqueue.push(details);
|
||||
return;
|
||||
}
|
||||
this.uploaddialog = true;
|
||||
|
||||
var timestamp = new Date().getTime();
|
||||
var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
|
||||
var content = '';
|
||||
var sel;
|
||||
if (extension in this.lastselected) {
|
||||
sel = this.lastselected[extension];
|
||||
} else {
|
||||
sel = handlers[0].module;
|
||||
}
|
||||
content += '<p>'+M.util.get_string('actionchoice', 'core_dndupload', file.name)+'</p>';
|
||||
content += '<div id="dndupload_handlers'+uploadid+'">';
|
||||
for (var i=0; i<handlers.length; i++) {
|
||||
var id = 'dndupload_handler'+uploadid+handlers[i].module;
|
||||
var checked = (handlers[i].module == sel) ? 'checked="checked" ' : '';
|
||||
content += '<input type="radio" name="handler" value="'+handlers[i].module+'" id="'+id+'" '+checked+'/>';
|
||||
content += ' <label for="'+id+'">';
|
||||
content += handlers[i].message;
|
||||
content += '</label><br/>';
|
||||
}
|
||||
content += '</div>';
|
||||
|
||||
var Y = this.Y;
|
||||
var self = this;
|
||||
var panel = new Y.Panel({
|
||||
bodyContent: content,
|
||||
width: 350,
|
||||
zIndex: 5,
|
||||
centered: true,
|
||||
modal: true,
|
||||
visible: true,
|
||||
render: true,
|
||||
buttons: [{
|
||||
value: M.util.get_string('upload', 'core'),
|
||||
action: function(e) {
|
||||
e.preventDefault();
|
||||
// Find out which module was selected
|
||||
var module = false;
|
||||
var div = Y.one('#dndupload_handlers'+uploadid);
|
||||
div.all('input').each(function(input) {
|
||||
if (input.get('checked')) {
|
||||
module = input.get('value');
|
||||
}
|
||||
});
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
panel.hide();
|
||||
// Remember this selection for next time
|
||||
self.lastselected[extension] = module;
|
||||
// Do the upload
|
||||
self.upload_file(file, section, sectionnumber, module);
|
||||
},
|
||||
section: Y.WidgetStdMod.FOOTER
|
||||
},{
|
||||
value: M.util.get_string('cancel', 'core'),
|
||||
action: function(e) {
|
||||
e.preventDefault();
|
||||
panel.hide();
|
||||
},
|
||||
section: Y.WidgetStdMod.FOOTER
|
||||
}]
|
||||
});
|
||||
// When the panel is hidden - destroy it and then check for other pending uploads
|
||||
panel.after("visibleChange", function(e) {
|
||||
if (!panel.get('visible')) {
|
||||
panel.destroy(true);
|
||||
self.check_upload_queue();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Check to see if there are any other dialog boxes to show, now that the current one has
|
||||
* been dealt with
|
||||
*/
|
||||
check_upload_queue: function() {
|
||||
this.uploaddialog = false;
|
||||
if (this.uploadqueue.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var details = this.uploadqueue.shift();
|
||||
if (details.isfile) {
|
||||
this.file_handler_dialog(details.handlers, details.extension, details.file, details.section, details.sectionnumber);
|
||||
} else {
|
||||
this.handle_item(details.type, details.contents, details.section, details.sectionnumber);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Do the file upload: show the dummy element, use an AJAX call to send the data
|
||||
* to the server, update the progress bar for the file, then replace the dummy
|
||||
* element with the real information once the AJAX call completes
|
||||
* @param file the details of the file, taken from the FileList in the drop event
|
||||
* @param section the DOM element representing the selected course section
|
||||
* @param sectionnumber the number of the selected course section
|
||||
*/
|
||||
upload_file: function(file, section, sectionnumber, module) {
|
||||
|
||||
// This would be an ideal place to use the Y.io function
|
||||
// however, this does not support data encoded using the
|
||||
// FormData object, which is needed to transfer data from
|
||||
// the DataTransfer object into an XMLHTTPRequest
|
||||
// This can be converted when the YUI issue has been integrated:
|
||||
// http://yuilibrary.com/projects/yui3/ticket/2531274
|
||||
var xhr = new XMLHttpRequest();
|
||||
var self = this;
|
||||
|
||||
if (file.size > this.maxbytes) {
|
||||
alert("'"+file.name+"' "+M.util.get_string('filetoolarge', 'core_dndupload'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the file to the display
|
||||
var resel = this.add_resource_element(file.name, section);
|
||||
|
||||
// Update the progress bar as the file is uploaded
|
||||
xhr.upload.addEventListener('progress', function(e) {
|
||||
if (e.lengthComputable) {
|
||||
var percentage = Math.round((e.loaded * 100) / e.total);
|
||||
resel.progress.style.width = percentage + '%';
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Wait for the AJAX call to complete, then update the
|
||||
// dummy element with the returned details
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
var result = JSON.parse(xhr.responseText);
|
||||
if (result) {
|
||||
if (result.error == 0) {
|
||||
// All OK - update the dummy element
|
||||
resel.icon.src = result.icon;
|
||||
resel.a.href = result.link;
|
||||
resel.namespan.innerHTML = result.name;
|
||||
resel.div.removeChild(resel.progressouter);
|
||||
resel.li.id = result.elementid;
|
||||
resel.div.innerHTML += result.commands;
|
||||
if (result.onclick) {
|
||||
resel.a.onclick = result.onclick;
|
||||
}
|
||||
self.add_editing(result.elementid);
|
||||
} else {
|
||||
// Error - remove the dummy element
|
||||
resel.parent.removeChild(resel.li);
|
||||
alert(result.error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(M.util.get_string('servererror', 'core_dndupload'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the data to send
|
||||
var formData = new FormData();
|
||||
formData.append('repo_upload_file', file);
|
||||
formData.append('sesskey', M.cfg.sesskey);
|
||||
formData.append('course', this.courseid);
|
||||
formData.append('section', sectionnumber);
|
||||
formData.append('module', module);
|
||||
formData.append('type', 'Files');
|
||||
|
||||
// Send the AJAX call
|
||||
xhr.open("POST", this.url, true);
|
||||
xhr.send(formData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a dialog box to gather the name of the resource / activity to be created
|
||||
* from the uploaded content
|
||||
* @param type the details of the type of content
|
||||
* @param contents the contents to be uploaded
|
||||
* @section the DOM element for the section being uploaded to
|
||||
* @sectionnumber the number of the section being uploaded to
|
||||
*/
|
||||
handle_item: function(type, contents, section, sectionnumber) {
|
||||
if (type.handlers.length == 0) {
|
||||
// Nothing to handle this - should not have got here
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.uploaddialog) {
|
||||
var details = new Object();
|
||||
details.isfile = false;
|
||||
details.type = type;
|
||||
details.contents = contents;
|
||||
details.section = section;
|
||||
details.setcionnumber = sectionnumber;
|
||||
this.uploadqueue.push(details);
|
||||
return;
|
||||
}
|
||||
this.uploaddialog = true;
|
||||
|
||||
var timestamp = new Date().getTime();
|
||||
var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
|
||||
var nameid = 'dndupload_handler_name'+uploadid;
|
||||
var content = '';
|
||||
content += '<label for="'+nameid+'">'+type.namemessage+'</label>';
|
||||
content += ' <input type="text" id="'+nameid+'" value="" />';
|
||||
if (type.handlers.length > 1) {
|
||||
content += '<div id="dndupload_handlers'+uploadid+'">';
|
||||
var sel = type.handlers[0].module;
|
||||
for (var i=0; i<type.handlers.length; i++) {
|
||||
var id = 'dndupload_handler'+uploadid;
|
||||
var checked = (type.handlers[i].module == sel) ? 'checked="checked" ' : '';
|
||||
content += '<input type="radio" name="handler" value="'+type.handlers[i].module+'" id="'+id+'" '+checked+'/>';
|
||||
content += ' <label for="'+id+'">';
|
||||
content += type.handlers[i].message;
|
||||
content += '</label><br/>';
|
||||
}
|
||||
content += '</div>';
|
||||
}
|
||||
|
||||
var Y = this.Y;
|
||||
var self = this;
|
||||
var panel = new Y.Panel({
|
||||
bodyContent: content,
|
||||
width: 350,
|
||||
zIndex: 5,
|
||||
centered: true,
|
||||
modal: true,
|
||||
visible: true,
|
||||
render: true,
|
||||
buttons: [{
|
||||
value: M.util.get_string('upload', 'core'),
|
||||
action: function(e) {
|
||||
e.preventDefault();
|
||||
var name = Y.one('#dndupload_handler_name'+uploadid).get('value');
|
||||
name = name.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // Trim
|
||||
if (name == '') {
|
||||
return;
|
||||
}
|
||||
var module = false;
|
||||
if (type.handlers.length > 1) {
|
||||
// Find out which module was selected
|
||||
var div = Y.one('#dndupload_handlers'+uploadid);
|
||||
div.all('input').each(function(input) {
|
||||
if (input.get('checked')) {
|
||||
module = input.get('value');
|
||||
}
|
||||
});
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
module = type.handlers[0].module;
|
||||
}
|
||||
panel.hide();
|
||||
// Do the upload
|
||||
self.upload_item(name, type.type, contents, section, sectionnumber, module);
|
||||
},
|
||||
section: Y.WidgetStdMod.FOOTER
|
||||
},{
|
||||
value: M.util.get_string('cancel', 'core'),
|
||||
action: function(e) {
|
||||
e.preventDefault();
|
||||
panel.hide();
|
||||
},
|
||||
section: Y.WidgetStdMod.FOOTER
|
||||
}]
|
||||
});
|
||||
// When the panel is hidden - destroy it and then check for other pending uploads
|
||||
panel.after("visibleChange", function(e) {
|
||||
if (!panel.get('visible')) {
|
||||
panel.destroy(true);
|
||||
self.check_upload_queue();
|
||||
}
|
||||
});
|
||||
// Focus on the 'name' box
|
||||
Y.one('#'+nameid).focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload any data types that are not files: display a dummy resource element, send
|
||||
* the data to the server, update the progress bar for the file, then replace the
|
||||
* dummy element with the real information once the AJAX call completes
|
||||
* @param name the display name for the resource / activity to create
|
||||
* @param type the details of the data type found in the drop event
|
||||
* @param contents the actual data that was dropped
|
||||
* @param section the DOM element representing the selected course section
|
||||
* @param sectionnumber the number of the selected course section
|
||||
*/
|
||||
upload_item: function(name, type, contents, section, sectionnumber, module) {
|
||||
|
||||
// This would be an ideal place to use the Y.io function
|
||||
// however, this does not support data encoded using the
|
||||
// FormData object, which is needed to transfer data from
|
||||
// the DataTransfer object into an XMLHTTPRequest
|
||||
// This can be converted when the YUI issue has been integrated:
|
||||
// http://yuilibrary.com/projects/yui3/ticket/2531274
|
||||
var xhr = new XMLHttpRequest();
|
||||
var self = this;
|
||||
|
||||
// Add the item to the display
|
||||
var resel = this.add_resource_element(name, section);
|
||||
|
||||
// Wait for the AJAX call to complete, then update the
|
||||
// dummy element with the returned details
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
var result = JSON.parse(xhr.responseText);
|
||||
if (result) {
|
||||
if (result.error == 0) {
|
||||
// All OK - update the dummy element
|
||||
resel.icon.src = result.icon;
|
||||
resel.a.href = result.link;
|
||||
resel.namespan.innerHTML = result.name;
|
||||
resel.div.removeChild(resel.progressouter);
|
||||
resel.li.id = result.elementid;
|
||||
resel.div.innerHTML += result.commands;
|
||||
if (result.onclick) {
|
||||
resel.a.onclick = result.onclick;
|
||||
}
|
||||
self.add_editing(result.elementid, sectionnumber);
|
||||
} else {
|
||||
// Error - remove the dummy element
|
||||
resel.parent.removeChild(resel.li);
|
||||
alert(result.error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(M.util.get_string('servererror', 'core_dndupload'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the data to send
|
||||
var formData = new FormData();
|
||||
formData.append('contents', contents);
|
||||
formData.append('displayname', name);
|
||||
formData.append('sesskey', M.cfg.sesskey);
|
||||
formData.append('course', this.courseid);
|
||||
formData.append('section', sectionnumber);
|
||||
formData.append('type', type);
|
||||
formData.append('module', module);
|
||||
|
||||
// Send the data
|
||||
xhr.open("POST", this.url, true);
|
||||
xhr.send(formData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Call the AJAX course editing initialisation to add the editing tools
|
||||
* to the newly-created resource link
|
||||
* @param elementid the id of the DOM element containing the new resource link
|
||||
* @param sectionnumber the number of the selected course section
|
||||
*/
|
||||
add_editing: function(elementid) {
|
||||
YUI().use('moodle-course-coursebase', function(Y) {
|
||||
M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);
|
||||
});
|
||||
}
|
||||
};
|
39
lib/ajax/dndupload.php
Normal file
39
lib/ajax/dndupload.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Starting point for drag and drop course uploads
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @copyright 2012 Davo smith
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define('AJAX_SCRIPT', true);
|
||||
|
||||
require_once(dirname(dirname(dirname(__FILE__))).'/config.php');
|
||||
require_once($CFG->libdir.'/dnduploadlib.php');
|
||||
|
||||
$courseid = required_param('course', PARAM_INT);
|
||||
$section = required_param('section', PARAM_INT);
|
||||
$type = required_param('type', PARAM_TEXT);
|
||||
$modulename = required_param('module', PARAM_PLUGIN);
|
||||
$displayname = optional_param('displayname', null, PARAM_TEXT);
|
||||
$contents = optional_param('contents', null, PARAM_RAW); // It will be up to each plugin to clean this data, before saving it.
|
||||
|
||||
$dndproc = new dndupload_processor($courseid, $section, $type, $modulename);
|
||||
$dndproc->process($displayname, $contents);
|
635
lib/dnduploadlib.php
Normal file
635
lib/dnduploadlib.php
Normal file
@ -0,0 +1,635 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Library to handle drag and drop course uploads
|
||||
*
|
||||
* @package core
|
||||
* @subpackage lib
|
||||
* @copyright 2012 Davo smith
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/repository/lib.php');
|
||||
require_once($CFG->dirroot.'/repository/upload/lib.php');
|
||||
require_once($CFG->dirroot.'/course/lib.php');
|
||||
|
||||
/**
|
||||
* Add the Javascript to enable drag and drop upload to a course page
|
||||
*
|
||||
* @param object $course The currently displayed course
|
||||
*/
|
||||
function dndupload_add_to_course($course) {
|
||||
global $CFG, $PAGE;
|
||||
|
||||
// Get all handlers.
|
||||
$handler = new dndupload_handler($course);
|
||||
|
||||
// Add the javascript to the page.
|
||||
$jsmodule = array(
|
||||
'name' => 'dndupload',
|
||||
'fullpath' => new moodle_url('/lib/ajax/dndupload.js'),
|
||||
'strings' => array(
|
||||
array('addfilehere', 'core_dndupload'),
|
||||
array('dndworking', 'core_dndupload'),
|
||||
array('filetoolarge', 'core_dndupload'),
|
||||
array('nofilereader', 'core_dndupload'),
|
||||
array('noajax', 'core_dndupload'),
|
||||
array('actionchoice', 'core_dndupload'),
|
||||
array('servererror', 'core_dndupload'),
|
||||
array('upload', 'core'),
|
||||
array('cancel', 'core')
|
||||
),
|
||||
'requires' => array('node', 'event', 'panel', 'json')
|
||||
);
|
||||
$vars = array(
|
||||
array('courseid' => $course->id,
|
||||
'maxbytes' => get_max_upload_file_size($CFG->maxbytes, $course->maxbytes),
|
||||
'handlers' => $handler->get_js_data())
|
||||
);
|
||||
|
||||
$PAGE->requires->js_init_call('M.course_dndupload.init', $vars, true, $jsmodule);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stores all the information about the available dndupload handlers
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2012 Davo Smith
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dndupload_handler {
|
||||
|
||||
/**
|
||||
* @var array A list of all registered mime types that can be dropped onto a course
|
||||
* along with the modules that will handle them.
|
||||
*/
|
||||
protected $types = array();
|
||||
|
||||
/**
|
||||
* @var array A list of the different file types (extensions) that different modules
|
||||
* will handle.
|
||||
*/
|
||||
protected $filehandlers = array();
|
||||
|
||||
/**
|
||||
* Gather a list of dndupload handlers from the different mods
|
||||
*
|
||||
* @param object $course The course this is being added to (to check course_allowed_module() )
|
||||
*/
|
||||
public function __construct($course) {
|
||||
global $CFG;
|
||||
|
||||
// Add some default types to handle.
|
||||
// Note: 'Files' type is hard-coded into the Javascript as this needs to be ...
|
||||
// ... treated a little differently.
|
||||
$this->add_type('url', array('url', 'text/uri-list'), get_string('addlinkhere', 'core_dndupload'),
|
||||
get_string('nameforlink', 'core_dndupload'), 10);
|
||||
$this->add_type('text/html', array('text/html'), get_string('addpagehere', 'core_dndupload'),
|
||||
get_string('nameforpage', 'core_dndupload'), 20);
|
||||
$this->add_type('text', array('text', 'text/plain'), get_string('addpagehere', 'core_dndupload'),
|
||||
get_string('nameforpage', 'core_dndupload'), 30);
|
||||
|
||||
// Loop through all modules to find handlers.
|
||||
$mods = get_plugin_list('mod');
|
||||
foreach ($mods as $modname => $modpath) {
|
||||
if (!course_allowed_module($course, $modname)) {
|
||||
continue;
|
||||
}
|
||||
$resp = plugin_callback('mod', $modname, 'dndupload', 'register', array());
|
||||
if (!$resp) {
|
||||
continue;
|
||||
}
|
||||
if (isset($resp['files'])) {
|
||||
foreach ($resp['files'] as $file) {
|
||||
$this->add_file_handler($file['extension'], $modname, $file['message']);
|
||||
}
|
||||
}
|
||||
if (isset($resp['addtypes'])) {
|
||||
foreach ($resp['addtypes'] as $type) {
|
||||
if (isset($type['priority'])) {
|
||||
$priority = $type['priority'];
|
||||
} else {
|
||||
$priority = 100;
|
||||
}
|
||||
$this->add_type($type['identifier'], $type['datatransfertypes'],
|
||||
$type['addmessage'], $type['namemessage'], $priority);
|
||||
}
|
||||
}
|
||||
if (isset($resp['types'])) {
|
||||
foreach ($resp['types'] as $type) {
|
||||
$this->add_type_handler($type['identifier'], $modname, $type['message']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to add a new mime type that can be drag and dropped onto a
|
||||
* course displayed in a browser window
|
||||
*
|
||||
* @param string $identifier The name that this type will be known as
|
||||
* @param array $datatransfertypes An array of the different types in the browser
|
||||
* 'dataTransfer.types' object that will map to this type
|
||||
* @param string $addmessage The message to display in the browser when this type is being
|
||||
* dragged onto the page
|
||||
* @param string $namemessage The message to pop up when asking for the name to give the
|
||||
* course module instance when it is created
|
||||
* @param int $priority Controls the order in which types are checked by the browser (mainly
|
||||
* needed to check for 'text' last as that is usually given as fallback)
|
||||
*/
|
||||
public function add_type($identifier, $datatransfertypes, $addmessage, $namemessage, $priority=100) {
|
||||
if ($this->is_known_type($identifier)) {
|
||||
throw new coding_exception("Type $identifier is already registered");
|
||||
}
|
||||
|
||||
$add = new stdClass;
|
||||
$add->identifier = $identifier;
|
||||
$add->datatransfertypes = $datatransfertypes;
|
||||
$add->addmessage = $addmessage;
|
||||
$add->namemessage = $namemessage;
|
||||
$add->priority = $priority;
|
||||
$add->handlers = array();
|
||||
|
||||
$this->types[] = $add;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to declare that a particular module will handle a particular type
|
||||
* of dropped data
|
||||
*
|
||||
* @param string $type The name of the type (as declared in add_type)
|
||||
* @param string $module The name of the module to handle this type
|
||||
* @param string $message The message to show the user if more than one handler is registered
|
||||
* for a type and the user needs to make a choice between them
|
||||
*/
|
||||
public function add_type_handler($type, $module, $message) {
|
||||
foreach ($this->types as $knowntype) {
|
||||
if ($knowntype->identifier == $type) {
|
||||
$add = new stdClass;
|
||||
$add->type = $type;
|
||||
$add->module = $module;
|
||||
$add->message = $message;
|
||||
|
||||
$knowntype->handlers[] = $add;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new coding_exception("Trying to add handler for unknown type $type");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to declare that a particular module will handle a particular type
|
||||
* of dropped file
|
||||
*
|
||||
* @param string $extension The file extension to handle ('*' for all types)
|
||||
* @param string $module The name of the module to handle this type
|
||||
* @param string $message The message to show the user if more than one handler is registered
|
||||
* for a type and the user needs to make a choice between them
|
||||
*/
|
||||
public function add_file_handler($extension, $module, $message) {
|
||||
$add = new stdClass;
|
||||
$add->extension = $extension;
|
||||
$add->module = $module;
|
||||
$add->message = $message;
|
||||
|
||||
$this->filehandlers[] = $add;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the type has been registered
|
||||
*
|
||||
* @param string $type The identifier of the type you are interested in
|
||||
* @return bool True if the type is registered
|
||||
*/
|
||||
public function is_known_type($type) {
|
||||
foreach ($this->types as $knowntype) {
|
||||
if ($knowntype->identifier == $type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the module in question has registered to handle the
|
||||
* type given
|
||||
*
|
||||
* @param string $module The name of the module
|
||||
* @param string $type The identifier of the type
|
||||
* @return bool True if the module has registered to handle that type
|
||||
*/
|
||||
public function has_type_handler($module, $type) {
|
||||
foreach ($this->types as $knowntype) {
|
||||
if ($knowntype->identifier == $type) {
|
||||
foreach ($knowntype->handlers as $handler) {
|
||||
if ($handler->module == $module) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the module in question has registered to handle files
|
||||
* with the given extension (or to handle all file types)
|
||||
*
|
||||
* @param string $module The name of the module
|
||||
* @param string $extension The extension of the uploaded file
|
||||
* @return bool True if the module has registered to handle files with
|
||||
* that extension (or to handle all file types)
|
||||
*/
|
||||
public function has_file_handler($module, $extension) {
|
||||
foreach ($this->filehandlers as $handler) {
|
||||
if ($handler->module == $module) {
|
||||
if ($handler->extension == '*' || $handler->extension == $extension) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of the file types that are handled by a particular module
|
||||
*
|
||||
* @param string $module The name of the module to check
|
||||
* @return array of file extensions or string '*'
|
||||
*/
|
||||
public function get_handled_file_types($module) {
|
||||
$types = array();
|
||||
foreach ($this->filehandlers as $handler) {
|
||||
if ($handler->module == $module) {
|
||||
if ($handler->extension == '*') {
|
||||
return '*';
|
||||
} else {
|
||||
// Prepending '.' as otherwise mimeinfo fails.
|
||||
$types[] = '.'.$handler->extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object to pass onto the javascript code with data about all the
|
||||
* registered file / type handlers
|
||||
*
|
||||
* @return object Data to pass on to Javascript code
|
||||
*/
|
||||
public function get_js_data() {
|
||||
$ret = new stdClass;
|
||||
|
||||
// Sort the types by priority.
|
||||
uasort($this->types, array($this, 'type_compare'));
|
||||
|
||||
$ret->types = array();
|
||||
foreach ($this->types as $type) {
|
||||
if (empty($type->handlers)) {
|
||||
continue; // Skip any types without registered handlers.
|
||||
}
|
||||
$ret->types[] = $type;
|
||||
}
|
||||
|
||||
$ret->filehandlers = $this->filehandlers;
|
||||
$uploadrepo = repository::get_instances(array('type' => 'upload'));
|
||||
if (empty($uploadrepo)) {
|
||||
$ret->filehandlers = array(); // No upload repo => no file handlers.
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison function used when sorting types by priority
|
||||
* @param object $type1 first type to compare
|
||||
* @param object $type2 second type to compare
|
||||
* @return integer -1 for $type1 < $type2; 1 for $type1 > $type2; 0 for equal
|
||||
*/
|
||||
protected function type_compare($type1, $type2) {
|
||||
if ($type1->priority < $type2->priority) {
|
||||
return -1;
|
||||
}
|
||||
if ($type1->priority > $type2->priority) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the upload, creating the course module and returning the result
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2012 Davo Smith
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class dndupload_processor {
|
||||
|
||||
/** Returned when no error has occurred */
|
||||
const ERROR_OK = 0;
|
||||
|
||||
/** @var object The course that we are uploading to */
|
||||
protected $course = null;
|
||||
|
||||
/** @var context_course The course context for capability checking */
|
||||
protected $context = null;
|
||||
|
||||
/** @var int The section number we are uploading to */
|
||||
protected $section = null;
|
||||
|
||||
/** @var string The type of upload (e.g. 'Files', 'text/plain') */
|
||||
protected $type = null;
|
||||
|
||||
/** @var object The details of the module type that will be created */
|
||||
protected $module= null;
|
||||
|
||||
/** @var object The course module that has been created */
|
||||
protected $cm = null;
|
||||
|
||||
/** @var dndupload_handler used to check the allowed file types */
|
||||
protected $dnduploadhandler = null;
|
||||
|
||||
/** @var string The name to give the new activity instance */
|
||||
protected $displayname = null;
|
||||
|
||||
/**
|
||||
* Set up some basic information needed to handle the upload
|
||||
*
|
||||
* @param int $courseid The ID of the course we are uploading to
|
||||
* @param int $section The section number we are uploading to
|
||||
* @param string $type The type of upload (as reported by the browser)
|
||||
* @param string $modulename The name of the module requested to handle this upload
|
||||
*/
|
||||
public function __construct($courseid, $section, $type, $modulename) {
|
||||
global $DB;
|
||||
|
||||
$this->course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
|
||||
|
||||
require_login($this->course, false);
|
||||
$this->context = context_course::instance($this->course->id);
|
||||
|
||||
if (!is_number($section) || $section < 0 || $section > $this->course->numsections) {
|
||||
throw new coding_exception("Invalid section number $section");
|
||||
}
|
||||
$this->section = $section;
|
||||
$this->type = $type;
|
||||
|
||||
if (!$this->module = $DB->get_record('modules', array('name' => $modulename))) {
|
||||
throw new coding_exception("Module $modulename does not exist");
|
||||
}
|
||||
|
||||
$this->dnduploadhandler = new dndupload_handler($this->course);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this upload is a 'file' upload
|
||||
*
|
||||
* @return bool true if it is a 'file' upload, false otherwise
|
||||
*/
|
||||
protected function is_file_upload() {
|
||||
return ($this->type == 'Files');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the upload - creating the module in the course and returning the result to the browser
|
||||
*
|
||||
* @param string $displayname optional the name (from the browser) to give the course module instance
|
||||
* @param string $content optional the content of the upload (for non-file uploads)
|
||||
*/
|
||||
public function process($displayname = null, $content = null) {
|
||||
require_capability('moodle/course:manageactivities', $this->context);
|
||||
|
||||
if ($this->is_file_upload()) {
|
||||
require_capability('moodle/course:managefiles', $this->context);
|
||||
}
|
||||
|
||||
require_sesskey();
|
||||
|
||||
$this->displayname = $displayname;
|
||||
|
||||
if ($this->is_file_upload()) {
|
||||
$this->handle_file_upload();
|
||||
} else {
|
||||
$this->handle_other_upload($content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle uploads containing files - create the course module, ask the upload repository
|
||||
* to process the file, ask the mod to set itself up, then return the result to the browser
|
||||
*/
|
||||
protected function handle_file_upload() {
|
||||
global $CFG;
|
||||
|
||||
// Add the file to a draft file area.
|
||||
$draftitemid = file_get_unused_draft_itemid();
|
||||
$maxbytes = get_max_upload_file_size($CFG->maxbytes, $this->course->maxbytes);
|
||||
$types = $this->dnduploadhandler->get_handled_file_types($this->module->name);
|
||||
$repo = repository::get_instances(array('type' => 'upload'));
|
||||
if (empty($repo)) {
|
||||
throw new moodle_exception('errornouploadrepo', 'core_dndupload');
|
||||
}
|
||||
$repo = reset($repo); // Get the first (and only) upload repo.
|
||||
$details = $repo->process_upload(null, $maxbytes, $types, '/', $draftitemid);
|
||||
if (empty($this->displayname)) {
|
||||
$this->displayname = $this->display_name_from_file($details['file']);
|
||||
}
|
||||
|
||||
// Create a course module to hold the new instance.
|
||||
$this->create_course_module();
|
||||
|
||||
// Ask the module to set itself up.
|
||||
$moduledata = $this->prepare_module_data($draftitemid);
|
||||
$instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
|
||||
if ($instanceid === 'invalidfunction') {
|
||||
throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
|
||||
}
|
||||
|
||||
// Finish setting up the course module.
|
||||
$this->finish_setup_course_module($instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle uploads not containing file - create the course module, ask the mod to
|
||||
* set itself up, then return the result to the browser
|
||||
*
|
||||
* @param string $content the content uploaded to the browser
|
||||
*/
|
||||
protected function handle_other_upload($content) {
|
||||
// Create a course module to hold the new instance.
|
||||
$this->create_course_module();
|
||||
|
||||
// Ask the module to set itself up.
|
||||
$moduledata = $this->prepare_module_data(null, $content);
|
||||
$instanceid = plugin_callback('mod', $this->module->name, 'dndupload', 'handle', array($moduledata), 'invalidfunction');
|
||||
if ($instanceid === 'invalidfunction') {
|
||||
throw new coding_exception("{$this->module->name} does not support drag and drop upload (missing {$this->module->name}_dndupload_handle function");
|
||||
}
|
||||
|
||||
// Finish setting up the course module.
|
||||
$this->finish_setup_course_module($instanceid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the name of the mod instance from the name of the file
|
||||
* (remove the extension and convert underscore => space
|
||||
*
|
||||
* @param string $filename the filename of the uploaded file
|
||||
* @return string the display name to use
|
||||
*/
|
||||
protected function display_name_from_file($filename) {
|
||||
$pos = textlib::strrpos($filename, '.');
|
||||
if ($pos) { // Want to skip if $pos === 0 OR $pos === false.
|
||||
$filename = textlib::substr($filename, 0, $pos);
|
||||
}
|
||||
return str_replace('_', ' ', $filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the coursemodule to hold the file/content that has been uploaded
|
||||
*/
|
||||
protected function create_course_module() {
|
||||
if (!course_allowed_module($this->course, $this->module->name)) {
|
||||
throw new coding_exception("The module {$this->module->name} is not allowed to be added to this course");
|
||||
}
|
||||
|
||||
$this->cm = new stdClass();
|
||||
$this->cm->course = $this->course->id;
|
||||
$this->cm->section = $this->section;
|
||||
$this->cm->module = $this->module->id;
|
||||
$this->cm->modulename = $this->module->name;
|
||||
$this->cm->instance = 0; // This will be filled in after we create the instance.
|
||||
$this->cm->visible = 1;
|
||||
$this->cm->groupmode = $this->course->groupmode;
|
||||
$this->cm->groupingid = $this->course->defaultgroupingid;
|
||||
|
||||
if (!$this->cm->id = add_course_module($this->cm)) {
|
||||
throw new coding_exception("Unable to create the course module");
|
||||
}
|
||||
// The following are used inside some few core functions, so may as well set them here.
|
||||
$this->cm->coursemodule = $this->cm->id;
|
||||
$this->cm->groupmodelink = (!$this->course->groupmodeforce);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather together all the details to pass on to the mod, so that it can initialise it's
|
||||
* own database tables
|
||||
*
|
||||
* @param int $draftitemid optional the id of the draft area containing the file (for file uploads)
|
||||
* @param string $content optional the content dropped onto the course (for non-file uploads)
|
||||
* @return object data to pass on to the mod, containing:
|
||||
* string $type the 'type' as registered with dndupload_handler (or 'Files')
|
||||
* object $course the course the upload was for
|
||||
* int $draftitemid optional the id of the draft area containing the files
|
||||
* int $coursemodule id of the course module that has already been created
|
||||
* string $displayname the name to use for this activity (can be overriden by the mod)
|
||||
*/
|
||||
protected function prepare_module_data($draftitemid = null, $content = null) {
|
||||
$data = new stdClass();
|
||||
$data->type = $this->type;
|
||||
$data->course = $this->course;
|
||||
if ($draftitemid) {
|
||||
$data->draftitemid = $draftitemid;
|
||||
} else if ($content) {
|
||||
$data->content = $content;
|
||||
}
|
||||
$data->coursemodule = $this->cm->id;
|
||||
$data->displayname = $this->displayname;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the mod has set itself up, to finish off any course module settings
|
||||
* (set instance id, add to correct section, set visibility, etc.) and send the response
|
||||
*
|
||||
* @param int $instanceid id returned by the mod when it was created
|
||||
*/
|
||||
protected function finish_setup_course_module($instanceid) {
|
||||
global $DB, $USER;
|
||||
|
||||
if (!$instanceid) {
|
||||
// Something has gone wrong - undo everything we can.
|
||||
$modcontext = context_module::instance($this->cm->id);
|
||||
delete_context(CONTEXT_MODULE, $this->cm->id);
|
||||
$DB->delete_records('course_modules', array('id' => $this->cm->id));
|
||||
throw new moodle_exception('errorcreatingactivity', 'core_dndupload');
|
||||
}
|
||||
|
||||
$DB->set_field('course_modules', 'instance', $instanceid, array('id' => $this->cm->id));
|
||||
|
||||
$sectionid = add_mod_to_section($this->cm);
|
||||
$DB->set_field('course_modules', 'section', $sectionid, array('id' => $this->cm->id));
|
||||
|
||||
set_coursemodule_visible($this->cm->id, true);
|
||||
|
||||
// Rebuild the course cache and retrieve the final info about this module.
|
||||
rebuild_course_cache($this->course->id, true);
|
||||
$this->course->modinfo = null; // Otherwise we will just get the old version back again.
|
||||
$info = get_fast_modinfo($this->course);
|
||||
$mod = $info->cms[$this->cm->id];
|
||||
|
||||
// Trigger mod_created event with information about this module.
|
||||
$eventdata = new stdClass();
|
||||
$eventdata->modulename = $mod->modname;
|
||||
$eventdata->name = $mod->name;
|
||||
$eventdata->cmid = $mod->id;
|
||||
$eventdata->courseid = $this->course->id;
|
||||
$eventdata->userid = $USER->id;
|
||||
events_trigger('mod_created', $eventdata);
|
||||
|
||||
add_to_log($this->course->id, "course", "add mod",
|
||||
"../mod/{$mod->modname}/view.php?id=$mod->id",
|
||||
"{$mod->modname} $instanceid");
|
||||
add_to_log($this->course->id, $mod->modname, "add",
|
||||
"view.php?id=$mod->id",
|
||||
"$instanceid", $mod->id);
|
||||
|
||||
if ($this->cm->groupmodelink && plugin_supports('mod', $mod->modname, FEATURE_GROUPS, 0)) {
|
||||
$mod->groupmodelink = $this->cm->groupmodelink;
|
||||
} else {
|
||||
$mod->groupmodelink = false;
|
||||
}
|
||||
|
||||
$this->send_response($mod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the details of the newly created activity back to the client browser
|
||||
*
|
||||
* @param cm_info $mod details of the mod just created
|
||||
*/
|
||||
protected function send_response($mod) {
|
||||
$resp = new stdClass();
|
||||
$resp->error = self::ERROR_OK;
|
||||
$resp->icon = $mod->get_icon_url().'';
|
||||
$resp->name = $mod->name;
|
||||
$resp->link = $mod->get_url().'';
|
||||
$resp->elementid = 'module-'.$mod->id;
|
||||
$resp->commands = make_editing_buttons($mod, true, true, 0, $mod->sectionnum);
|
||||
$resp->onclick = $mod->get_on_click();
|
||||
|
||||
echo json_encode($resp);
|
||||
die();
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
$string['contentheader'] = 'Content';
|
||||
$string['dnduploadmakefolder'] = 'Unzip files and create folder';
|
||||
$string['folder:addinstance'] = 'Add a new folder';
|
||||
$string['folder:managefiles'] = 'Manage files in folder module';
|
||||
$string['folder:view'] = 'View folder content';
|
||||
|
@ -370,3 +370,49 @@ function folder_export_contents($cm, $baseurl) {
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the ability to handle drag and drop file uploads
|
||||
* @return array containing details of the files / types the mod can handle
|
||||
*/
|
||||
function folder_dndupload_register() {
|
||||
return array('files' => array(
|
||||
array('extension' => 'zip', 'message' => get_string('dnduploadmakefolder', 'mod_folder'))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a file that has been uploaded
|
||||
* @param object $uploadinfo details of the file / content that has been uploaded
|
||||
* @return int instance id of the newly created mod
|
||||
*/
|
||||
function folder_dndupload_handle($uploadinfo) {
|
||||
global $DB, $USER;
|
||||
|
||||
$folder = new stdClass();
|
||||
$folder->course = $uploadinfo->course->id;
|
||||
$folder->name = $uploadinfo->displayname;
|
||||
$folder->intro = '<p>'.$uploadinfo->displayname.'</p>';
|
||||
$folder->introformat = FORMAT_HTML;
|
||||
$folder->timemodified = time();
|
||||
|
||||
$folder->id = $DB->insert_record('folder', $folder);
|
||||
|
||||
// Retrieve the file from the draft file area.
|
||||
$context = context_module::instance($uploadinfo->coursemodule);
|
||||
file_save_draft_area_files($uploadinfo->draftitemid, $context->id, 'mod_folder', 'temp', 0, array('subdirs'=>true));
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'mod_folder', 'temp', 0, 'sortorder', false);
|
||||
// Only ever one file - extract the contents.
|
||||
$file = reset($files);
|
||||
|
||||
$success = $file->extract_to_storage(new zip_packer(), $context->id, 'mod_folder', 'content', 0, '/', $USER->id);
|
||||
$fs->delete_area_files($context->id, 'mod_folder', 'temp', 0);
|
||||
|
||||
if ($success) {
|
||||
return $folder->id;
|
||||
}
|
||||
|
||||
$DB->delete_records('folder', array('id' => $folder->id));
|
||||
return false;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
$string['configdisplayoptions'] = 'Select all options that should be available, existing settings are not modified. Hold CTRL key to select multiple fields.';
|
||||
$string['content'] = 'Page content';
|
||||
$string['contentheader'] = 'Content';
|
||||
$string['createpage'] = 'Create a new page';
|
||||
$string['displayoptions'] = 'Available display options';
|
||||
$string['displayselect'] = 'Display';
|
||||
$string['displayselectexplain'] = 'Select display type.';
|
||||
|
@ -484,3 +484,54 @@ function page_export_contents($cm, $baseurl) {
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the ability to handle drag and drop file uploads
|
||||
* @return array containing details of the files / types the mod can handle
|
||||
*/
|
||||
function page_dndupload_register() {
|
||||
return array('types' => array(
|
||||
array('identifier' => 'text/html', 'message' => get_string('createpage', 'page')),
|
||||
array('identifier' => 'text', 'message' => get_string('createpage', 'page'))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a file that has been uploaded
|
||||
* @param object $uploadinfo details of the file / content that has been uploaded
|
||||
* @return int instance id of the newly created mod
|
||||
*/
|
||||
function page_dndupload_handle($uploadinfo) {
|
||||
global $DB, $CFG;
|
||||
require_once("$CFG->libdir/resourcelib.php");
|
||||
|
||||
$config = get_config('page');
|
||||
$display = $config->display;
|
||||
if ($display == RESOURCELIB_DISPLAY_POPUP) {
|
||||
$displayoptions['popupwidth'] = $config->popupwidth;
|
||||
$displayoptions['popupheight'] = $config->popupheight;
|
||||
}
|
||||
$displayoptions['printheading'] = $config->printheading;
|
||||
$displayoptions['printintro'] = $config->printintro;
|
||||
$displayoptions = serialize($displayoptions);
|
||||
|
||||
$page = new stdClass();
|
||||
$page->course = $uploadinfo->course->id;
|
||||
$page->name = $uploadinfo->displayname;
|
||||
$page->intro = '<p>'.$uploadinfo->displayname.'</p>';
|
||||
$page->introformat = FORMAT_HTML;
|
||||
if ($uploadinfo->type == 'text/html') {
|
||||
$page->contentformat = FORMAT_HTML;
|
||||
$page->content = clean_param($uploadinfo->content, PARAM_CLEANHTML);
|
||||
} else {
|
||||
$page->contentformat = FORMAT_PLAIN;
|
||||
$page->content = clean_param($uploadinfo->content, PARAM_TEXT);
|
||||
}
|
||||
$page->display = $display;
|
||||
$page->displayoptions = $displayoptions;
|
||||
$page->timemodified = time();
|
||||
|
||||
$page->id = $DB->insert_record('page', $page);
|
||||
|
||||
return $page->id;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ $string['displayselect_help'] = 'This setting, together with the file type and w
|
||||
* New window - The file is displayed in a new browser window with menus and an address bar';
|
||||
$string['displayselect_link'] = 'mod/file/mod';
|
||||
$string['displayselectexplain'] = 'Choose display type, unfortunately not all types are suitable for all files.';
|
||||
$string['dnduploadresource'] = 'Create file resource';
|
||||
$string['encryptedcode'] = 'Encrypted code';
|
||||
$string['filenotfound'] = 'File not found, sorry.';
|
||||
$string['filterfiles'] = 'Use filters on file content';
|
||||
|
@ -490,3 +490,60 @@ function resource_export_contents($cm, $baseurl) {
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the ability to handle drag and drop file uploads
|
||||
* @return array containing details of the files / types the mod can handle
|
||||
*/
|
||||
function resource_dndupload_register() {
|
||||
return array('files' => array(
|
||||
array('extension' => '*', 'message' => get_string('dnduploadresource', 'mod_resource'))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a file that has been uploaded
|
||||
* @param object $uploadinfo details of the file / content that has been uploaded
|
||||
* @return int instance id of the newly created mod
|
||||
*/
|
||||
function resource_dndupload_handle($uploadinfo) {
|
||||
global $DB, $CFG;
|
||||
require_once("$CFG->libdir/resourcelib.php");
|
||||
|
||||
// Set display options to site defaults.
|
||||
$config = get_config('resource');
|
||||
$display = $config->display;
|
||||
$displayoptions = array();
|
||||
if ($display == RESOURCELIB_DISPLAY_POPUP) {
|
||||
$displayoptions['popupheight'] = $config->popupheight;
|
||||
$displayoptions['popupwidth'] = $config->popupwidth;
|
||||
}
|
||||
if (in_array($display, array(RESOURCELIB_DISPLAY_AUTO, RESOURCELIB_DISPLAY_EMBED, RESOURCELIB_DISPLAY_FRAME))) {
|
||||
$displayoptions['printheading'] = $config->printheading;
|
||||
$displayoptions['printintro'] = $config->printintro;
|
||||
}
|
||||
$displayoptions = serialize($displayoptions);
|
||||
|
||||
// Create the database entry.
|
||||
$resource = new stdClass();
|
||||
$resource->course = $uploadinfo->course->id;
|
||||
$resource->name = $uploadinfo->displayname;
|
||||
$resource->intro = '<p>'.$uploadinfo->displayname.'</p>';
|
||||
$resource->introformat = FORMAT_HTML;
|
||||
$resource->display = $display;
|
||||
$resource->displayoptions = $displayoptions;
|
||||
$resource->timemodified = time();
|
||||
|
||||
$resource->id = $DB->insert_record('resource', $resource);
|
||||
|
||||
// Retrieve the file from the draft file area.
|
||||
$context = context_module::instance($uploadinfo->coursemodule);
|
||||
file_save_draft_area_files($uploadinfo->draftitemid, $context->id, 'mod_resource', 'content', 0, array('subdirs' => false));
|
||||
$fs = get_file_storage();
|
||||
$files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder', false);
|
||||
// Only ever one file - set it as the 'main' file.
|
||||
$file = reset($files);
|
||||
file_set_sortorder($context->id, 'mod_resource', 'content', 0, $file->get_filepath(), $file->get_filename(), 1);
|
||||
|
||||
return $resource->id;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ $string['configframesize'] = 'When a web page or an uploaded file is displayed w
|
||||
$string['configrolesinparams'] = 'Enable if you want to include localized role names in list of available parameter variables.';
|
||||
$string['configsecretphrase'] = 'This secret phrase is used to produce encrypted code value that can be sent to some servers as a parameter. The encrypted code is produced by an md5 value of the current user IP address concatenated with your secret phrase. ie code = md5(IP.secretphrase). Please note that this is not reliable because IP address may change and is often shared by different computers.';
|
||||
$string['contentheader'] = 'Content';
|
||||
$string['createurl'] = 'Create a URL';
|
||||
$string['displayoptions'] = 'Available display options';
|
||||
$string['displayselect'] = 'Display';
|
||||
$string['displayselect_help'] = 'This setting, together with the URL file type and whether the browser allows embedding, determines how the URL is displayed. Options may include:
|
||||
|
@ -343,3 +343,49 @@ function url_export_contents($cm, $baseurl) {
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the ability to handle drag and drop file uploads
|
||||
* @return array containing details of the files / types the mod can handle
|
||||
*/
|
||||
function url_dndupload_register() {
|
||||
return array('types' => array(
|
||||
array('identifier' => 'url', 'message' => get_string('createurl', 'url'))
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a file that has been uploaded
|
||||
* @param object $uploadinfo details of the file / content that has been uploaded
|
||||
* @return int instance id of the newly created mod
|
||||
*/
|
||||
function url_dndupload_handle($uploadinfo) {
|
||||
global $DB, $CFG;
|
||||
require_once("$CFG->libdir/resourcelib.php");
|
||||
|
||||
$config = get_config('url');
|
||||
$display = $config->display;
|
||||
if ($display == RESOURCELIB_DISPLAY_POPUP) {
|
||||
$displayoptions['popupwidth'] = $config->popupwidth;
|
||||
$displayoptions['popupheight'] = $config->popupheight;
|
||||
}
|
||||
if (in_array($display, array(RESOURCELIB_DISPLAY_AUTO, RESOURCELIB_DISPLAY_EMBED, RESOURCELIB_DISPLAY_FRAME))) {
|
||||
$displayoptions['printheading'] = $config->printheading;
|
||||
$displayoptions['printintro'] = $config->printintro;
|
||||
}
|
||||
$displayoptions = serialize($displayoptions);
|
||||
|
||||
$url = new stdClass();
|
||||
$url->course = $uploadinfo->course->id;
|
||||
$url->name = $uploadinfo->displayname;
|
||||
$url->intro = '<p>'.$uploadinfo->displayname.'</p>';
|
||||
$url->introformat = FORMAT_HTML;
|
||||
$url->externalurl = clean_param($uploadinfo->content, PARAM_URL);
|
||||
$url->display = $display;
|
||||
$url->displayoptions = $displayoptions;
|
||||
$url->timemodified = time();
|
||||
|
||||
$url->id = $DB->insert_record('url', $url);
|
||||
|
||||
return $url->id;
|
||||
}
|
||||
|
@ -42,9 +42,31 @@ class repository_upload extends repository {
|
||||
* @return array|bool
|
||||
*/
|
||||
public function upload($saveas_filename, $maxbytes) {
|
||||
global $USER, $CFG;
|
||||
global $CFG;
|
||||
|
||||
$types = optional_param_array('accepted_types', '*', PARAM_RAW);
|
||||
$savepath = optional_param('savepath', '/', PARAM_PATH);
|
||||
$itemid = optional_param('itemid', 0, PARAM_INT);
|
||||
$license = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT);
|
||||
$author = optional_param('author', '', PARAM_TEXT);
|
||||
|
||||
return $this->process_upload($saveas_filename, $maxbytes, $types, $savepath, $itemid, $license, $author);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual processing of the uploaded file
|
||||
* @param string $saveas_filename name to give to the file
|
||||
* @param int $maxbytes maximum file size
|
||||
* @param mixed $types optional array of file extensions that are allowed or '*' for all
|
||||
* @param string $savepath optional path to save the file to
|
||||
* @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
|
||||
* @return object containing details of the file uploaded
|
||||
*/
|
||||
public function process_upload($saveas_filename, $maxbytes, $types = '*', $savepath = '/', $itemid = 0, $license = null, $author = '') {
|
||||
global $USER, $CFG;
|
||||
|
||||
if ((is_array($types) and in_array('*', $types)) or $types == '*') {
|
||||
$this->mimetypes = '*';
|
||||
} else {
|
||||
@ -53,13 +75,17 @@ class repository_upload extends repository {
|
||||
}
|
||||
}
|
||||
|
||||
if ($license == null) {
|
||||
$license = $CFG->sitedefaultlicense;
|
||||
}
|
||||
|
||||
$record = new stdClass();
|
||||
$record->filearea = 'draft';
|
||||
$record->component = 'user';
|
||||
$record->filepath = optional_param('savepath', '/', PARAM_PATH);
|
||||
$record->itemid = optional_param('itemid', 0, PARAM_INT);
|
||||
$record->license = optional_param('license', $CFG->sitedefaultlicense, PARAM_TEXT);
|
||||
$record->author = optional_param('author', '', PARAM_TEXT);
|
||||
$record->filepath = $savepath;
|
||||
$record->itemid = $itemid;
|
||||
$record->license = $license;
|
||||
$record->author = $author;
|
||||
|
||||
$context = get_context_instance(CONTEXT_USER, $USER->id);
|
||||
$elname = 'repo_upload_file';
|
||||
|
@ -127,3 +127,9 @@
|
||||
|
||||
table.category_subcategories {margin-bottom:1em;}
|
||||
table.category_subcategories td {white-space: nowrap;}
|
||||
/* Course drag and drop upload styles */
|
||||
#dndupload-status {width:60%;margin:0 auto;padding:2px;border:1px solid #ddd;text-align:center;background:#ffc}
|
||||
.dndupload-preview {color:#909090;border:1px dashed #909090;}
|
||||
.dndupload-progress-outer {width:70px;border:solid black 1px;height:10px;display:inline-block;margin:0;padding:0;overflow:clip;position:relative;}
|
||||
.dndupload-progress-inner {width:0%;height:100%;background-color:green;display:inline-block;margin:0;padding:0;float:left;}
|
||||
.dndupload-hidden {display:none;}
|
||||
|
Loading…
x
Reference in New Issue
Block a user