MDL-38509 Check for writable plugin type location in install from ZIP form
authorDavid Mudrák <david@moodle.com>
Mon, 18 Mar 2013 23:10:49 +0000 (00:10 +0100)
committerDavid Mudrák <david@moodle.com>
Thu, 28 Mar 2013 10:54:05 +0000 (11:54 +0100)
Standard mform validation is implemented as well as progressively
enhanced AJAX version.

admin/tool/installaddon/classes/installer.php
admin/tool/installaddon/classes/installfromzip_form.php
admin/tool/installaddon/lang/en/tool_installaddon.php
admin/tool/installaddon/permcheck.php [new file with mode: 0644]
admin/tool/installaddon/renderer.php
admin/tool/installaddon/yui/permcheck/permcheck.js [new file with mode: 0644]

index eb05255..2833254 100644 (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 ///////////////////////////////////////////////////
 
     /**
index 56ce0af..aef05c7 100644 (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;
     }
 }
index a9b9a78..833a7f1 100644 (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';
diff --git a/admin/tool/installaddon/permcheck.php b/admin/tool/installaddon/permcheck.php
new file mode 100644 (file)
index 0000000..d2125bb
--- /dev/null
@@ -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);
index aedeca4..e670c6d 100644 (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();
diff --git a/admin/tool/installaddon/yui/permcheck/permcheck.js b/admin/tool/installaddon/yui/permcheck/permcheck.js
new file mode 100644 (file)
index 0000000..ad0e4bd
--- /dev/null
@@ -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']
+});