MDL-38509 Check for writable plugin type location in install from ZIP form

Standard mform validation is implemented as well as progressively
enhanced AJAX version.
This commit is contained in:
David Mudrák 2013-03-19 00:10:49 +01:00
parent 2459758b30
commit ddab904ba8
6 changed files with 245 additions and 2 deletions

View file

@ -34,6 +34,9 @@ defined('MOODLE_INTERNAL') || die();
*/
class tool_installaddon_installer {
/** @var tool_installaddon_installfromzip */
protected $installfromzipform = null;
/**
* Factory method returning an instance of this class.
*
@ -80,10 +83,16 @@ class tool_installaddon_installer {
global $CFG;
require_once(dirname(__FILE__).'/installfromzip_form.php');
if (!is_null($this->installfromzipform)) {
return $this->installfromzipform;
}
$action = new moodle_url('/admin/tool/installaddon/index.php');
$customdata = array('installer' => $this);
return new tool_installaddon_installfromzip($action, $customdata);
$this->installfromzipform = new tool_installaddon_installfromzip($action, $customdata);
return $this->installfromzipform;
}
/**
@ -105,6 +114,33 @@ class tool_installaddon_installer {
return $menu;
}
/**
* Is it possible to create a new plugin directory for the given plugin type?
*
* @throws coding_exception for invalid plugin types or non-existing plugin type locations
* @param string $plugintype
* @return boolean
*/
public function is_plugintype_writable($plugintype) {
$plugintypepath = null;
foreach (get_plugin_types() as $type => $fullpath) {
if ($type === $plugintype) {
$plugintypepath = $fullpath;
break;
}
}
if (is_null($plugintypepath)) {
throw new coding_exception('Unknown plugin type!');
}
if (!is_dir($plugintypepath)) {
throw new coding_exception('Plugin type location does not exist!');
}
return is_writable($plugintypepath);
}
//// End of external API ///////////////////////////////////////////////////
/**

View file

@ -44,10 +44,15 @@ class tool_installaddon_installfromzip extends moodleform {
$installer = $this->_customdata['installer'];
$options = $installer->get_plugin_types_menu();
$mform->addElement('select', 'plugintype', get_string('installfromziptype', 'tool_installaddon'), $options);
$mform->addElement('select', 'plugintype', get_string('installfromziptype', 'tool_installaddon'), $options,
array('id' => 'tool_installaddon_installfromzip_plugintype'));
$mform->addHelpButton('plugintype', 'installfromziptype', 'tool_installaddon');
$mform->addRule('plugintype', null, 'required', null, 'client');
$mform->addElement('static', 'permcheck', '',
html_writer::span(get_string('permcheck', 'tool_installaddon'), '',
array('id' => 'tool_installaddon_installfromzip_permcheck')));
$mform->addElement('filepicker', 'zipfile', get_string('installfromzipfile', 'tool_installaddon'),
null, array('accepted_types' => '.zip'));
$mform->addHelpButton('zipfile', 'installfromzipfile', 'tool_installaddon');
@ -69,8 +74,15 @@ class tool_installaddon_installfromzip extends moodleform {
*/
public function validation($data, $files) {
$installer = $this->_customdata['installer'];
$errors = parent::validation($data, $files);
if (!$installer->is_plugintype_writable($data['plugintype'])) {
$paths = get_plugin_types(true);
$path = $paths[$data['plugintype']];
$errors['plugintype'] = get_string('permcheckresultno', 'tool_installaddon', array('path' => $path));
}
return $errors;
}
}

View file

@ -38,4 +38,9 @@ $string['installfromzipfile_help'] = 'The plugin ZIP package must contain just o
$string['installfromzipsubmit'] = 'Install add-on from the ZIP file';
$string['installfromziptype'] = 'Plugin type';
$string['installfromziptype_help'] = 'Choose the correct type of plugin you are about to install. The installation procedure may fail badly when incorrect plugin type is provided.';
$string['permcheck'] = 'Make sure the plugin type root location is writable by the web server process';
$string['permcheckerror'] = 'Error while checking for write permission';
$string['permcheckprogress'] = 'Checking for write permission ...';
$string['permcheckresultno'] = 'Plugin type location <em>{$a->path}</em> not writable';
$string['permcheckresultyes'] = 'Plugin type location <em>{$a->path}</em> is writable';
$string['pluginname'] = 'Add-on installer';

View file

@ -0,0 +1,79 @@
<?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/>.
/**
* Checks the write permission for the given plugin type
*
* @package tool_installaddon
* @subpackage ajax
* @copyright 2013 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('AJAX_SCRIPT', true);
require(dirname(__FILE__) . '/../../../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once(dirname(__FILE__).'/classes/installer.php');
require_login();
if (!has_capability('moodle/site:config', context_system::instance())) {
header('HTTP/1.1 403 Forbidden');
die();
}
if (!empty($CFG->disableonclickaddoninstall)) {
header('HTTP/1.1 403 Forbidden');
die();
}
if (!confirm_sesskey()) {
header('HTTP/1.1 403 Forbidden');
die();
}
$plugintype = optional_param('plugintype', null, PARAM_ALPHANUMEXT);
if (is_null($plugintype)) {
header('HTTP/1.1 400 Bad Request');
die();
}
$plugintypepath = null;
foreach (get_plugin_types() as $type => $fullpath) {
if ($type === $plugintype) {
$plugintypepath = $fullpath;
break;
}
}
if (is_null($plugintypepath)) {
header('HTTP/1.1 400 Bad Request');
die();
}
$installer = tool_installaddon_installer::instance();
$response = array('path' => $plugintypepath);
if ($installer->is_plugintype_writable($plugintype)) {
$response['writable'] = 1;
} else {
$response['writable'] = 0;
}
header('Content-Type: application/json; charset: utf-8');
echo json_encode($response);

View file

@ -58,6 +58,12 @@ class tool_installaddon_renderer extends plugin_renderer_base {
*/
public function index_page() {
$permcheckurl = new moodle_url('/admin/tool/installaddon/permcheck.php');
$this->page->requires->yui_module('moodle-tool_installaddon-permcheck', 'M.tool_installaddon.permcheck.init',
array(array('permcheckurl' => $permcheckurl->out())));
$this->page->requires->strings_for_js(
array('permcheckprogress', 'permcheckresultno', 'permcheckresultyes', 'permcheckerror'), 'tool_installaddon');
$out = $this->output->header();
$out .= $this->index_page_heading();
$out .= $this->index_page_repository();

View file

@ -0,0 +1,105 @@
/**
* Check for write permission for the selected plugin type
*
* @module moodle-tool_installaddon-permcheck
* @author David Mudrak <david@moodle.com>
*/
YUI.add('moodle-tool_installaddon-permcheck', function(Y) {
M.tool_installaddon = M.tool_installaddon || {};
/**
* @class permcheck
* @static
*/
M.tool_installaddon.permcheck = {
/**
* @method init
* @param {Object} config Configuration passed from the PHP
*/
init : function(config) {
this.config = config;
var plugintypesel = Y.one('#tool_installaddon_installfromzip_plugintype');
if (plugintypesel) {
plugintypesel.on('change', this.check_for_permission, this);
}
},
/**
* @method check_for_permission
* @param {Event} e
*/
check_for_permission : function(e) {
var plugintype = e.currentTarget.get('value');
if (plugintype == '') {
return;
}
Y.log('Selected plugin type: ' + plugintype, 'debug', 'moodle-tool_installaddon-permcheck');
Y.io(this.config.permcheckurl, {
'method' : 'GET',
'data' : {
'sesskey' : M.cfg.sesskey,
'plugintype' : plugintype
},
'arguments' : {
'plugintypeselector' : e.currentTarget,
'showresult' : function(msg, status) {
var resultline = Y.one('#tool_installaddon_installfromzip_permcheck');
if (resultline) {
if (status === 'success') {
resultline.setContent('<span class="success"><img src="' + M.util.image_url('i/tick_green_big') + '" /> ' +
msg + '</span>');
} else if (status === 'progress') {
resultline.setContent('<span class="progress"><img src="' + M.cfg.loadingicon + '" /> ' +
msg + '</span>');
} else {
resultline.setContent('<span class="error"><img src="' + M.util.image_url('i/cross_red_big') + '" /> ' +
msg + '</span>');
}
}
}
},
'on' : {
'start' : function(transid, args) {
args.showresult(M.util.get_string('permcheckprogress', 'tool_installaddon'), 'progress');
},
'success': function(transid, outcome, args) {
var response;
try {
response = Y.JSON.parse(outcome.responseText);
if (response.error) {
Y.log(response.error, 'error', 'moodle-tool_installaddon-permcheck');
args.showresult(M.util.get_string('permcheckerror', 'tool_installaddon', response), 'error');
} else if (response.path && response.writable == 1) {
args.showresult(M.util.get_string('permcheckresultyes', 'tool_installaddon', response), 'success');
} else if (response.path && response.writable == 0) {
args.showresult(M.util.get_string('permcheckresultno', 'tool_installaddon', response), 'error');
} else {
Y.log(response, 'debug', 'moodle-tool_installaddon-permcheck');
args.showresult(M.util.get_string('permcheckerror', 'tool_installaddon', response), 'error');
}
} catch (e) {
Y.log(e, 'error', 'moodle-tool_installaddon-permcheck');
args.showresult(M.util.get_string('permcheckerror', 'tool_installaddon'), 'error');
}
},
'failure': function(transid, outcome, args) {
Y.log(outcome.statusText, 'error', 'moodle-tool_installaddon-permcheck');
args.showresult(M.util.get_string('permcheckerror', 'tool_installaddon'));
}
}
});
},
/**
* @property
* @type {Object}
*/
config : null
};
}, '@VERSION@', {
requires:['node', 'event', 'io-base']
});