diff --git a/admin/settings/license.php b/admin/settings/license.php new file mode 100644 index 00000000000..bd9ad8fd09a --- /dev/null +++ b/admin/settings/license.php @@ -0,0 +1,45 @@ +. + +/** + * This file defines the settings pages for licenses. + * + * @package core + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/licenselib.php'); + +if ($hassiteconfig) { + + $temp = new admin_settingpage('licensesettings', new lang_string('licensesettings', 'admin')); + + $licenses = license_manager::get_active_licenses_as_array(); + + $temp->add(new admin_setting_configselect('sitedefaultlicense', + new lang_string('configsitedefaultlicense', 'admin'), + new lang_string('configsitedefaultlicensehelp', 'admin'), + 'unknown', + $licenses)); + $temp->add(new admin_setting_configcheckbox('rememberuserlicensepref', + new lang_string('rememberuserlicensepref', 'admin'), + new lang_string('rememberuserlicensepref_help', 'admin'), + 1)); + $ADMIN->add('license', $temp); +} diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php index 278d4f35eb0..4eb6ec4a7a2 100644 --- a/admin/settings/plugins.php +++ b/admin/settings/plugins.php @@ -182,20 +182,6 @@ if ($hassiteconfig) { $plugin->load_settings($ADMIN, 'mlbackendsettings', $hassiteconfig); } -/// License types - $ADMIN->add('modules', new admin_category('licensesettings', new lang_string('licenses'))); - $temp = new admin_settingpage('managelicenses', new lang_string('managelicenses', 'admin')); - - require_once($CFG->libdir . '/licenselib.php'); - $licenses = array(); - $array = explode(',', $CFG->licenses); - foreach ($array as $value) { - $licenses[$value] = new lang_string($value, 'license'); - } - $temp->add(new admin_setting_configselect('sitedefaultlicense', new lang_string('configsitedefaultlicense','admin'), new lang_string('configsitedefaultlicensehelp','admin'), 'allrightsreserved', $licenses)); - $temp->add(new admin_setting_managelicenses()); - $ADMIN->add('licensesettings', $temp); - /// Filter plugins $ADMIN->add('modules', new admin_category('filtersettings', new lang_string('managefilters'))); diff --git a/admin/settings/top.php b/admin/settings/top.php index 8c922d1196c..87178430ea1 100644 --- a/admin/settings/top.php +++ b/admin/settings/top.php @@ -32,6 +32,7 @@ $ADMIN->add('root', new admin_category('analytics', new lang_string('analytics', $ADMIN->add('root', new admin_category('competencies', new lang_string('competencies', 'core_competency'))); $ADMIN->add('root', new admin_category('badges', new lang_string('badges'), empty($CFG->enablebadges))); $ADMIN->add('root', new admin_category('h5p', new lang_string('h5p', 'core_h5p'))); +$ADMIN->add('root', new admin_category('license', new lang_string('license'))); $ADMIN->add('root', new admin_category('location', new lang_string('location','admin'))); $ADMIN->add('root', new admin_category('language', new lang_string('language'))); $ADMIN->add('root', new admin_category('messaging', new lang_string('messagingcategory', 'admin'))); diff --git a/admin/tool/licensemanager/amd/build/delete_license.min.js b/admin/tool/licensemanager/amd/build/delete_license.min.js new file mode 100644 index 00000000000..17dd2af256b --- /dev/null +++ b/admin/tool/licensemanager/amd/build/delete_license.min.js @@ -0,0 +1,2 @@ +define ("tool_licensemanager/delete_license",["jquery","core/modal_factory","core/modal_events","core/url","core/str"],function(a,b,c,d,e){var f=a(".delete-license");b.create({type:b.types.SAVE_CANCEL,title:e.get_string("deletelicense","tool_licensemanager"),body:e.get_string("deletelicenseconfirmmessage","tool_licensemanager"),preShowCallback:function preShowCallback(b,c){b=a(b);var e={action:"delete",license:b.data("license")};c.deleteURL=d.relativeUrl("/admin/tool/licensemanager/index.php",e,!0)},large:!0},f).done(function(a){a.getRoot().on(c.save,function(b){b.preventDefault();window.location.href=a.deleteURL})})}); +//# sourceMappingURL=delete_license.min.js.map diff --git a/admin/tool/licensemanager/amd/build/delete_license.min.js.map b/admin/tool/licensemanager/amd/build/delete_license.min.js.map new file mode 100644 index 00000000000..caee9ae810d --- /dev/null +++ b/admin/tool/licensemanager/amd/build/delete_license.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/delete_license.js"],"names":["define","$","ModalFactory","ModalEvents","Url","String","trigger","create","type","types","SAVE_CANCEL","title","get_string","body","preShowCallback","triggerElement","modal","params","data","deleteURL","relativeUrl","large","done","getRoot","on","save","e","preventDefault","window","location","href"],"mappings":"AAwBAA,OAAM,sCAAC,CAAC,QAAD,CAAW,oBAAX,CAAiC,mBAAjC,CAAsD,UAAtD,CAAkE,UAAlE,CAAD,CACF,SAASC,CAAT,CAAYC,CAAZ,CAA0BC,CAA1B,CAAuCC,CAAvC,CAA4CC,CAA5C,CAAoD,CAEhD,GAAIC,CAAAA,CAAO,CAAGL,CAAC,CAAC,iBAAD,CAAf,CACAC,CAAY,CAACK,MAAb,CAAoB,CAChBC,IAAI,CAAEN,CAAY,CAACO,KAAb,CAAmBC,WADT,CAEhBC,KAAK,CAAEN,CAAM,CAACO,UAAP,CAAkB,eAAlB,CAAmC,qBAAnC,CAFS,CAGhBC,IAAI,CAAER,CAAM,CAACO,UAAP,CAAkB,6BAAlB,CAAiD,qBAAjD,CAHU,CAIhBE,eAAe,CAAE,yBAASC,CAAT,CAAyBC,CAAzB,CAAgC,CAC7CD,CAAc,CAAGd,CAAC,CAACc,CAAD,CAAlB,CACA,GAAIE,CAAAA,CAAM,CAAG,CACT,OAAU,QADD,CAET,QAAWF,CAAc,CAACG,IAAf,CAAoB,SAApB,CAFF,CAAb,CAIAF,CAAK,CAACG,SAAN,CAAkBf,CAAG,CAACgB,WAAJ,CAAgB,sCAAhB,CAAwDH,CAAxD,IACrB,CAXe,CAYhBI,KAAK,GAZW,CAApB,CAaGf,CAbH,EAcKgB,IAdL,CAcU,SAASN,CAAT,CAAgB,CAClBA,CAAK,CAACO,OAAN,GAAgBC,EAAhB,CAAmBrB,CAAW,CAACsB,IAA/B,CAAqC,SAASC,CAAT,CAAY,CAE7CA,CAAC,CAACC,cAAF,GAEAC,MAAM,CAACC,QAAP,CAAgBC,IAAhB,CAAuBd,CAAK,CAACG,SAChC,CALD,CAMH,CArBL,CAsBH,CA1BC,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Modal for confirming deletion of a custom license.\n *\n * @module tool_licensemanager/delete_license\n * @class delete_license\n * @package tool_licensemanager\n * @copyright 2019 Tom Dickman \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/modal_factory', 'core/modal_events', 'core/url', 'core/str'],\n function($, ModalFactory, ModalEvents, Url, String) {\n\n var trigger = $('.delete-license');\n ModalFactory.create({\n type: ModalFactory.types.SAVE_CANCEL,\n title: String.get_string('deletelicense', 'tool_licensemanager'),\n body: String.get_string('deletelicenseconfirmmessage', 'tool_licensemanager'),\n preShowCallback: function(triggerElement, modal) {\n triggerElement = $(triggerElement);\n let params = {\n 'action': 'delete',\n 'license': triggerElement.data('license')\n };\n modal.deleteURL = Url.relativeUrl('/admin/tool/licensemanager/index.php', params, true);\n },\n large: true,\n }, trigger)\n .done(function(modal) {\n modal.getRoot().on(ModalEvents.save, function(e) {\n // Stop the default save button behaviour which is to close the modal.\n e.preventDefault();\n // Redirect to delete url.\n window.location.href = modal.deleteURL;\n });\n });\n });\n"],"file":"delete_license.min.js"} \ No newline at end of file diff --git a/admin/tool/licensemanager/amd/src/delete_license.js b/admin/tool/licensemanager/amd/src/delete_license.js new file mode 100644 index 00000000000..6abdcec052e --- /dev/null +++ b/admin/tool/licensemanager/amd/src/delete_license.js @@ -0,0 +1,51 @@ +// 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 . + +/** + * Modal for confirming deletion of a custom license. + * + * @module tool_licensemanager/delete_license + * @class delete_license + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', 'core/modal_factory', 'core/modal_events', 'core/url', 'core/str'], + function($, ModalFactory, ModalEvents, Url, String) { + + var trigger = $('.delete-license'); + ModalFactory.create({ + type: ModalFactory.types.SAVE_CANCEL, + title: String.get_string('deletelicense', 'tool_licensemanager'), + body: String.get_string('deletelicenseconfirmmessage', 'tool_licensemanager'), + preShowCallback: function(triggerElement, modal) { + triggerElement = $(triggerElement); + let params = { + 'action': 'delete', + 'license': triggerElement.data('license') + }; + modal.deleteURL = Url.relativeUrl('/admin/tool/licensemanager/index.php', params, true); + }, + large: true, + }, trigger) + .done(function(modal) { + modal.getRoot().on(ModalEvents.save, function(e) { + // Stop the default save button behaviour which is to close the modal. + e.preventDefault(); + // Redirect to delete url. + window.location.href = modal.deleteURL; + }); + }); + }); diff --git a/admin/tool/licensemanager/classes/form/edit_license.php b/admin/tool/licensemanager/classes/form/edit_license.php new file mode 100644 index 00000000000..624ce80fa01 --- /dev/null +++ b/admin/tool/licensemanager/classes/form/edit_license.php @@ -0,0 +1,124 @@ +. + +/** + * Form for creating/updating a custom license. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_licensemanager\form; + +use moodleform; +use tool_licensemanager\helper; +use tool_licensemanager\manager; + +defined('MOODLE_INTERNAL') || die('Direct access to this script is forbidden.'); + +global $CFG; +require_once($CFG->libdir . '/formslib.php'); + +/** + * Form for creating/updating a custom license. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class edit_license extends moodleform { + + /** + * @var string the action form is taking. + */ + private $action; + + /** + * @var string license shortname if editing or empty string if creating license. + */ + private $licenseshortname; + + /** + * edit_license constructor. + * + * @param string $action the license_manager action to be taken by form. + * @param string $licenseshortname the shortname of the license to edit. + */ + public function __construct(string $action, string $licenseshortname) { + $this->action = $action; + $this->licenseshortname = $licenseshortname; + + if ($action == manager::ACTION_UPDATE && !empty($licenseshortname)) { + parent::__construct(helper::get_update_license_url($licenseshortname)); + } else { + parent::__construct(helper::get_create_license_url()); + } + } + + /** + * Form definition for creation and editing of licenses. + */ + public function definition() { + + $mform = $this->_form; + + $mform->addElement('text', 'shortname', get_string('shortname', 'tool_licensemanager')); + $mform->setType('shortname', PARAM_ALPHANUMEXT); + // Shortname is only editable when user is creating a license. + if ($this->action != manager::ACTION_CREATE) { + $mform->freeze('shortname'); + } else { + $mform->addRule('shortname', get_string('shortnamerequirederror', 'tool_licensemanager'), 'required'); + } + + $mform->addElement('text', 'fullname', get_string('fullname', 'tool_licensemanager')); + $mform->setType('fullname', PARAM_TEXT); + $mform->addRule('fullname', get_string('fullnamerequirederror', 'tool_licensemanager'), 'required'); + + $mform->addElement('text', 'source', get_string('source', 'tool_licensemanager')); + $mform->setType('source', PARAM_URL); + $mform->addHelpButton('source', 'source', 'tool_licensemanager'); + $mform->addRule('source', get_string('sourcerequirederror', 'tool_licensemanager'), 'required'); + + $mform->addElement('date_selector', 'version', get_string('version', 'tool_licensemanager'), get_string('from')); + $mform->addHelpButton('version', 'version', 'tool_licensemanager'); + + $this->add_action_buttons(); + } + + /** + * Validate form data and return errors (if any). + * + * @param array $data array of ("fieldname"=>value) of submitted data + * @param array $files array of uploaded files "element_name"=>tmp_file_path + * @return array of "element_name"=>"error_description" if there are errors, + * or an empty array if everything is OK (true allowed for backwards compatibility too). + */ + public function validation($data, $files) { + $errors = parent::validation($data, $files); + + if (array_key_exists('source', $data) && !filter_var($data['source'], FILTER_VALIDATE_URL)) { + $errors['source'] = get_string('invalidurl', 'tool_licensemanager'); + } + + if (array_key_exists('version', $data) && $data['version'] > time()) { + $errors['version'] = get_string('versioncannotbefuture', 'tool_licensemanager'); + } + + return $errors; + } +} diff --git a/admin/tool/licensemanager/classes/helper.php b/admin/tool/licensemanager/classes/helper.php new file mode 100644 index 00000000000..b71a9aac185 --- /dev/null +++ b/admin/tool/licensemanager/classes/helper.php @@ -0,0 +1,154 @@ +. + +/** + * License manager helper class. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_licensemanager; + +use moodle_url; + +defined('MOODLE_INTERNAL') || die(); + +/** + * License manager helper class. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class helper { + + /** + * Moodle relative path to the licenses manager. + */ + const MANAGER_PATH = '/admin/tool/licensemanager/index.php'; + + /** + * Get the URL for viewing the license manager interface. + * + * @return \moodle_url + */ + public static function get_licensemanager_url() : moodle_url { + global $CFG; + + $url = new moodle_url($CFG->wwwroot . self::MANAGER_PATH, + ['sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL for endpoint enabling a license. + * + * @param string $licenseshortname the shortname of license to enable. + * + * @return \moodle_url + */ + public static function get_enable_license_url(string $licenseshortname) : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_ENABLE, 'license' => $licenseshortname, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL for endpoint disabling a license. + * + * @param string $licenseshortname the shortname of license to disable. + * + * @return \moodle_url + */ + public static function get_disable_license_url(string $licenseshortname) : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_DISABLE, 'license' => $licenseshortname, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL endpoint to create a new license. + * + * @return \moodle_url + */ + public static function get_create_license_url() : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_CREATE, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL endpoint to update an existing license. + * + * @param string $licenseshortname the shortname of license to update. + * + * @return \moodle_url + */ + public static function get_update_license_url(string $licenseshortname) : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_UPDATE, 'license' => $licenseshortname, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL endpoint to move a license up order. + * + * @param string $licenseshortname the shortname of license to move up. + * + * @return \moodle_url + */ + public static function get_moveup_license_url(string $licenseshortname) : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_MOVE_UP, 'license' => $licenseshortname, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Get the URL endpoint to move a license down order. + * + * @param string $licenseshortname the shortname of license to move down. + * + * @return \moodle_url + */ + public static function get_movedown_license_url(string $licenseshortname) : moodle_url { + $url = new moodle_url(self::MANAGER_PATH, + ['action' => manager::ACTION_MOVE_DOWN, 'license' => $licenseshortname, 'sesskey' => sesskey()]); + + return $url; + } + + /** + * Convert a license version number string to a UNIX epoch. + * + * @param string $version + * + * @return int $epoch + */ + public static function convert_version_to_epoch(string $version) : int { + $date = substr($version, 0, 8); + $epoch = strtotime($date); + + return $epoch; + } +} diff --git a/admin/tool/licensemanager/classes/manager.php b/admin/tool/licensemanager/classes/manager.php new file mode 100644 index 00000000000..2f3ad2cee20 --- /dev/null +++ b/admin/tool/licensemanager/classes/manager.php @@ -0,0 +1,247 @@ +. + +/** + * License manager. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_licensemanager; + +use tool_licensemanager\form\edit_license; +use license_manager; +use stdClass; + +defined('MOODLE_INTERNAL') || die(); + +/** + * License manager, main controller for tool_licensemanager. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manager { + + /** + * Action for creating a new custom license. + */ + const ACTION_CREATE = 'create'; + + /** + * Action for updating a custom license's details. + */ + const ACTION_UPDATE = 'update'; + + /** + * Action for deleting a custom license. + */ + const ACTION_DELETE = 'delete'; + + /** + * Action for disabling a custom license. + */ + const ACTION_DISABLE = 'disable'; + + /** + * Action for enabling a custom license. + */ + const ACTION_ENABLE = 'enable'; + + /** + * Action for displaying the license list view. + */ + const ACTION_VIEW_LICENSE_MANAGER = 'viewlicensemanager'; + + /** + * Action for moving a license up order. + */ + const ACTION_MOVE_UP = 'moveup'; + + /** + * Action for moving a license down order. + */ + const ACTION_MOVE_DOWN = 'movedown'; + + /** + * Entry point for internal license manager. + * + * @param string $action the api action to carry out. + * @param string|object $license the license object or shortname of license to carry action out on. + */ + public function execute(string $action, $license) : void { + + admin_externalpage_setup('licensemanager'); + + // Convert license to a string if it's a full license object. + if (is_object($license)) { + $license = $license->shortname; + } + + $viewmanager = true; + + switch ($action) { + case self::ACTION_DISABLE: + license_manager::disable($license); + break; + + case self::ACTION_ENABLE: + license_manager::enable($license); + break; + + case self::ACTION_DELETE: + license_manager::delete($license); + break; + + case self::ACTION_CREATE: + case self::ACTION_UPDATE: + $viewmanager = $this->edit($action, $license); + break; + + case self::ACTION_MOVE_UP: + case self::ACTION_MOVE_DOWN: + $this->change_license_order($action, $license); + break; + + case self::ACTION_VIEW_LICENSE_MANAGER: + default: + break; + } + if ($viewmanager) { + $this->view_license_manager(); + } + } + + /** + * Edit an existing license or create a new license. + * + * @param string $action the form action to carry out. + * @param string $licenseshortname the shortname of the license to edit. + * + * @return bool true if license editing complete, false otherwise. + */ + private function edit(string $action, string $licenseshortname) : bool { + + if ($action != self::ACTION_CREATE && $action != self::ACTION_UPDATE) { + throw new \coding_exception('license edit actions are limited to create and update'); + } + + $form = new form\edit_license($action, $licenseshortname); + + if ($form->is_cancelled()) { + return true; + } else if ($data = $form->get_data()) { + + $license = new stdClass(); + if ($action == self::ACTION_CREATE) { + // Check that license shortname isn't already in use. + if (!empty(license_manager::get_license_by_shortname($data->shortname))) { + print_error('duplicatelicenseshortname', 'tool_licensemanager', + helper::get_licensemanager_url(), + $data->shortname); + } + $license->shortname = $data->shortname; + } else { + if (empty(license_manager::get_license_by_shortname($licenseshortname))) { + print_error('licensenotfoundshortname', 'license', + helper::get_licensemanager_url(), + $licenseshortname); + } + $license->shortname = $licenseshortname; + } + $license->fullname = $data->fullname; + $license->source = $data->source; + // Legacy date format maintained to prevent breaking on upgrade. + $license->version = date('Ymd', $data->version) . '00'; + + license_manager::save($license); + + return true; + } else { + $this->view_license_editor($action, $licenseshortname, $form); + + return false; + } + } + + /** + * Change license order by moving up or down license order. + * + * @param string $direction which direction to move, up or down. + * @param string $licenseshortname the shortname of the license to move up or down order. + */ + private function change_license_order(string $direction, string $licenseshortname) : void { + + if (!empty($licenseshortname)) { + if ($direction == self::ACTION_MOVE_UP) { + license_manager::change_license_sortorder(license_manager::LICENSE_MOVE_UP, $licenseshortname); + } else if ($direction == self::ACTION_MOVE_DOWN) { + license_manager::change_license_sortorder(license_manager::LICENSE_MOVE_DOWN, $licenseshortname); + } + } + } + + /** + * View the license editor to create or edit a license. + * + * @param string $action + * @param string $licenseshortname the shortname of the license to create/edit. + * @param \tool_licensemanager\form\edit_license $form the form for submitting edit data. + */ + private function view_license_editor(string $action, string $licenseshortname, edit_license $form) : void { + global $PAGE; + + $renderer = $PAGE->get_renderer('tool_licensemanager'); + + if ($action == self::ACTION_UPDATE && $license = license_manager::get_license_by_shortname($licenseshortname)) { + $return = $renderer->render_edit_licence_headers($licenseshortname); + + $form->set_data(['shortname' => $license->shortname]); + $form->set_data(['fullname' => $license->fullname]); + $form->set_data(['source' => $license->source]); + $form->set_data(['version' => helper::convert_version_to_epoch($license->version)]); + + } else { + $return = $renderer->render_create_licence_headers(); + } + $return .= $form->render(); + $return .= $renderer->footer(); + + echo $return; + } + + /** + * View the license manager. + */ + private function view_license_manager() : void { + global $PAGE; + + $PAGE->requires->js_call_amd('tool_licensemanager/delete_license'); + + $renderer = $PAGE->get_renderer('tool_licensemanager'); + $html = $renderer->header(); + $html .= $renderer->heading(get_string('licensemanager', 'tool_licensemanager')); + + $table = new \tool_licensemanager\output\table(); + $html .= $renderer->render($table); + $html .= $renderer->footer(); + + echo $html; + } +} diff --git a/admin/tool/licensemanager/classes/output/renderer.php b/admin/tool/licensemanager/classes/output/renderer.php new file mode 100644 index 00000000000..19ac8d419a4 --- /dev/null +++ b/admin/tool/licensemanager/classes/output/renderer.php @@ -0,0 +1,96 @@ +. + +/** + * Renderer for 'tool_licensemanager' component. + * + * @package tool_licensemanager + * @copyright Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_licensemanager\output; + +defined('MOODLE_INTERNAL') || die(); + +use license_manager; +use plugin_renderer_base; +use tool_licensemanager\helper; + +/** + * Renderer class for 'tool_licensemanager' component. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class renderer extends plugin_renderer_base { + + /** + * Render the headers for create license form. + * + * @return string html fragment for display. + */ + public function render_create_licence_headers() : string { + + $this->page->navbar->add(get_string('createlicense', 'tool_licensemanager'), + helper::get_create_license_url()); + + $return = $this->header(); + $return .= $this->heading(get_string('createlicense', 'tool_licensemanager')); + + return $return; + } + + /** + * Render the headers for edit license form. + * + * @param string $licenseshortname the shortname of license to edit. + * + * @return string html fragment for display. + */ + public function render_edit_licence_headers(string $licenseshortname) : string { + + $this->page->navbar->add(get_string('editlicense', 'tool_licensemanager'), + helper::get_update_license_url($licenseshortname)); + + $return = $this->header(); + $return .= $this->heading(get_string('editlicense', 'tool_licensemanager')); + + return $return; + } + + /** + * Render the license manager table. + * + * @param \renderable $table the renderable. + * + * @return string HTML. + */ + public function render_table(\renderable $table) { + $licenses = license_manager::get_licenses(); + + // Add the create license button. + $html = $table->create_license_link(); + + // Add the table containing licenses for management. + $html .= $this->box_start('generalbox editorsui'); + $html .= $table->create_license_manager_table($licenses, $this); + $html .= $this->box_end(); + + return $html; + } +} diff --git a/admin/tool/licensemanager/classes/output/table.php b/admin/tool/licensemanager/classes/output/table.php new file mode 100644 index 00000000000..17144fcc691 --- /dev/null +++ b/admin/tool/licensemanager/classes/output/table.php @@ -0,0 +1,185 @@ +. + +/** + * Renderable for display of license manager table. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_licensemanager\output; + +use html_table; +use html_table_cell; +use html_table_row; +use html_writer; +use license_manager; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Renderable for display of license manager table. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class table implements \renderable { + + /** + * 'Create License' link. + * + * @return string HTML string. + */ + public function create_license_link() { + $link = html_writer::link(\tool_licensemanager\helper::get_create_license_url(), + get_string('createlicensebuttontext', 'tool_licensemanager'), + ['class' => 'btn btn-secondary mb-3']); + + return $link; + } + + /** + * Create the HTML table for license management. + * + * @param array $licenses + * @param \renderer_base $output + * + * @return string HTML for license manager table. + */ + public function create_license_manager_table(array $licenses, \renderer_base $output) { + $table = new html_table(); + $table->head = [ + get_string('enable'), + get_string('license', 'tool_licensemanager'), + get_string('version'), + get_string('order'), + get_string('edit'), + get_string('delete'), + ]; + $table->colclasses = [ + 'text-center', + 'text-left', + 'text-left', + 'text-center', + 'text-center', + 'text-center', + ]; + $table->id = 'manage-licenses'; + $table->attributes['class'] = 'admintable generaltable'; + $table->data = []; + + $rownumber = 0; + $rowcount = count($licenses); + + foreach ($licenses as $key => $value) { + $canmoveup = $rownumber > 0; + $canmovedown = $rownumber < $rowcount - 1; + $table->data[] = $this->get_license_table_row_data($value, $canmoveup, $canmovedown, $output); + $rownumber++; + } + + $html = html_writer::table($table); + + return $html; + } + + /** + * Get table row data for a license. + * + * @param object $license the license to populate row data for. + * @param bool $canmoveup can this row move up. + * @param bool $canmovedown can this row move down. + * @param \renderer_base $output the renderer + * + * @return \html_table_row of columns values for row. + */ + protected function get_license_table_row_data($license, bool $canmoveup, bool $canmovedown, \renderer_base $output) { + global $CFG; + + $summary = $license->fullname . ' ('. $license->shortname . ')'; + if (!empty($license->source)) { + $summary .= html_writer::empty_tag('br'); + $summary .= html_writer::link($license->source, $license->source, ['target' => '_blank']); + } + $summarycell = new html_table_cell($summary); + $summarycell->attributes['class'] = 'license-summary'; + $versioncell = new html_table_cell($license->version); + $versioncell->attributes['class'] = 'license-version'; + + $deletelicense = ''; + if ($license->shortname == $CFG->sitedefaultlicense) { + $hideshow = $output->pix_icon('t/locked', get_string('sitedefaultlicenselock', 'tool_licensemanager')); + } else { + if ($license->enabled == license_manager::LICENSE_ENABLED) { + $hideshow = html_writer::link(\tool_licensemanager\helper::get_disable_license_url($license->shortname), + $output->pix_icon('t/hide', get_string('disablelicensename', 'tool_licensemanager', $license->fullname))); + } else { + $hideshow = html_writer::link(\tool_licensemanager\helper::get_enable_license_url($license->shortname), + $output->pix_icon('t/show', get_string('enablelicensename', 'tool_licensemanager', $license->fullname))); + } + + if ($license->custom == license_manager::CUSTOM_LICENSE) { + // Link url is added by the JS `delete_license` modal used for confirmation of deletion, to avoid + // link being usable before JavaScript loads on page. + $deletelicense = html_writer::link('#', $output->pix_icon('i/trash', + get_string('deletelicensename', 'tool_licensemanager', $license->fullname)), + ['class' => 'delete-license', 'data-license' => $license->shortname]); + } + } + $hideshowcell = new html_table_cell($hideshow); + $hideshowcell->attributes['class'] = 'license-status'; + + if ($license->custom == license_manager::CUSTOM_LICENSE) { + $editlicense = html_writer::link(\tool_licensemanager\helper::get_update_license_url($license->shortname), + $output->pix_icon('t/editinline', get_string('editlicensename', 'tool_licensemanager', $license->fullname)), + ['class' => 'edit-license']); + } else { + $editlicense = ''; + } + $editlicensecell = new html_table_cell($editlicense); + $editlicensecell->attributes['class'] = 'edit-license'; + + $spacer = $output->pix_icon('spacer', '', 'moodle', ['class' => 'iconsmall']); + $updown = ''; + if ($canmoveup) { + $updown .= html_writer::link(\tool_licensemanager\helper::get_moveup_license_url($license->shortname), + $output->pix_icon('t/up', get_string('movelicenseupname', 'tool_licensemanager', $license->fullname), + 'moodle', ['class' => 'iconsmall']), + ['class' => 'move-up']) . ''; + } else { + $updown .= $spacer; + } + + if ($canmovedown) { + $updown .= ' '.html_writer::link(\tool_licensemanager\helper::get_movedown_license_url($license->shortname), + $output->pix_icon('t/down', get_string('movelicensedownname', 'tool_licensemanager', $license->fullname), + 'moodle', ['class' => 'iconsmall']), + ['class' => 'move-down']); + } else { + $updown .= $spacer; + } + $updowncell = new html_table_cell($updown); + $updowncell->attributes['class'] = 'license-order'; + + $row = new html_table_row([$hideshowcell, $summarycell, $versioncell, $updowncell, $editlicensecell, $deletelicense]); + $row->attributes['data-license'] = $license->shortname; + $row->attributes['class'] = strtolower(get_string('license', 'tool_licensemanager')); + + return $row; + } +} diff --git a/admin/tool/licensemanager/classes/privacy/provider.php b/admin/tool/licensemanager/classes/privacy/provider.php new file mode 100644 index 00000000000..b4eb6115dac --- /dev/null +++ b/admin/tool/licensemanager/classes/privacy/provider.php @@ -0,0 +1,47 @@ +. + +/** + * Privacy Subsystem implementation for tool_licensemanager implementing null_provider. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_licensemanager\privacy; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Privacy Subsystem implementation for tool_licensemanager implementing null_provider. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class provider implements \core_privacy\local\metadata\null_provider { + + /** + * Get the language string identifier with the component's language + * file to explain why this plugin stores no data. + * + * @return string + */ + public static function get_reason() : string { + return 'privacy:metadata'; + } +} diff --git a/admin/licenses.php b/admin/tool/licensemanager/index.php similarity index 55% rename from admin/licenses.php rename to admin/tool/licensemanager/index.php index 820e7758a69..123dd9db6e0 100644 --- a/admin/licenses.php +++ b/admin/tool/licensemanager/index.php @@ -1,5 +1,4 @@ . /** - * Allows admin to configure licenses. + * License manager page. + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -require_once('../config.php'); -require_once($CFG->libdir.'/adminlib.php'); -require_once($CFG->libdir.'/licenselib.php'); +require_once('../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); +require_once($CFG->libdir . '/licenselib.php'); require_admin(); -$returnurl = "$CFG->wwwroot/$CFG->admin/settings.php?section=managelicenses"; +$returnurl = \tool_licensemanager\helper::get_licensemanager_url(); $action = optional_param('action', '', PARAM_ALPHANUMEXT); $license = optional_param('license', '', PARAM_SAFEDIR); -//////////////////////////////////////////////////////////////////////////////// -// process actions - if (!confirm_sesskey()) { redirect($returnurl); } -$return = true; -switch ($action) { - case 'disable': - license_manager::disable($license); - break; +// Route via the manager. +$licensemanager = new \tool_licensemanager\manager(); +$PAGE->set_context(context_system::instance()); +$PAGE->set_url(\tool_licensemanager\helper::get_licensemanager_url()); +$PAGE->set_title(get_string('licensemanager', 'tool_licensemanager')); - case 'enable': - license_manager::enable($license); - break; - - default: - break; -} - -if ($return) { - redirect ($returnurl); -} +$licensemanager->execute($action, $license); diff --git a/admin/tool/licensemanager/lang/en/tool_licensemanager.php b/admin/tool/licensemanager/lang/en/tool_licensemanager.php new file mode 100644 index 00000000000..97e372a21b8 --- /dev/null +++ b/admin/tool/licensemanager/lang/en/tool_licensemanager.php @@ -0,0 +1,52 @@ +. + +/** + * Strings for component 'tool_licensemanager', language 'en' + * + * @package tool_licensemanager + * @copyright 2019 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +$string['pluginname'] = 'License manager'; +$string['createlicense'] = 'Create custom licence'; +$string['createlicensebuttontext'] = 'Create licence'; +$string['deletelicense'] = 'Delete licence'; +$string['deletelicenseconfirmmessage'] = 'Are you sure you want to delete this licence?'; +$string['deletelicensename'] = 'Delete license \'{$a}\''; +$string['disablelicensename'] = 'Disable licence \'{$a}\''; +$string['duplicatelicenseshortname'] = 'Licence shortname must be unique, duplicate value found.'; +$string['editlicense'] = 'Edit licence'; +$string['editlicensename'] = 'Edit licence \'{$a}\''; +$string['enablelicensename'] = 'Enable licence \'{$a}\''; +$string['fullname'] = 'Licence full name'; +$string['fullnamerequirederror'] = 'You must enter a full name for the licence.'; +$string['invalidurl'] = 'Invalid source URL'; +$string['license'] = 'Licence'; +$string['licensemanager'] = 'Licence manager'; +$string['movelicensedownname'] = 'Move \'{$a}\' license down order'; +$string['movelicenseupname'] = 'Move \'{$a}\' license up order'; +$string['privacy:metadata'] = 'The tool_licensemanager plugin stores no personal data.'; +$string['shortname'] = 'Licence short name'; +$string['sitedefaultlicenselock'] = 'This is the site default license. It cannot be disabled.'; +$string['shortnamerequirederror'] = 'You must enter a short name for the licence.'; +$string['source'] = 'Licence source'; +$string['source_help'] = 'The URL (with http:// or https:// prefix) where the licence terms and conditions can be found.'; +$string['sourcerequirederror'] = 'You must enter a valid URL for licence source.'; +$string['version'] = 'Licence version'; +$string['versioncannotbefuture'] = 'Licence version cannot be set to a future date.'; +$string['version_help'] = 'Publication date of the licence version being utilised.'; + diff --git a/admin/tool/licensemanager/settings.php b/admin/tool/licensemanager/settings.php new file mode 100644 index 00000000000..ffb61e71a84 --- /dev/null +++ b/admin/tool/licensemanager/settings.php @@ -0,0 +1,33 @@ +. + +/** + * Settings page. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +if ($hassiteconfig) { + $temp = new admin_externalpage('licensemanager', + get_string('licensemanager', 'tool_licensemanager'), + \tool_licensemanager\helper::get_licensemanager_url()); + + $ADMIN->add('license', $temp); +} diff --git a/admin/tool/licensemanager/tests/behat/delete_license.feature b/admin/tool/licensemanager/tests/behat/delete_license.feature new file mode 100644 index 00000000000..9bab27bf5fd --- /dev/null +++ b/admin/tool/licensemanager/tests/behat/delete_license.feature @@ -0,0 +1,28 @@ +@tool @tool_licensemanager +Feature: Delete custom licenses + In order to manage custom licenses + As an admin + I need to be able to delete custom licenses but not standard Moodle licenses + + @javascript + Scenario: I can delete a custom license + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + And I click on "Create licence" "link" + And I set the following fields to these values: + | shortname | MIT | + | fullname | MIT Licence | + | source | https://opensource.org/licenses/MIT | + | version[day] | 1 | + | version[month] | March | + | version[year] | 2019 | + And I press "Save changes" + And I click on "Delete" "icon" in the "MIT" "table_row" + When I click on "Save changes" "button" in the "Delete licence" "dialogue" + Then I should not see "MIT Licence" in the "manage-licenses" "table" + + Scenario: I cannot delete a standard license + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + Then I should see "Licence not specified" in the "unknown" "table_row" + And I should not see "Delete" in the "unknown" "table_row" diff --git a/admin/tool/licensemanager/tests/behat/edit_license.feature b/admin/tool/licensemanager/tests/behat/edit_license.feature new file mode 100644 index 00000000000..d4d73ff220c --- /dev/null +++ b/admin/tool/licensemanager/tests/behat/edit_license.feature @@ -0,0 +1,79 @@ +@tool @tool_licensemanager +Feature: Custom licences + In order to use custom licences + As an admin + I need to be able to add custom licences + + Scenario: I am able to create custom licences + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + And I click on "Create licence" "link" + And I set the following fields to these values: + | shortname | MIT | + | fullname | MIT Licence | + | source | https://opensource.org/licenses/MIT | + | version[day] | 1 | + | version[month] | January | + | version[year] | 2020 | + When I press "Save changes" + Then I should see "Licence manager" + And I should see "MIT Licence" in the "MIT" "table_row" + And I should see "https://opensource.org/licenses/MIT" in the "MIT" "table_row" + + Scenario: I am only be able to make custom license with a valid url source (including scheme). + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + And I click on "Create licence" "link" + And I set the following fields to these values: + | shortname | MIT | + | fullname | MIT Licence | + | source | opensource.org/licenses/MIT | + | version[day] | 1 | + | version[month] | January | + | version[year] | 2020 | + When I press "Save changes" + Then I should see "Invalid source URL" + And I set the following fields to these values: + | source | mailto:tomdickman@catalyst-au.net | + And I press "Save changes" + And I should see "Invalid source URL" + And I set the following fields to these values: + | source | https://opensource.org/licenses/MIT | + And I press "Save changes" + And I should see "Licence manager" + And I should see "MIT Licence" in the "MIT" "table_row" + And I should see "https://opensource.org/licenses/MIT" in the "MIT" "table_row" + + Scenario: Custom license version format must be YYYYMMDD00 + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + And I click on "Create licence" "link" + And I set the following fields to these values: + | shortname | MIT | + | fullname | MIT Licence | + | source | https://opensource.org/licenses/MIT | + | version[day] | 1 | + | version[month] | March | + | version[year] | 2019 | + When I press "Save changes" + Then I should see "Licence manager" + And I should see "2019030100" in the "MIT" "table_row" + + @javascript + Scenario: Custom license short name should not be editable after first creation + Given I log in as "admin" + And I navigate to "Licence > Licence manager" in site administration + And I click on "Create licence" "link" + And I set the following fields to these values: + | shortname | MIT | + | fullname | MIT Licence | + | source | https://opensource.org/licenses/MIT | + | version[day] | 1 | + | version[month] | March | + | version[year] | 2019 | + And I press "Save changes" + And I should see "Licence manager" + And I should see "MIT Licence" in the "MIT" "table_row" + When I click on "Edit" "icon" in the "MIT" "table_row" + Then I should see "Edit licence" + And the "shortname" "field" should be disabled diff --git a/admin/tool/licensemanager/tests/behat/license_manager.feature b/admin/tool/licensemanager/tests/behat/license_manager.feature new file mode 100644 index 00000000000..2977d12c991 --- /dev/null +++ b/admin/tool/licensemanager/tests/behat/license_manager.feature @@ -0,0 +1,35 @@ +@tool @tool_licensemanager +Feature: License manager + In order to manage licenses + As an admin + I need to be able to view and alter licence preferences in the license manager. + + Scenario: I should be able to see the default Moodle licences. + Given I log in as "admin" + When I navigate to "Licence > Licence manager" in site administration + Then I should see "Licence not specified" in the "unknown" "table_row" + And I should see "All rights reserved" in the "allrightsreserved" "table_row" + And I should see "Public domain" in the "public" "table_row" + And I should see "Creative Commons" in the "cc" "table_row" + And I should see "Creative Commons - NoDerivs" in the "cc-nd" "table_row" + And I should see "Creative Commons - No Commercial NoDerivs" in the "cc-nc-nd" "table_row" + And I should see "Creative Commons - No Commercial" in the "cc-nc" "table_row" + And I should see "Creative Commons - No Commercial ShareAlike" in the "cc-nc-sa" "table_row" + And I should see "Creative Commons - ShareAlike" in the "cc-sa" "table_row" + + Scenario: I should be able to enable and disable licenses + Given I log in as "admin" + And I navigate to "Licence > Licence settings" in site administration + When I set the field "Default site licence" to "Public domain" + And I press "Save changes" + And I navigate to "Licence > Licence manager" in site administration + Then "This is the site default license" "icon" should exist in the "public" "table_row" + And "Enable license" "icon" should not exist in the "public" "table_row" + And "This is the site default license" "icon" should not exist in the "cc" "table_row" + And I navigate to "Licence > Licence settings" in site administration + And I set the field "Default site licence" to "Creative Commons" + And I press "Save changes" + And I navigate to "Licence > Licence manager" in site administration + And "This is the site default license" "icon" should exist in the "cc" "table_row" + And "Enable license" "icon" should not exist in the "cc" "table_row" + And "This is the site default license" "icon" should not exist in the "public" "table_row" diff --git a/admin/tool/licensemanager/tests/helper_test.php b/admin/tool/licensemanager/tests/helper_test.php new file mode 100644 index 00000000000..14c34c6a137 --- /dev/null +++ b/admin/tool/licensemanager/tests/helper_test.php @@ -0,0 +1,44 @@ +. + +/** + * Tests for tool_licensemanager helper class. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Tests for tool_licensemanager helper class. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @group tool_licensemanager + */ +class helper_test extends advanced_testcase { + + public function test_convert_version_to_epoch() { + + $version = '2020010100'; + $expected = strtotime(20200101); + + $this->assertEquals($expected, \tool_licensemanager\helper::convert_version_to_epoch($version)); + } +} diff --git a/admin/tool/licensemanager/tests/manager_test.php b/admin/tool/licensemanager/tests/manager_test.php new file mode 100644 index 00000000000..e86cf68d633 --- /dev/null +++ b/admin/tool/licensemanager/tests/manager_test.php @@ -0,0 +1,199 @@ +. + +/** + * Tests for tool_licensemanager manager class. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once($CFG->libdir . '/formslib.php'); +require_once($CFG->libdir . '/licenselib.php'); + +/** + * Tests for tool_licensemanager manager class. + * + * @package tool_licensemanager + * @copyright 2020 Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @group tool_licensemanager + */ +class manager_test extends advanced_testcase { + + /** + * Test editing a license. + */ + public function test_edit_existing_license() { + $this->resetAfterTest(); + + // Create initial custom license to edit. + $testlicense = new stdClass(); + $testlicense->shortname = 'my-lic'; + $testlicense->fullname = 'My License'; + $testlicense->source = 'https://fakeurl.net'; + $testlicense->version = date('Ymd', time()) . '00'; + $testlicense->custom = license_manager::CUSTOM_LICENSE; + + license_manager::save($testlicense); + license_manager::enable($testlicense->shortname); + + $manager = new \tool_licensemanager\manager(); + + // Attempt to submit form data with altered details. + $formdata = [ + 'shortname' => 'new-value', + 'fullname' => 'New License Name', + 'source' => 'https://updatedfakeurl.net', + 'version' => time() + ]; + + // Attempt to submit form data with an altered shortname. + \tool_licensemanager\form\edit_license::mock_submit($formdata); + + // We're testing a private method, so we need to setup reflector magic. + $method = new ReflectionMethod('\tool_licensemanager\manager', 'edit'); + $method->setAccessible(true); // Allow accessing of private method. + $method->invoke($manager, \tool_licensemanager\manager::ACTION_UPDATE, $testlicense->shortname); + + // Should not create a new license when updating an existing license. + $this->assertEmpty(license_manager::get_license_by_shortname($formdata['shortname'])); + + $actual = license_manager::get_license_by_shortname('my-lic'); + // Should not be able to update the shortname of the license. + $this->assertNotSame($formdata['shortname'], $actual->shortname); + // Should be able to update other details of the license. + $this->assertSame($formdata['fullname'], $actual->fullname); + $this->assertSame($formdata['source'], $actual->source); + $this->assertSame(date('Ymd', $formdata['version']) . '00', $actual->version); + } + + public function test_edit_license_not_exists() { + $manager = new \tool_licensemanager\manager(); + + // We're testing a private method, so we need to setup reflector magic. + $method = new ReflectionMethod('\tool_licensemanager\manager', 'edit'); + $method->setAccessible(true); // Allow accessing of private method. + + // Attempt to update a license that doesn't exist. + $formdata = [ + 'shortname' => 'new-value', + 'fullname' => 'New License Name', + 'source' => 'https://updatedfakeurl.net', + 'version' => time() + ]; + \tool_licensemanager\form\edit_license::mock_submit($formdata); + + // Should not be able to update a license with a shortname that doesn't exist. + $this->expectException('moodle_exception'); + $method->invoke($manager, \tool_licensemanager\manager::ACTION_UPDATE, $formdata['shortname']); + } + + public function test_edit_license_no_shortname() { + $manager = new \tool_licensemanager\manager(); + + // We're testing a private method, so we need to setup reflector magic. + $method = new ReflectionMethod('\tool_licensemanager\manager', 'edit'); + $method->setAccessible(true); // Allow accessing of private method. + + // Attempt to update a license without passing license shortname. + $formdata = [ + 'fullname' => 'New License Name', + 'source' => 'https://updatedfakeurl.net', + 'version' => time() + ]; + \tool_licensemanager\form\edit_license::mock_submit($formdata); + + // Should not be able to update empty license shortname. + $this->expectException('moodle_exception'); + $method->invoke($manager, \tool_licensemanager\manager::ACTION_UPDATE, ''); + } + + /** + * Test creating a new license. + */ + public function test_edit_create_license() { + $this->resetAfterTest(); + + $licensecount = count(license_manager::get_licenses()); + + $manager = new \tool_licensemanager\manager(); + + $formdata = [ + 'shortname' => 'new-value', + 'fullname' => 'My License', + 'source' => 'https://fakeurl.net', + 'version' => time() + ]; + + // Attempt to submit form data for a new license. + \tool_licensemanager\form\edit_license::mock_submit($formdata); + + // We're testing a private method, so we need to setup reflector magic. + $method = new ReflectionMethod('\tool_licensemanager\manager', 'edit'); + $method->setAccessible(true); // Allow accessing of private method. + $method->invoke($manager, \tool_licensemanager\manager::ACTION_CREATE, $formdata['shortname']); + + // Should create a new license in database. + $this->assertCount($licensecount + 1, license_manager::get_licenses()); + $actual = license_manager::get_license_by_shortname($formdata['shortname']); + $this->assertSame($formdata['shortname'], $actual->shortname); + $this->assertSame($formdata['fullname'], $actual->fullname); + $this->assertSame($formdata['source'], $actual->source); + $this->assertSame(date('Ymd', $formdata['version']) . '00', $actual->version); + + // Attempt to submit form data for a duplicate license. + \tool_licensemanager\form\edit_license::mock_submit($formdata); + + // Should not be able to create duplicate licenses. + $this->expectException('moodle_exception'); + $method->invoke($manager, \tool_licensemanager\manager::ACTION_CREATE, $formdata['shortname']); + } + + /** + * Test changing the order of licenses. + */ + public function test_change_license_order() { + $this->resetAfterTest(); + + $licenseorder = array_keys(license_manager::get_licenses()); + $initialposition = array_search('cc-nc', $licenseorder); + + $manager = new tool_licensemanager\manager(); + + // We're testing a private method, so we need to setup reflector magic. + $method = new ReflectionMethod('\tool_licensemanager\manager', 'change_license_order'); + $method->setAccessible(true); // Allow accessing of private method. + $method->invoke($manager, \tool_licensemanager\manager::ACTION_MOVE_UP, 'cc-nc'); + + $licenseorder = array_keys(license_manager::get_licenses()); + $newposition = array_search('cc-nc', $licenseorder); + + $this->assertLessThan($initialposition, $newposition); + + $initialposition = array_search('allrightsreserved', $licenseorder); + $method->invoke($manager, \tool_licensemanager\manager::ACTION_MOVE_DOWN, 'allrightsreserved'); + $licenseorder = array_keys(license_manager::get_licenses()); + $newposition = array_search('cc-nc', $licenseorder); + + $this->assertGreaterThan($initialposition, $newposition); + } + +} diff --git a/admin/tool/licensemanager/version.php b/admin/tool/licensemanager/version.php new file mode 100644 index 00000000000..faf9c51949d --- /dev/null +++ b/admin/tool/licensemanager/version.php @@ -0,0 +1,31 @@ +. + +/** + * Version details for component 'tool_licensemanager'. + * + * @package tool_licensemanager + * @copyright Tom Dickman + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2020050600; +$plugin->requires = 2020050200; // Requires this Moodle version. +$plugin->component = 'tool_licensemanager'; + +$plugin->maturity = MATURITY_STABLE; diff --git a/lang/en/admin.php b/lang/en/admin.php index 08ccfaad6dc..19c40d75fd5 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -81,7 +81,6 @@ $string['autolang'] = 'Language autodetect'; $string['autologinguests'] = 'Auto-login guests'; $string['searchareas'] = 'Search areas'; $string['availableto'] = 'Available to'; -$string['availablelicenses'] = 'Available licences'; $string['backgroundcolour'] = 'Transparent colour'; $string['backups'] = 'Backups'; $string['backup_shortname'] = 'Use course name in backup filename'; @@ -763,7 +762,6 @@ $string['managecustomfields'] = 'Manage custom field types'; $string['manageformats'] = 'Manage course formats'; $string['manageformatsgotosettings'] = 'Default format can be changed in {$a}'; $string['managelang'] = 'Manage'; -$string['managelicenses'] = 'Manage licences'; $string['manageqbehaviours'] = 'Manage question behaviours'; $string['manageqtypes'] = 'Manage question types'; $string['maturity50'] = 'Alpha'; @@ -1046,6 +1044,8 @@ $string['registration_help'] = 'By registering: $string['registrationwarning'] = 'Your site is not yet registered.'; $string['registrationwarningcontactadmin'] = 'Your site is not yet registered. Please notify your administrator.'; $string['releasenoteslink'] = 'For information about this version of Moodle, please see the online Release Notes'; +$string['rememberuserlicensepref'] = 'Remember user licence preference'; +$string['rememberuserlicensepref_help'] = 'If enabled, the last licence selected by the user is preselected when uploading a file in the file picker. Otherwise, the default site licence is preselected.'; $string['rememberusername'] = 'Remember username'; $string['rememberusername_desc'] = 'Enable if you want to store permanent cookies with usernames during user login. Permanent cookies may be considered a privacy issue if used without consent.'; $string['reportsmanage'] = 'Manage reports'; @@ -1461,3 +1461,7 @@ $string['registermoodleorgli2'] = 'Statistics about your site will be added to t $string['registerwithmoodleorg'] = 'Register your site'; $string['configrequestcategoryselection'] = 'Allow the selection of a category when requesting a course.'; $string['requestcategoryselection'] = 'Enable category selection'; + +// Deprecated since Moodle 3.9. +$string['availablelicenses'] = 'Available licences'; +$string['managelicenses'] = 'Manage licences'; diff --git a/lang/en/cache.php b/lang/en/cache.php index 6c24f1b9eeb..6c2245214f1 100644 --- a/lang/en/cache.php +++ b/lang/en/cache.php @@ -60,6 +60,7 @@ $string['cachedef_groupdata'] = 'Course group information'; $string['cachedef_h5p_content_type_translations'] = 'H5P content-type libraries translations'; $string['cachedef_htmlpurifier'] = 'HTML Purifier - cleaned content'; $string['cachedef_langmenu'] = 'List of available languages'; +$string['cachedef_license'] = 'List of licences'; $string['cachedef_message_time_last_message_between_users'] = 'Time created for most recent message in a conversation'; $string['cachedef_modelfirstanalyses'] = 'First analysis by model and analysable'; $string['cachedef_locking'] = 'Locking'; diff --git a/lang/en/deprecated.txt b/lang/en/deprecated.txt index 0164c2870ea..1bb420fea0d 100644 --- a/lang/en/deprecated.txt +++ b/lang/en/deprecated.txt @@ -141,4 +141,6 @@ europe/belfast,core_timezones pacific/ponape,core_timezones pacific/truk,core_timezones pacific/yap,core_timezones -editsettings,core_badges \ No newline at end of file +editsettings,core_badges +availablelicenses,core_admin +managelicenses,core_admin diff --git a/lang/en/license.php b/lang/en/license.php index 1c40f7800bd..f33e68718bd 100644 --- a/lang/en/license.php +++ b/lang/en/license.php @@ -22,6 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +// Core licenses. $string['allrightsreserved'] = 'All rights reserved'; $string['cc'] = 'Creative Commons'; $string['cc-nc'] = 'Creative Commons - No Commercial'; @@ -30,4 +31,10 @@ $string['cc-nc-sa'] = 'Creative Commons - No Commercial ShareAlike'; $string['cc-nd'] = 'Creative Commons - NoDerivs'; $string['cc-sa'] = 'Creative Commons - ShareAlike'; $string['public'] = 'Public domain'; -$string['unknown'] = 'Other'; +$string['unknown'] = 'Licence not specified'; + +// Error messages. +$string['cannotdeletecore'] = 'Cannot delete a standard licence'; +$string['cannotdeletelicenseinuse'] = 'Cannot delete a licence which is currently assigned to one or more files'; +$string['licensenotfoundshortname'] = 'Cannot find a licence with the short name \'{$a}\''; +$string['missinglicensesortorder'] = 'Cannot set licence order, one or more installed licences is missing from new order'; diff --git a/lib/adminlib.php b/lib/adminlib.php index b75e4e19c14..1541db2f81a 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -7243,40 +7243,72 @@ class admin_setting_manageantiviruses extends admin_setting { * Special class for license administration. * * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. + * @todo MDL-45184 This class will be deleted in Moodle 4.3. */ class admin_setting_managelicenses extends admin_setting { /** - * Calls parent::__construct with specific arguments + * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. + * @todo MDL-45184 This class will be deleted in Moodle 4.3 */ public function __construct() { - $this->nosave = true; - parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', ''); + global $ADMIN; + + debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', + DEBUG_DEVELOPER); + + // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already. + if (!is_null($ADMIN->locate('licensemanager'))) { + $temp = new admin_externalpage('licensemanager', + get_string('licensemanager', 'tool_licensemanager'), + \tool_licensemanager\helper::get_licensemanager_url()); + + $ADMIN->add('license', $temp); + } } /** * Always returns true, does nothing * + * @deprecated since Moodle 3.9 MDL-45184. + * @todo MDL-45184 This method will be deleted in Moodle 4.3 + * * @return true */ public function get_setting() { + debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', + DEBUG_DEVELOPER); + return true; } /** * Always returns true, does nothing * + * @deprecated since Moodle 3.9 MDL-45184. + * @todo MDL-45184 This method will be deleted in Moodle 4.3 + * * @return true */ public function get_defaultsetting() { + debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', + DEBUG_DEVELOPER); + return true; } /** * Always returns '', does not write anything * + * @deprecated since Moodle 3.9 MDL-45184. + * @todo MDL-45184 This method will be deleted in Moodle 4.3 + * * @return string Always returns '' */ public function write_setting($data) { + debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', + DEBUG_DEVELOPER); + // do not write any setting return ''; } @@ -7284,53 +7316,18 @@ class admin_setting_managelicenses extends admin_setting { /** * Builds the XHTML to display the control * + * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead. + * @todo MDL-45184 This method will be deleted in Moodle 4.3 + * * @param string $data Unused * @param string $query * @return string */ public function output_html($data, $query='') { - global $CFG, $OUTPUT; - require_once($CFG->libdir . '/licenselib.php'); - $url = "licenses.php?sesskey=" . sesskey(); + debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.', + DEBUG_DEVELOPER); - // display strings - $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none')); - $licenses = license_manager::get_licenses(); - - $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true); - - $return .= $OUTPUT->box_start('generalbox editorsui'); - - $table = new html_table(); - $table->head = array($txt->name, $txt->enable); - $table->colclasses = array('leftalign', 'centeralign'); - $table->id = 'availablelicenses'; - $table->attributes['class'] = 'admintable generaltable'; - $table->data = array(); - - foreach ($licenses as $value) { - $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank')); - - if ($value->enabled == 1) { - $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname, - $OUTPUT->pix_icon('t/hide', get_string('disable'))); - } else { - $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname, - $OUTPUT->pix_icon('t/show', get_string('enable'))); - } - - if ($value->shortname == $CFG->sitedefaultlicense) { - $displayname .= ' '.$OUTPUT->pix_icon('t/locked', get_string('default')); - $hideshow = ''; - } - - $enabled = true; - - $table->data[] =array($displayname, $hideshow); - } - $return .= html_writer::table($table); - $return .= $OUTPUT->box_end(); - return highlight($query, $return); + redirect(\tool_licensemanager\helper::get_licensemanager_url()); } } diff --git a/lib/classes/plugin_manager.php b/lib/classes/plugin_manager.php index ba448c94896..0d8d832e68b 100644 --- a/lib/classes/plugin_manager.php +++ b/lib/classes/plugin_manager.php @@ -1998,10 +1998,11 @@ class core_plugin_manager { 'tool' => array( 'analytics', 'availabilityconditions', 'behat', 'capability', 'cohortroles', 'customlang', - 'dataprivacy', 'dbtransfer', 'filetypes', 'generator', 'health', 'httpsreplace', 'innodb', 'installaddon', - 'langimport', 'log', 'lp', 'lpimportcsv', 'lpmigrate', 'messageinbound', 'mobile', 'multilangupgrade', - 'monitor', 'oauth2', 'phpunit', 'policy', 'profiling', 'recyclebin', 'replace', 'spamcleaner', 'task', - 'templatelibrary', 'uploadcourse', 'uploaduser', 'unsuproles', 'usertours', 'xmldb' + 'dataprivacy', 'dbtransfer', 'filetypes', 'generator', 'health', 'httpsreplace', 'innodb', + 'installaddon', 'langimport', 'licensemanager', 'log', 'lp', 'lpimportcsv', 'lpmigrate', 'messageinbound', + 'mobile', 'multilangupgrade', 'monitor', 'oauth2', 'phpunit', 'policy', 'profiling', 'recyclebin', + 'replace', 'spamcleaner', 'task', 'templatelibrary', 'uploadcourse', 'uploaduser', 'unsuproles', + 'usertours', 'xmldb' ), 'webservice' => array( diff --git a/lib/db/caches.php b/lib/db/caches.php index ac2fa19aa2a..14dca042857 100644 --- a/lib/db/caches.php +++ b/lib/db/caches.php @@ -462,4 +462,11 @@ $definitions = array( 'staticacceleration' => true, 'staticaccelerationsize' => 100 ], + + // Cache for licenses. + 'license' => [ + 'mode' => cache_store::MODE_APPLICATION, + 'simplekeys' => false, + 'simpledata' => false + ], ); diff --git a/lib/db/install.xml b/lib/db/install.xml index 2bc9085f701..be3ac0058ba 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -2535,6 +2535,7 @@ + @@ -2853,6 +2854,8 @@ + + diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index 1f5796e3f6f..d0d822d82b6 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -2392,5 +2392,40 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2020052000.00); } + if ($oldversion < 2020052200.01) { + + // Define field custom to be added to license. + $table = new xmldb_table('license'); + $field = new xmldb_field('custom', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); + + // Conditionally launch add field custom. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Define field sortorder to be added to license. + $field = new xmldb_field('sortorder', XMLDB_TYPE_INTEGER, '5', null, XMLDB_NOTNULL, null, '0'); + + // Conditionally launch add field sortorder. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Define index license (not unique) to be added to files. + $table = new xmldb_table('files'); + $index = new xmldb_index('license', XMLDB_INDEX_NOTUNIQUE, ['license']); + + // Conditionally launch add index license. + if (!$dbman->index_exists($table, $index)) { + $dbman->add_index($table, $index); + } + + // Upgrade the core license details. + upgrade_core_licenses(); + + // Main savepoint reached. + upgrade_main_savepoint(true, 2020052200.01); + } + return true; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index d2ebc51d0a7..8e66a0f17a3 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -628,3 +628,124 @@ function upgrade_analytics_fix_contextids_defaults() { $params = ['zero' => '0', 'null' => 'null']; $DB->execute("UPDATE {analytics_models} set contextids = null WHERE " . $select, $params); } + +/** + * Upgrade core licenses shipped with Moodle. + */ +function upgrade_core_licenses() { + global $CFG, $DB; + + $corelicenses = []; + + $license = new stdClass(); + $license->shortname = 'unknown'; + $license->fullname = 'Licence not specified'; + $license->source = ''; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'allrightsreserved'; + $license->fullname = 'All rights reserved'; + $license->source = 'https://en.wikipedia.org/wiki/All_rights_reserved'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'public'; + $license->fullname = 'Public domain'; + $license->source = 'https://en.wikipedia.org/wiki/Public_domain'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc'; + $license->fullname = 'Creative Commons'; + $license->source = 'https://creativecommons.org/licenses/by/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc-nd'; + $license->fullname = 'Creative Commons - NoDerivs'; + $license->source = 'https://creativecommons.org/licenses/by-nd/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc-nc-nd'; + $license->fullname = 'Creative Commons - No Commercial NoDerivs'; + $license->source = 'https://creativecommons.org/licenses/by-nc-nd/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc-nc'; + $license->fullname = 'Creative Commons - No Commercial'; + $license->source = 'https://creativecommons.org/licenses/by-nc/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc-nc-sa'; + $license->fullname = 'Creative Commons - No Commercial ShareAlike'; + $license->source = 'https://creativecommons.org/licenses/by-nc-sa/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + $license = new stdClass(); + $license->shortname = 'cc-sa'; + $license->fullname = 'Creative Commons - ShareAlike'; + $license->source = 'https://creativecommons.org/licenses/by-sa/3.0/'; + $license->enabled = 1; + $license->version = '2010033100'; + $license->custom = 0; + $corelicenses[] = $license; + + foreach ($corelicenses as $corelicense) { + // Check for current license to maintain idempotence. + $currentlicense = $DB->get_record('license', ['shortname' => $corelicense->shortname]); + if (!empty($currentlicense)) { + $corelicense->id = $currentlicense->id; + // Remember if the license was enabled before upgrade. + $corelicense->enabled = $currentlicense->enabled; + $DB->update_record('license', $corelicense); + } else if (!isset($CFG->upgraderunning) || during_initial_install()) { + // Only install missing core licenses if not upgrading or during initial install. + $DB->insert_record('license', $corelicense); + } + } + + // Add sortorder to all licenses. + $licenses = $DB->get_records('license'); + $sortorder = 1; + foreach ($licenses as $license) { + $license->sortorder = $sortorder++; + $DB->update_record('license', $license); + } + + // Set the license config values, used by file repository for rendering licenses at front end. + $activelicenses = $DB->get_records_menu('license', ['enabled' => 1], 'id', 'id, shortname'); + set_config('licenses', implode(',', $activelicenses)); + + $sitedefaultlicense = get_config('', 'sitedefaultlicense'); + if (empty($sitedefaultlicense) || !in_array($sitedefaultlicense, $activelicenses)) { + set_config('sitedefaultlicense', reset($activelicenses)); + } +} diff --git a/lib/form/filemanager.js b/lib/form/filemanager.js index b3ebb6ea4af..5fe7586e5cc 100644 --- a/lib/form/filemanager.js +++ b/lib/form/filemanager.js @@ -704,17 +704,29 @@ M.form_filemanager.init = function(Y, options) { this.filemanager.one('.fp-content').fp_display_filelist(options, list, this.lazyloading); this.content_scrolled(); }, - populate_licenses_select: function(node) { - if (!node) { + populateLicensesSelect: function(licensenode, filenode) { + if (!licensenode) { return; } - node.setContent(''); - var licenses = this.options.licenses; + licensenode.setContent(''); + var selectedlicense = this.filepicker_options.defaultlicense; + if (filenode) { + // File has a license already, use it. + selectedlicense = filenode.license; + } else if (this.filepicker_options.rememberuserlicensepref) { + selectedlicense = this.get_preference('recentlicense'); + } + var licenses = this.filepicker_options.licenses; for (var i in licenses) { - var option = Y.Node.create('