diff --git a/admin/tool/installaddon/classes/installer.php b/admin/tool/installaddon/classes/installer.php new file mode 100644 index 00000000000..eb052558d5a --- /dev/null +++ b/admin/tool/installaddon/classes/installer.php @@ -0,0 +1,160 @@ +. + +/** + * Provides tool_installaddon_installer class + * + * @package tool_installaddon + * @subpackage classes + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Implements main plugin features. + * + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_installaddon_installer { + + /** + * Factory method returning an instance of this class. + * + * @return tool_installaddon_installer + */ + public static function instance() { + return new self(); + } + + /** + * Returns URL to the repository that addons can be searched in and installed from + * + * @return moodle_url + */ + public function get_addons_repository_url() { + global $CFG; + + if (!empty($CFG->config_php_settings['alternativeaddonsrepositoryurl'])) { + $url = $CFG->config_php_settings['alternativeaddonsrepositoryurl']; + } else { + $url = 'https://moodle.org/plugins/get.php'; + } + + if (!$this->should_send_site_info()) { + return new moodle_url($url); + } + + // Append the basic information about our site. + $site = array( + 'fullname' => $this->get_site_fullname(), + 'url' => $this->get_site_url(), + 'major_version' => $this->get_site_major_version(), + ); + + $site = $this->encode_site_information($site); + + return new moodle_url($url, array('site' => $site)); + } + + /** + * @return tool_installaddon_installfromzip + */ + public function get_installfromzip_form() { + global $CFG; + require_once(dirname(__FILE__).'/installfromzip_form.php'); + + $action = new moodle_url('/admin/tool/installaddon/index.php'); + $customdata = array('installer' => $this); + + return new tool_installaddon_installfromzip($action, $customdata); + } + + /** + * Returns localised list of available plugin types + * + * @return array (string)plugintype => (string)plugin name + */ + public function get_plugin_types_menu() { + global $CFG; + require_once($CFG->libdir.'/pluginlib.php'); + + $pluginman = plugin_manager::instance(); + + $menu = array('' => get_string('choosedots')); + foreach (array_keys($pluginman->get_plugin_types()) as $plugintype) { + $menu[$plugintype] = $pluginman->plugintype_name($plugintype).' ('.$plugintype.')'; + } + + return $menu; + } + + //// End of external API /////////////////////////////////////////////////// + + /** + * @return string this site full name + */ + protected function get_site_fullname() { + global $SITE; + + return $SITE->fullname; + } + + /** + * @return string this site URL + */ + protected function get_site_url() { + global $CFG; + + return $CFG->wwwroot; + } + + /** + * @return string major version like 2.5, 2.6 etc. + */ + protected function get_site_major_version() { + return moodle_major_version(); + } + + /** + * Encodes the given array in a way that can be safely appended as HTTP GET param + * + * Be ware! The recipient may rely on the exact way how the site information is encoded. + * Do not change anything here unless you know what you are doing and understand all + * consequences! (Don't you love warnings like that, too? :-p) + * + * @param array $info + * @return string + */ + protected function encode_site_information(array $info) { + return base64_encode(json_encode($info)); + } + + /** + * Decide if the encoded site information should be sent to the add-ons repository site + * + * For now, we just return true. In the future, we may want to implement some + * privacy aware logic (based on site/user preferences for example). + * + * @return bool + */ + protected function should_send_site_info() { + return true; + } +} diff --git a/admin/tool/installaddon/classes/installfromzip_form.php b/admin/tool/installaddon/classes/installfromzip_form.php new file mode 100644 index 00000000000..56ce0af0ae1 --- /dev/null +++ b/admin/tool/installaddon/classes/installfromzip_form.php @@ -0,0 +1,76 @@ +. + +/** + * @package tool_installaddon + * @subpackage classes + * @category form + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/formslib.php'); + +/** + * Defines a simple form for uploading the add-on ZIP package + * + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_installaddon_installfromzip extends moodleform { + + /** + * Defines the form elements + */ + public function definition() { + + $mform = $this->_form; + $installer = $this->_customdata['installer']; + + $options = $installer->get_plugin_types_menu(); + $mform->addElement('select', 'plugintype', get_string('installfromziptype', 'tool_installaddon'), $options); + $mform->addHelpButton('plugintype', 'installfromziptype', 'tool_installaddon'); + $mform->addRule('plugintype', null, 'required', null, 'client'); + + $mform->addElement('filepicker', 'zipfile', get_string('installfromzipfile', 'tool_installaddon'), + null, array('accepted_types' => '.zip')); + $mform->addHelpButton('zipfile', 'installfromzipfile', 'tool_installaddon'); + $mform->addRule('zipfile', null, 'required', null, 'client'); + + $mform->addElement('checkbox', 'acknowledgement', get_string('acknowledgement', 'tool_installaddon'), + ' '.get_string('acknowledgementtext', 'tool_installaddon')); + $mform->addRule('acknowledgement', get_string('acknowledgementmust', 'tool_installaddon'), 'required', null, 'client'); + + $this->add_action_buttons(false, get_string('installfromzipsubmit', 'tool_installaddon')); + } + + /** + * Validate the form fields + * + * @param array $data + * @param array $files + * @return array (string)field name => (string)validation error text + */ + public function validation($data, $files) { + + $errors = parent::validation($data, $files); + + return $errors; + } +} diff --git a/admin/tool/installaddon/index.php b/admin/tool/installaddon/index.php new file mode 100644 index 00000000000..062e05cbc69 --- /dev/null +++ b/admin/tool/installaddon/index.php @@ -0,0 +1,50 @@ +. + +/** + * The main screen of the tool. + * + * @package tool_installaddon + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(dirname(__FILE__) . '/../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once(dirname(__FILE__).'/classes/installer.php'); + +admin_externalpage_setup('tool_installaddon_index'); + +if (!empty($CFG->disableonclickaddoninstall)) { + notice(get_string('featuredisabled', 'tool_installaddon')); +} + +$installer = tool_installaddon_installer::instance(); + +$form = $installer->get_installfromzip_form(); + +if ($form->is_cancelled()) { + redirect($PAGE->url); + +} else if ($data = $form->get_data()) { + // todo $installer->process_installfromzip_form($data); +} + +$output = $PAGE->get_renderer('tool_installaddon'); +$output->set_installer_instance($installer); + +echo $output->index_page(); diff --git a/admin/tool/installaddon/lang/en/tool_installaddon.php b/admin/tool/installaddon/lang/en/tool_installaddon.php new file mode 100644 index 00000000000..a9b9a78401b --- /dev/null +++ b/admin/tool/installaddon/lang/en/tool_installaddon.php @@ -0,0 +1,41 @@ +. + +/** + * Strings for the tool_installaddon component. + * + * @package tool_installaddon + * @category string + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$string['acknowledgement'] = 'Acknowledgement'; +$string['acknowledgementmust'] = 'You must acknowledge this'; +$string['acknowledgementtext'] = 'I understand that it is my responsibility to have full backups of this site prior to installing add-ons. I accept and understand that add-ons (especially but not only those originating in unofficial sources) may contain security holes, can make the site unavailable, or cause private data leaks or loss.'; +$string['featuredisabled'] = 'Add-on installer is disabled at this site.'; +$string['installaddons'] = 'Install add-ons'; +$string['installfromrepo'] = 'Install add-ons from Moodle plugins directory'; +$string['installfromrepo_help'] = 'You will be redirected to the Moodle plugins directory to search for and install an add-on. Note that your site fullname, URL and major version will be sent as well, to make the installation process easier for you.'; +$string['installfromzipfile'] = 'ZIP package'; +$string['installfromzipfile_help'] = 'The plugin ZIP package must contain just one directory with the name of the plugin. The ZIP will be extracted into the appropriate location for the given plugin type. Packages downloaded from the Moodle plugins directory have this format.'; +$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['pluginname'] = 'Add-on installer'; diff --git a/admin/tool/installaddon/pix/icon.png b/admin/tool/installaddon/pix/icon.png new file mode 100644 index 00000000000..f2d450b5c37 Binary files /dev/null and b/admin/tool/installaddon/pix/icon.png differ diff --git a/admin/tool/installaddon/pix/icon.svg b/admin/tool/installaddon/pix/icon.svg new file mode 100644 index 00000000000..19dead1036a --- /dev/null +++ b/admin/tool/installaddon/pix/icon.svg @@ -0,0 +1,16 @@ + + + +]> + + + + + diff --git a/admin/tool/installaddon/renderer.php b/admin/tool/installaddon/renderer.php new file mode 100644 index 00000000000..aedeca41aad --- /dev/null +++ b/admin/tool/installaddon/renderer.php @@ -0,0 +1,114 @@ +. + +/** + * Output rendering for the plugin. + * + * @package tool_installaddon + * @category output + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Implements the plugin renderer + * + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_installaddon_renderer extends plugin_renderer_base { + + /** @var tool_installaddon_installer */ + protected $installer = null; + + /** + * Sets the tool_installaddon_installer instance being used. + * + * @throws coding_exception if the installer has been already set + * @param tool_installaddon_installer $installer + */ + public function set_installer_instance(tool_installaddon_installer $installer) { + if (is_null($this->installer)) { + $this->installer = $installer; + } else { + throw new coding_exception('Attempting to reset the installer instance.'); + } + } + + /** + * Defines the index page layout + * + * @return string + */ + public function index_page() { + + $out = $this->output->header(); + $out .= $this->index_page_heading(); + $out .= $this->index_page_repository(); + $out .= $this->index_page_upload(); + $out .= $this->output->footer(); + + return $out; + } + + /** + * Renders the index page heading + * + * @return string + */ + protected function index_page_heading() { + return $this->output->heading(get_string('pluginname', 'tool_installaddon')); + } + + /** + * Renders the widget for browsing the add-on repository + * + * @return string + */ + protected function index_page_repository() { + + $url = $this->installer->get_addons_repository_url(); + + $out = $this->box( + $this->output->single_button($url, get_string('installfromrepo', 'tool_installaddon'), 'get'). + $this->output->help_icon('installfromrepo', 'tool_installaddon'), + 'generalbox', 'installfromrepobox' + ); + + return $out; + } + + /** + * Renders the widget for uploading the add-on ZIP package + * + * @return string + */ + protected function index_page_upload() { + + $form = $this->installer->get_installfromzip_form(); + + ob_start(); + $form->display(); + $out = ob_get_clean(); + + $out = $this->box($out, 'generalbox', 'installfromzipbox'); + + return $out; + } +} diff --git a/admin/tool/installaddon/settings.php b/admin/tool/installaddon/settings.php new file mode 100644 index 00000000000..326f0309a5a --- /dev/null +++ b/admin/tool/installaddon/settings.php @@ -0,0 +1,34 @@ +. + +/** + * Puts the plugin actions into the admin settings tree. + * + * @package tool_installaddon + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +if ($hassiteconfig and empty($CFG->disableonclickaddoninstall)) { + + $ADMIN->add('modules', new admin_externalpage('tool_installaddon_index', + get_string('installaddons', 'tool_installaddon'), + "$CFG->wwwroot/$CFG->admin/tool/installaddon/")); + +} diff --git a/admin/tool/installaddon/styles.css b/admin/tool/installaddon/styles.css new file mode 100644 index 00000000000..3de50f35259 --- /dev/null +++ b/admin/tool/installaddon/styles.css @@ -0,0 +1,13 @@ +#page-admin-tool-installaddon-index #installfromrepobox { + text-align: center; + padding-top: 2em; + padding-bottom: 2em; +} + +#page-admin-tool-installaddon-index #installfromrepobox .singlebutton { + display: inline-block; +} + +#page-admin-tool-installaddon-index #installfromrepobox .singlebutton input[type=submit] { + padding: 1em; +} diff --git a/admin/tool/installaddon/tests/installer_test.php b/admin/tool/installaddon/tests/installer_test.php new file mode 100644 index 00000000000..a5028ddb800 --- /dev/null +++ b/admin/tool/installaddon/tests/installer_test.php @@ -0,0 +1,80 @@ +. + +/** + * Provides the unit tests class and some helper classes + * + * @package tool_installaddon + * @category test + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/installaddon/classes/installer.php'); + + +/** + * Unit tests for the {@link tool_installaddon_installer} class + * + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_installaddon_installer_test extends advanced_testcase { + + public function test_get_addons_repository_url() { + $installer = new testable_tool_installaddon_installer(); + $url = $installer->get_addons_repository_url(); + $query = parse_url($url, PHP_URL_QUERY); + $this->assertEquals(1, preg_match('~^site=(.+)$~', $query, $matches)); + $site = rawurldecode($matches[1]); + $site = json_decode(base64_decode($site), true); + $this->assertEquals('array', gettype($site)); + $this->assertEquals(3, count($site)); + $this->assertSame($installer->get_site_fullname(), $site['fullname']); + $this->assertSame($installer->get_site_url(), $site['url']); + $this->assertSame($installer->get_site_major_version(), $site['major_version']); + } +} + + +/** + * Testable subclass of the tested class + * + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class testable_tool_installaddon_installer extends tool_installaddon_installer { + + public function get_site_fullname() { + return '

Nasty site

'; + } + + public function get_site_url() { + return 'file:///etc/passwd'; + } + + public function get_site_major_version() { + return "2.5'; DROP TABLE mdl_user; --"; + } + + protected function should_send_site_info() { + return true; + } +} diff --git a/admin/tool/installaddon/version.php b/admin/tool/installaddon/version.php new file mode 100644 index 00000000000..403deefc6f8 --- /dev/null +++ b/admin/tool/installaddon/version.php @@ -0,0 +1,29 @@ +. + +/** + * @package tool_installaddon + * @copyright 2013 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->component = 'tool_installaddon'; +$plugin->version = 2013031400; +$plugin->requires = 2013031400; +$plugin->maturity = MATURITY_ALPHA;