├── pix └── icon.gif ├── tinymce ├── img │ └── managefiles.png └── editor_plugin.js ├── README.txt ├── styles.css ├── version.php ├── module.js ├── lang └── en │ └── tinymce_managefiles.php ├── lib.php ├── manage_form.php └── manage.php /pix/icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marinaglancy/moodle-tinymce_managefiles/master/pix/icon.gif -------------------------------------------------------------------------------- /tinymce/img/managefiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marinaglancy/moodle-tinymce_managefiles/master/tinymce/img/managefiles.png -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This plugin will add a button to the TinyMCE text editor in Moodle that allows to 2 | manage files embedded in the current text area. 3 | 4 | To install copy files to lib/editor/tinymce/plugins/managefiles 5 | Please note that Moodle 2.6 already includes this plugin in the standard distribution. 6 | 7 | CHANGES LOG 8 | =========== 9 | 10 | Version 1.3 11 | ----------- 12 | 13 | - Plugin now work correctly in quiz questions (for legacy reasons those textareas 14 | allows subfolders in the filearea attached to the editor). 15 | - Popup automatically opens fullscreen on small screens 16 | - Popup window correctly uses current environment (theme and language of the 17 | current course and/or user). 18 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | #tinymce_managefiles_manageform.hasunusedfiles .managefilesstatus {display:none;} 2 | #tinymce_managefiles_manageform.hasmissingfiles .managefilesstatus {display:inline;} 3 | 4 | #tinymce_managefiles_manageform #deletefiles, 5 | #tinymce_managefiles_manageform #id_deletefiles {display:none;} 6 | #tinymce_managefiles_manageform.hasunusedfiles #deletefiles, 7 | #tinymce_managefiles_manageform.hasunusedfiles #id_deletefiles {display:block;} 8 | #tinymce_managefiles_manageform #deletefiles .felement.fcheckbox, 9 | #tinymce_managefiles_manageform #id_deletefiles .felement.fcheckbox {display:none;} 10 | #tinymce_managefiles_manageform #deletefiles .felement.fcheckbox.isunused, 11 | #tinymce_managefiles_manageform #id_deletefiles .felement.fcheckbox.isunused {display:block;} 12 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * TinyMCE manage files plugin version details. 19 | * 20 | * @package tinymce_managefiles 21 | * @copyright 2013 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $plugin->version = 2013120200; // The current plugin version (Date: YYYYMMDDXX). 28 | $plugin->requires = 2012120300; // Required Moodle version. 29 | $plugin->release = '1.3'; 30 | $plugin->maturity = MATURITY_STABLE; 31 | $plugin->component = 'tinymce_managefiles'; // Full name of the plugin (used for diagnostics). 32 | -------------------------------------------------------------------------------- /module.js: -------------------------------------------------------------------------------- 1 | M.tinymce_managefiles = M.tinymce_managefiles || {} 2 | M.tinymce_managefiles.analysefiles = function(Y) { 3 | var form = Y.one('#tinymce_managefiles_manageform'), 4 | usedfiles, missingfiles = '', i; 5 | if (!form || !window.parent || !window.parent.tinyMCE.activeEditor) { 6 | return; 7 | } 8 | usedfiles = window.parent.tinyMCE.activeEditor.execCommand('mceManageFilesUsedFiles') 9 | var delfilesfieldset = form.one('#deletefiles,#id_deletefiles') 10 | for (i in usedfiles) { 11 | if (!delfilesfieldset.one('.felement.fcheckbox input[name="deletefile[' + usedfiles[i] + ']"]')) { 12 | missingfiles += '
  • ' + usedfiles[i] + '
  • '; 13 | } 14 | } 15 | if (missingfiles !== '') { 16 | form.addClass('hasmissingfiles') 17 | form.one('.managefilesstatus').setContent(M.str.tinymce_managefiles.hasmissingfiles + ' ').addClass('error'); 18 | } 19 | delfilesfieldset.all('.felement.fcheckbox').each(function(el) { 20 | var chb = el.one('input[type=checkbox]'), 21 | match = /^deletefile\[(.*)\]$/.exec(chb.get('name')); 22 | if (match && usedfiles.indexOf(match[1]) === -1) { 23 | el.addClass('isunused') 24 | form.addClass('hasunusedfiles') 25 | } 26 | }); 27 | if (missingfiles === '' && !form.hasClass('hasunusedfiles')) { 28 | form.one('.managefilesstatus').setContent(M.str.tinymce_managefiles.allfilesok); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lang/en/tinymce_managefiles.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * TinyMCE manage files plugin language file 19 | * 20 | * @package tinymce_managefiles 21 | * @copyright 2013 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $string['pluginname'] = 'Manage embedded files'; 26 | $string['manageareafiles'] = 'Manage files embedded in text editor'; 27 | $string['managefiles:desc'] = 'Manage embedded files'; 28 | $string['allfilesok'] = 'There are no missing or unused files'; 29 | $string['hasmissingfiles'] = 'Warning! The following files that are referenced in the text area appear to be missing:'; 30 | $string['refreshfiles'] = 'Refresh the lists of missing and unused files'; 31 | $string['unusedfilesheader'] = 'Unused files'; 32 | $string['unusedfilesdesc'] = 'The following embedded files are not used in the text area:'; 33 | $string['deleteselected'] = 'Delete selected files'; -------------------------------------------------------------------------------- /lib.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | defined('MOODLE_INTERNAL') || die(); 18 | 19 | /** 20 | * Plugin for managing files embedded in the text editor 21 | * 22 | * @package tinymce_managefiles 23 | * @copyright 2013 Marina Glancy 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | class tinymce_managefiles extends editor_tinymce_plugin { 27 | /** @var array list of buttons defined by this plugin */ 28 | protected $buttons = array('managefiles'); 29 | 30 | /** 31 | * Adjusts TinyMCE init parameters for tinymce_managefiles 32 | * 33 | * Adds file area restrictions parameters and actual 'managefiles' button 34 | * 35 | * @param array $params TinyMCE init parameters array 36 | * @param context $context Context where editor is being shown 37 | * @param array $options Options for this editor 38 | */ 39 | protected function update_init_params(array &$params, context $context, 40 | array $options = null) { 41 | global $USER; 42 | 43 | // Add parameters for filemanager 44 | $params['managefiles'] = array('usercontext' => context_user::instance($USER->id)->id); 45 | foreach (array('itemid', 'context', 'areamaxbytes', 'maxbytes', 'subdirs', 'return_types') as $key) { 46 | if (isset($options[$key])) { 47 | if ($key === 'context' && is_object($options[$key])) { 48 | // Just context id is enough 49 | $params['managefiles'][$key] = $options[$key]->id; 50 | } else { 51 | $params['managefiles'][$key] = $options[$key]; 52 | } 53 | } 54 | } 55 | 56 | // Add button after moodlemedia button in advancedbuttons3. 57 | $added = $this->add_button_after($params, 3, 'managefiles', 'moodlemedia', false); 58 | 59 | // So, if no moodlemedia, add after 'image'. 60 | if (!$added) { 61 | $this->add_button_after($params, 3, 'managefiles', 'image'); 62 | } 63 | 64 | // Add JS file, which uses default name. 65 | $this->add_js_plugin($params); 66 | } 67 | 68 | protected function get_sort_order() { 69 | return 310; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /manage_form.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Class tinymce_managefiles_manage_form 19 | * 20 | * @package tinymce_managefiles 21 | * @copyright 2013 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir."/formslib.php"); 28 | 29 | /** 30 | * Form allowing to edit files in one draft area 31 | * 32 | * No buttons are necessary since the draft area files are saved immediately using AJAX 33 | * 34 | * @package tinymce_managefiles 35 | * @copyright 2013 Marina Glancy 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class tinymce_managefiles_manage_form extends moodleform { 39 | function definition() { 40 | global $PAGE; 41 | $mform = $this->_form; 42 | 43 | $itemid = $this->_customdata['draftitemid']; 44 | $options = $this->_customdata['options']; 45 | $files = $this->_customdata['files']; 46 | 47 | $mform->addElement('hidden', 'itemid'); 48 | $mform->setType('itemid', PARAM_INT); 49 | $mform->addElement('hidden', 'maxbytes'); 50 | $mform->setType('maxbytes', PARAM_INT); 51 | $mform->addElement('hidden', 'subdirs'); 52 | $mform->setType('subdirs', PARAM_INT); 53 | $mform->addElement('hidden', 'accepted_types'); 54 | $mform->setType('accepted_types', PARAM_RAW); 55 | $mform->addElement('hidden', 'return_types'); 56 | $mform->setType('return_types', PARAM_INT); 57 | $mform->addElement('hidden', 'context'); 58 | $mform->setType('context', PARAM_INT); 59 | $mform->addElement('hidden', 'areamaxbytes'); 60 | $mform->setType('areamaxbytes', PARAM_INT); 61 | 62 | $mform->addElement('filemanager', 'files_filemanager', '', null, $options); 63 | 64 | $mform->addElement('submit', 'refresh', get_string('refreshfiles', 'tinymce_managefiles')); 65 | $mform->registerNoSubmitButton('refresh'); 66 | 67 | $mform->addElement('static', '', '', 68 | html_writer::tag('div', '', array('class' => 'managefilesstatus'))); 69 | 70 | $mform->addElement('header', 'deletefiles', get_string('unusedfilesheader', 'tinymce_managefiles')); 71 | $mform->addElement('static', '', '', 72 | html_writer::tag('span', get_string('unusedfilesdesc', 'tinymce_managefiles'), array('class' => 'managefilesunuseddesc'))); 73 | foreach ($files as $file) { 74 | $mform->addElement('checkbox', 'deletefile['.$file.']', '', $file); 75 | $mform->setType('deletefile['.$file.']', PARAM_INT); 76 | } 77 | $mform->addElement('submit', 'delete', get_string('deleteselected', 'tinymce_managefiles')); 78 | 79 | $PAGE->requires->js_init_call('M.tinymce_managefiles.analysefiles', array(), true); 80 | $PAGE->requires->strings_for_js(array('allfilesok', 'hasmissingfiles'), 'tinymce_managefiles'); 81 | 82 | $this->set_data(array('files_filemanager' => $itemid, 83 | 'itemid' => $itemid, 84 | 'subdirs' => $options['subdirs'], 85 | 'maxbytes' => $options['maxbytes'], 86 | 'areamaxbytes' => $options['areamaxbytes'], 87 | 'accepted_types' => $options['accepted_types'], 88 | 'return_types' => $options['return_types'], 89 | 'context' => $options['context']->id, 90 | )); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /manage.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Manage files in user draft area attached to texteditor 19 | * 20 | * @package tinymce_managefiles 21 | * @copyright 2013 Marina Glancy 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | require('../../../../../config.php'); 26 | require_once('manage_form.php'); 27 | require_once($CFG->libdir.'/filestorage/file_storage.php'); 28 | 29 | $itemid = required_param('itemid', PARAM_INT); 30 | $maxbytes = optional_param('maxbytes', 0, PARAM_INT); 31 | $subdirs = optional_param('subdirs', 0, PARAM_INT); 32 | $accepted_types = optional_param('accepted_types', '*', PARAM_RAW); // TODO not yet passed to this script 33 | $return_types = optional_param('return_types', null, PARAM_INT); 34 | $areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT); 35 | $contextid = optional_param('context', SYSCONTEXTID, PARAM_INT); 36 | 37 | $context = context::instance_by_id($contextid); 38 | if ($context->contextlevel == CONTEXT_MODULE) { 39 | // Module context. 40 | $cm = $DB->get_record('course_modules', array('id' => $context->instanceid)); 41 | require_login($cm->course, true, $cm); 42 | } else if (($coursecontext = $context->get_course_context(false)) && $coursecontext->id != SITEID) { 43 | // Course context or block inside the course. 44 | require_login($coursecontext->instanceid); 45 | $PAGE->set_context($context); 46 | } else { 47 | // Block that is not inside the course, user or system context. 48 | require_login(); 49 | $PAGE->set_context($context); 50 | } 51 | if (isguestuser()) { 52 | // Guests can never manage files. 53 | print_error('noguest'); 54 | } 55 | 56 | $title = get_string('manageareafiles', 'tinymce_managefiles'); 57 | 58 | $PAGE->set_url('/lib/editor/tinymce/plugins/managefiles/manage.php'); 59 | $PAGE->set_title($title); 60 | $PAGE->set_heading($title); 61 | $PAGE->set_pagelayout('popup'); 62 | 63 | if ($return_types !== null) { 64 | $return_types = $return_types ^ 1; // links are allowed in textarea but never allowed in filemanager 65 | } 66 | 67 | $options = array( 68 | 'subdirs' => $subdirs, 69 | 'maxbytes' => $maxbytes, 70 | 'maxfiles' => -1, 71 | 'accepted_types' => $accepted_types, 72 | 'areamaxbytes' => $areamaxbytes, 73 | 'return_types' => $return_types, 74 | 'context' => $context 75 | ); 76 | 77 | $usercontext = context_user::instance($USER->id); 78 | $fs = get_file_storage(); 79 | $files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $itemid, '/', !empty($subdirs), false); 80 | $filenames = array(); 81 | foreach ($files as $file) { 82 | $filenames[] = ltrim($file->get_filepath(), '/'). $file->get_filename(); 83 | } 84 | 85 | $mform = new tinymce_managefiles_manage_form(null, 86 | array('options' => $options, 'draftitemid' => $itemid, 'files' => $filenames), 87 | 'post', '', array('id' => 'tinymce_managefiles_manageform')); 88 | 89 | if ($data = $mform->get_data()) { 90 | if (!empty($data->deletefile)) { 91 | foreach (array_keys($data->deletefile) as $filename) { 92 | $filepath = '/'; 93 | if (!empty($subdirs) && strlen(dirname($filename)) ) { 94 | $filepath = '/'. dirname($filename). '/'; 95 | } 96 | if ($file = $fs->get_file($usercontext->id, 'user', 'draft', $itemid, 97 | $filepath, basename($filename))) { 98 | $file->delete(); 99 | } 100 | } 101 | $filenames = array_diff($filenames, array_keys($data->deletefile)); 102 | $mform = new tinymce_managefiles_manage_form(null, 103 | array('options' => $options, 'draftitemid' => $itemid, 'files' => $filenames), 104 | 'post', '', array('id' => 'tinymce_managefiles_manageform')); 105 | } 106 | } 107 | 108 | echo $OUTPUT->header(); 109 | $mform->display(); 110 | echo $OUTPUT->footer(); 111 | -------------------------------------------------------------------------------- /tinymce/editor_plugin.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TinyMCE plugin ManageFiles - provides UI to edit files embedded in the text editor. 3 | * 4 | * @author Marina Glancy 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | */ 7 | 8 | (function() { 9 | tinymce.create('tinymce.plugins.MoodleManageFiles', { 10 | /** 11 | * Initializes the plugin, this will be executed after the plugin has been created. 12 | * This call is done before the editor instance has finished it's initialization so use the onInit event 13 | * of the editor instance to intercept that event. 14 | * 15 | * @param {tinymce.Editor} ed Editor instance that the plugin is initialized in. 16 | * @param {string} url Absolute URL to where the plugin is located. 17 | */ 18 | init : function(ed, url) { 19 | ed.addCommand('mceForceRepaint', function() { 20 | var root = ed.dom.getRoot(); 21 | items = root.getElementsByTagName("img"); 22 | for (var i = 0; i < items.length; i++) { 23 | src = items[i].getAttribute('src').replace(/\?\d+$/, ''); 24 | items[i].setAttribute('src', src+'?'+(new Date().getTime())) 25 | } 26 | ed.execCommand('mceRepaint'); 27 | ed.focus(); 28 | }); 29 | 30 | ed.addCommand('mceMaximizeWindow', function(w) { 31 | // This function duplicates the TinyMCE windowManager code when 'maximize' button is pressed. 32 | var vp = ed.dom.getViewPort(), 33 | id = w.id; 34 | // Reduce viewport size to avoid scrollbars 35 | vp.w -= 2; 36 | vp.h -= 2; 37 | 38 | w.oldPos = w.element.getXY(); 39 | w.oldSize = w.element.getSize(); 40 | 41 | w.element.moveTo(vp.x, vp.y); 42 | w.element.resizeTo(vp.w, vp.h); 43 | ed.dom.setStyles(id + '_ifr', {width : vp.w - w.deltaWidth, height : vp.h - w.deltaHeight}); 44 | ed.dom.addClass(id + '_wrapper', 'mceMaximized'); 45 | }); 46 | 47 | ed.addCommand('mceManageFiles', function() { 48 | var managefiles = ed.getParam('managefiles', {}), key, cnt = 0, 49 | fileurl = ed.getParam("moodle_plugin_base") + 'managefiles/manage.php?'; 50 | for (key in managefiles) { 51 | fileurl += (cnt++ ? '&' : '') + encodeURIComponent(key) + "=" + encodeURIComponent(managefiles[key]) + "&"; 52 | } 53 | var onClose = function() { 54 | ed.windowManager.onClose.remove(onClose); 55 | ed.execCommand('mceForceRepaint'); 56 | }; 57 | ed.windowManager.onClose.add(onClose); 58 | var vp = ed.dom.getViewPort(), 59 | width = 900 + parseInt(ed.getLang('advimage.delta_width', 0)), 60 | height = 600 + parseInt(ed.getLang('advimage.delta_height', 0)), 61 | maximizedmode = (width >= vp.w - 2 || height >= vp.h - 2); 62 | if (maximizedmode) { 63 | width = vp.w; 64 | height = vp.h; 65 | } 66 | w = ed.windowManager.open({ 67 | file : fileurl , 68 | width : width, 69 | height : height, 70 | inline : 1 71 | }, { 72 | plugin_url : url // Plugin absolute URL 73 | }); 74 | if (maximizedmode) { 75 | ed.execCommand('mceMaximizeWindow', w); 76 | } 77 | }); 78 | 79 | ed.addCommand('mceManageFilesUsedFiles', function() { 80 | var managefiles = ed.getParam('managefiles', {}), 81 | text = ed.dom.getRoot().innerHTML, 82 | base = ed.getParam('document_base_url') + '/draftfile.php/' + managefiles['usercontext'] + '/user/draft/' + managefiles['itemid'] + '/', 83 | patt = new RegExp(base.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(.+?)[\\?\"']", 'gm'), 84 | arr = [], match, filename; 85 | while ((match = patt.exec(text)) !== null) { 86 | filename = unescape(match[1]); 87 | if (arr.indexOf(filename) === -1) { 88 | arr[arr.length] = filename; 89 | } 90 | } 91 | return arr; 92 | }); 93 | 94 | var managefiles = ed.getParam('managefiles', {}); 95 | // Get draft area id from filepicker options. 96 | if (!managefiles.itemid && M.editor_tinymce.filepicker_options 97 | && M.editor_tinymce.filepicker_options[ed.id] 98 | && M.editor_tinymce.filepicker_options[ed.id].image 99 | && M.editor_tinymce.filepicker_options[ed.id].image.itemid) { 100 | managefiles.itemid = M.editor_tinymce.filepicker_options[ed.id].image.itemid; 101 | ed.settings['managefiles'].itemid = managefiles.itemid; 102 | } 103 | 104 | // Register buttons 105 | if (managefiles.itemid) { 106 | ed.addButton('managefiles', { 107 | title : 'managefiles.desc', 108 | cmd : 'mceManageFiles', 109 | image : url + '/img/managefiles.png' 110 | }); 111 | } 112 | }, 113 | createControl : function(n, cm) { 114 | return null; 115 | }, 116 | 117 | /** 118 | * Returns information about the plugin as a name/value array. 119 | * The current keys are longname, author, authorurl, infourl and version. 120 | * 121 | * @return {Object} Name/value array containing information about the plugin. 122 | */ 123 | getInfo : function() { 124 | return { 125 | longname : 'Moodle Manage embedded files plugin', 126 | author : 'Marina Glancy', 127 | infourl : 'http://moodle.org', 128 | version : "1.0" 129 | }; 130 | } 131 | }); 132 | 133 | // Register plugin. 134 | tinymce.PluginManager.add('managefiles', tinymce.plugins.MoodleManageFiles); 135 | })(); 136 | --------------------------------------------------------------------------------