diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index f5b1072..cd82f6f 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -16,7 +16,7 @@ jobs: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 mariadb: - image: mariadb:10 + image: mariadb:10.6.7 env: MYSQL_USER: 'root' MYSQL_ALLOW_EMPTY_PASSWORD: "true" @@ -29,9 +29,19 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0'] - moodle-branch: ['MOODLE_401_STABLE'] - database: [pgsql, mariadb] + include: + - php: '8.2' + moodle-branch: 'MOODLE_403_STABLE' + database: 'pgsql' + - php: '8.0' + moodle-branch: 'MOODLE_402_STABLE' + database: 'mariadb' + - php: '8.0' + moodle-branch: 'MOODLE_401_STABLE' + database: 'pgsql' + - php: '7.4' + moodle-branch: 'MOODLE_400_STABLE' + database: 'mariadb' steps: - name: Check out repository code diff --git a/Documentation.md b/Documentation.md new file mode 100644 index 0000000..ee13766 --- /dev/null +++ b/Documentation.md @@ -0,0 +1,208 @@ +# Skills - Monitoring skills and learning time + +## Overview + +The "Skills" feature is designed to enhance learner engagement within Moodle. As a tool-type plugin, Skills facilitates the awarding of skills based on points. Learners can acquire skills by completing linked courses or activities, fostering a sense of achievement and progress. + +The "Skills" feature allows administrators and teachers to configure up to 10 levels for each skill. Each level corresponds to a specific set of points, creating a structured progression system. This system enhances the granularity of skill development and provides learners with clear milestones to achieve. + +This documentation provides an in-depth guide to the various aspects and functionalities of the Skills feature. + +## Installation and initial setup + +### Installation + +You can install the Pulse plugin using the Moodle plugin installer. Here are the steps to follow: + +1. Download the "Skills" plugin from the Moodle plugins repository or from the bdecent website. +2. Log in to your Moodle site as an administrator. +3. Go to "Site administration > Plugins > Install plugins". +4. Upload the downloaded plugin ZIP file. +5. Follow the prompts to install the plugin. +6. Once the admin tool plugin is installed, you can manage it by going to Site Administration > Plugins > Admin Tools > Skills. From there, you can set up the skills and levels system, assign to courses and modules. + +Alternatively, you can also install the Skills plugin manually. Here are the steps to follow: + +1. Download the "Skills" plugin from the Moodle plugins repository +2. Unzip the downloaded file. +3. Upload the skills folder to the moodle/admin/tool directory on your Moodle server. +4. Log in to your Moodle site as an administrator. +5. Go to "Site administration > Notifications". +6. Follow the prompts to install the plugin. + +## Key Features: + +1. **Skill Creation and Management**: Create, edit, and delete skills seamlessly. + +2. **Add Levels**: Create multiple levels with specific set of points for each skill. + +3. **User Points System**: Allow users to earn points for mastering skills and track their progress. + +4. **Logs and Reports**: Access detailed logs of user points and generate reports on skill usage. + +5. **Privacy Controls**: Configure privacy settings to handle user consent and data protection. + +6. **Integration with Courses**: Link skills to courses, providing a holistic view of a user's achievements. + + +## How it works. + +Admins initiate the process by creating a skill and establishing levels with assigned points for the skill. The skill is then linked to a course through the "Manage Skills" page. Teachers wield multiple options for awarding points: + +1. **Points**: Assign a specific number of points to the user. + +2. **Force Levels**: Compel the user's points to align with a designated level (potentially decreasing their current points). + +3. **Set Levels**: Adjust the user's points to match a specified level, provided the user has sufficient points. + +Upon course completion, users earn points and acquire skills based on the established setup. + + +# Manage skills: + + Manage skills to create a new skill and edit existing skills. + +1. **Filter:** The "Filter" option is used to filter the list of skills within the category lists. + +### Active Skills: + +List of skills currently active and user can earn skills and teachers can use it in there courses. + +The "Active Skills" tab displays a full list of created skills or the filtered skills within the categories. + +1. **Key:** + + Each skill should be uniquely identified by a distinct key to maintain clarity and organization in the system. + +2. **Skill Name:** + + The designation 'Skill Name' serves as the unique identifier for each individual skill. + +3. **Description:** + + The "Description" refers to a detailed explanation or information provided about various skills. + +4. **Time created:** + + Time Created" indicates the record of when specific skills were established. + +5. **Course Categories:** + + It displays the list of categories added for specific skills. + +6. **Actions:** + + 1. ***Edit settings:*** Click the 'Edit' icon in the table to make changes to a specific skill. + 2. ***Status:*** Use this toggle icon in the table to enable or disable the status of the specific skill. + 3. **Archive:** Click the "Archive" option in the table to Archive the specific skill. + +### Archived Skills: + +Archived skills are not available in course list, and not awared to students. + +The "Archived Skills" tab displays a list of archived skills or the filtered skills within the categories. + +**Actions:** + + 1. ***Delete:*** Click the 'delete' option to remove a specific skill. + 2. ***Active:*** Click the 'active' option to move a specific skill to the "Active" tab. + + +## General Configuration + +Use the "**Create Skill**" button to create a new skills. Skills comes with following configurations. + +These configurations establish the rules and standards for assessing, tracking, and awarding skills. + +1. **Skill Name:** + + The designation 'Skill Name' serves as the unique identifier for each individual skill. + +2. **Key:** + + Each skill should be uniquely identified by a distinct key to maintain clarity and organization in the system. + +3. **Description:** + + The term "Skills Description" refers to a detailed explanation or information provided about various skills. + +4. **Status:** + + Choose the status for this skill: + + ***Enabled:*** The skill will be added to all courses that match the course categories setting below and can be configured by teachers. + + ***Disabled:*** The skill will not be added to any courses and cannot be used by teachers. + +5. **Learning time:** + + The time required to complete this skill within the course. + +6. **Skill color:** + + Choose a color to represent the skill level. + +7. **Available in course categories:** + + Select the categories to make this skill available exclusively to courses within the chosen category. If no category is selected, the course will be available globally across all categories. + +## Levels - General settings + +1. **Number of levels** + +Select the number of levels available for this skill. Each level have a specific point requirement for achievement and other following configurations. + +2. **Level Name** + +Provide the level name for the specific skill. + +3. **Level Point** + +Please specify the point value for the specific skill level. + +4. **Level Color** + +Select a color to represent the level. This will override the general skill color for visualization purposes. + +5. **Level Image** + +Please upload an image that represents the level of skill. This will be used for visualization. + + +# Course skills settings + +To access the skills list and assign them to a course, utilize the "Manage Skills" link found in the secondary navigation of the course. Within this interface, you have the option to grant a precise number of points or set the points required to reach a specific skill level. + +Simply employ the "Edit" icon in the table to activate the skill for the course and configure the settings for "Upon Course Completion" and "Points." + +1. **Status:** + + Choose the status for this skill: + + ***Enabled:*** The skill will be added to the course that match the upon completion setting below and can be configured by teachers. + + ***Disabled:*** The skill will not be added to any courses and cannot be used by teachers. + +2. **Upon course completion:** + + Upon course completion, you can choose from several options to determine what should happen at the end of the course. + + ***Nothing:*** Choose 'Nothing' to use activity completion, instead of course completion, for awarding points. + + ***Points:*** Select 'Add points' to have the specified number of skill points added upon course completion. Please note that using negative numbers will result in a deduction of points. + + ***Set level:*** Choose 'Set level' to have the completion of the course add the necessary number of points required to reach that level, unless the student already has more points. + + ***Force level:*** Select 'Force level' to set the number of points to the amount required for that level upon course completion, regardless of the student's previous level/points. This may result in students having fewer points than before. + +3. **Points:** + + Enter the number of skill points to be awarded or deducted. Use a positive number to add points and a negative number to deduct points. + + ***Example:*** + Entering "50" will add 50 points. + Entering "-20" will deduct 20 points. + +4. **Level:** + + Choose the desired skill level for this course. Upon completion, the student will receive the corresponding number of points required to achieve the selected level. diff --git a/README.md b/README.md index eee015a..0f868c7 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ See http://docs.moodle.org/en/Installing_plugins for details on installing Moodl Admins or users with the capability tool/skills:manage (by default given to managers) need to create skills under Site Administration > Plugins > Tools > Skills and make them available either globally or for specific course categories. -Teachers (or users with the capability tool/skills:managecourseskills) can then manage skills in their course from the "Manage skills" page which is found in the secondary navigation of the course. From there, teachers can then enable specific skills and configure how many points students earn upon completion of the course. +Teachers (or users with the capability tool/skills:managecourseskillslist) can then manage skills in their course from the "Manage skills" page which is found in the secondary navigation of the course. From there, teachers can then enable specific skills and configure how many points students earn upon completion of the course. # Documentation diff --git a/addon/README.md b/addon/README.md new file mode 100644 index 0000000..aad6063 --- /dev/null +++ b/addon/README.md @@ -0,0 +1,12 @@ +# Skills Plugin Addons + +Welcome to the Skills plugin addons! This addon houses additional features and sub-plugin addons for the "Tool skills" plugin in Moodle. + +## Overview + +The Skills plugin enhances the functionality to providing advanced features related to skills management. This addon focuses on extending the plugin with extra addons, specifically designed for extend user points allocation methods. + +# Copyright + +bdecent gmbh +bdecent.de diff --git a/classes/courseskills.php b/classes/courseskills.php index 05ae2cd..0d938c9 100644 --- a/classes/courseskills.php +++ b/classes/courseskills.php @@ -24,10 +24,14 @@ namespace tool_skills; +defined('MOODLE_INTERNAL') || die(); + use completion_info; use moodle_exception; use stdClass; +require_once($CFG->dirroot.'/admin/tool/skills/lib.php'); + /** * Manage the skills for courses. Trigger skills to assign point for users. */ @@ -60,7 +64,7 @@ protected function __construct(int $courseid) { } /** - * Create the retunr the clas instance for this skillcourse id. + * Create the retun the clas instance for this skillcourse id. * * @param int $courseid * @return self @@ -70,7 +74,7 @@ public static function get(int $courseid) : self { } /** - * Create the retunr the clas instance for this skillcourse id. + * Fetch to the skills course data . * * @param int $skillid * @return self @@ -105,6 +109,7 @@ public function get_instance_skills(): array { return array_map(fn($sk) => skills::get($sk->skill), $skills); } + /** * Remove the course skills records. * @@ -115,6 +120,8 @@ public function remove_instance_skills() { $DB->delete_records('tool_skills_courses', ['courseid' => $this->courseid]); + \tool_skills\helper::extend_addons_remove_course_instance($this->courseid); + $this->get_logs()->delete_method_log($this->courseid, 'course'); } @@ -193,7 +200,7 @@ public function get_user_earned_points(int $userid) { * @return void */ public function manage_course_completions(int $userid) { - global $CFG; + global $CFG, $DB; require_once($CFG->dirroot . '/lib/completionlib.php'); @@ -204,13 +211,37 @@ public function manage_course_completions(int $userid) { // Get course skills records. $skills = $this->get_instance_skills(); foreach ($skills as $skillcourseid => $skill) { + // Create a skill course record instance for this skill. $this->set_skill_instance($skillcourseid); - $skill->assign_skills($this, $userid); + // Get the data. + $csdata = $this->build_data(); + + // Start the database transaction. + $transaction = $DB->start_delegated_transaction(); + + switch ($csdata->uponcompletion) { + + case skills::COMPLETIONFORCELEVEL: + $skill->force_level($this, $csdata->level, $userid); + break; + + case skills::COMPLETIONSETLEVEL: + $skill->moveto_level($this, $csdata->level, $userid); + break; + + case skills::COMPLETIONPOINTS: + $skill->increase_points($this, $csdata->points, $userid); + break; + } + + // End the database transaction. + $transaction->allow_commit(); } } } + /** * Manage users completion. * diff --git a/classes/events/observer.php b/classes/events/observer.php index 2dbe2ef..e9cfd17 100644 --- a/classes/events/observer.php +++ b/classes/events/observer.php @@ -59,7 +59,7 @@ public static function course_completed(\core\event\course_completed $event) { public static function user_deleted(\core\event\user_deleted $event) { // Fetch the event data. $data = $event->get_data(); - $relateduserid = $data['userid']; // Completed user id. + $relateduserid = $data['objectid']; // Completed user id. // Remove the user skill points. user::get($relateduserid)->remove_user_skillpoints(); } diff --git a/classes/form/course_form.php b/classes/form/course_form.php index 7e69e0e..11eee9d 100644 --- a/classes/form/course_form.php +++ b/classes/form/course_form.php @@ -96,6 +96,15 @@ public function definition() { $mform->addHelpButton('level', 'completionlevel', 'tool_skills'); } + /** + * Check the access for the submit data to this form. + * + * @return bool + */ + protected function check_access_for_dynamic_submission(): void { + // TODO: Validatation of user capability goes here. + } + /** * Get the context of this form used. * @@ -108,15 +117,6 @@ protected function get_context_for_dynamic_submission(): \context { return $courseid ? \context_course::instance($courseid) : \context_system::instance(); } - /** - * Check the access for the submit data to this form. - * - * @return bool - */ - protected function check_access_for_dynamic_submission(): void { - // TODO: Validatation of user capability goes here. - } - /** * Process the submission from AJAX. * diff --git a/classes/form/skills_form.php b/classes/form/skills_form.php index 468ecdc..288fd8a 100644 --- a/classes/form/skills_form.php +++ b/classes/form/skills_form.php @@ -31,7 +31,7 @@ use context_system; use html_writer; -use \tool_skills\skills; +use tool_skills\skills; /** * Skills create/edit form. @@ -178,7 +178,7 @@ public function definition_after_data() { if ($i == 0 && !$mform->getElementValue("levels[$i][name]")) { $mform->setDefaults([ "levels[$i][name]" => get_string('skillslevel', 'tool_skills') . ' ' . $i, - "levels[$i][points]" => '0' + "levels[$i][points]" => '0', ]); } @@ -233,7 +233,7 @@ public function data_preprocessing(&$defaultvalues) { $defaultvalues = (object) $defaultvalues; $filemanagers = [ - 'image' => 'image' + 'image' => 'levelimage', ]; // Levels count. @@ -242,13 +242,11 @@ public function data_preprocessing(&$defaultvalues) { // Prepare the file manager fields to store images. foreach ($filemanagers as $configname => $filearea) { // For all levels in this skills. - for ($i = 1; $i <= $levelscount; $i++) { + for ($i = 0; $i <= $levelscount; $i++) { if (empty($defaultvalues->levels[$i])) { continue; } - // Fileare for this level. - $filearea .= '_' . $i; // Draft item id. $draftitemid = file_get_submitted_draft_itemid($filearea); // Use the level id as item id. @@ -263,6 +261,7 @@ public function data_preprocessing(&$defaultvalues) { $defaultvalues->levels[$i][$configname] = $draftitemid; } } + } /** @@ -282,7 +281,7 @@ public function data_postprocessing(&$data) { $data = (object) $data; $filemanagers = [ - 'image' => 'image' + 'image' => 'levelimage', ]; $levelscount = $data->levelscount; @@ -290,16 +289,16 @@ public function data_postprocessing(&$data) { // Prepare the file manager fields to store images. foreach ($filemanagers as $configname => $filearea) { - for ($i = 1; $i <= $levelscount; $i++) { + for ($i = 0; $i <= $levelscount; $i++) { if (empty($data->levels[$i])) { continue; } + // Level id used as item id. $levelid = $data->levels[$i]['id'] ?: 0; - // Now save the files in correct part of the File API. - $filearea .= '_' . $i; + // Now save the files in correct part of the File API. file_save_draft_area_files( $data->levels[$i][$configname], $context->id, 'tool_skills', $filearea, $levelid, $this->get_editor_options($context) @@ -321,7 +320,7 @@ protected function get_editor_options($context=null) { 'subdirs' => true, 'maxfiles' => 1, 'maxbytes' => 1000000, - 'context' => $context ?: $PAGE->context + 'context' => $context ?: $PAGE->context, ]; } diff --git a/classes/helper.php b/classes/helper.php index d4b52c0..dbb2d9e 100644 --- a/classes/helper.php +++ b/classes/helper.php @@ -49,7 +49,7 @@ public static function skills_buttons($tab, $filtered=false) { if (has_capability('tool/skills:manage', $PAGE->context)) { // Setup create template button on page. $caption = get_string('createskill', 'tool_skills'); - $editurl = new \moodle_url('/admin/tool/skills/manage/edit.php', array('sesskey' => sesskey())); + $editurl = new \moodle_url('/admin/tool/skills/manage/edit.php', ['sesskey' => sesskey()]); // IN Moodle 4.2, primary button param depreceted. $primary = defined('single_button::BUTTON_PRIMARY') ? single_button::BUTTON_PRIMARY : true; @@ -61,7 +61,7 @@ public static function skills_buttons($tab, $filtered=false) { $button .= \html_writer::start_div('filter-form-container'); $button .= \html_writer::link('javascript:void(0)', $OUTPUT->pix_icon('i/filter', 'Filter'), [ 'id' => 'tool-skills-filter', - 'class' => 'sort-toolskills btn btn-primary ml-2 ' . ($filtered ? 'filtered' : '') + 'class' => 'sort-toolskills btn btn-primary ml-2 ' . ($filtered ? 'filtered' : ''), ]); $filter = new \tool_skills_table_filter(null, ['t' => $tab]); $button .= \html_writer::tag('div', $filter->render(), ['id' => 'tool-skills-filterform', 'class' => 'hide']); @@ -70,4 +70,169 @@ public static function skills_buttons($tab, $filtered=false) { return $button; } + /** + * Undocumented function + * + * @return void + */ + public static function get_skills_list() { + global $DB; + // List of skills available. + $skills = $DB->get_records('tool_skills', []); + array_walk($skills, function(&$skill) { + $skill = \tool_skills\skills::get($skill->id); + }); + + return $skills; + } + + /** + * Get the list of completed skills of the user. + * + * @param int $userid + * @return array + */ + public static function get_user_completedskills(int $userid) { + global $DB; + // List of skills available. + $skills = \tool_skills\user::get($userid)->get_user_skills(); + + $completed = []; + foreach ($skills as $skill) { + $skillpoint = $skill->skillobj->get_points_to_earnskill(); + if ($skillpoint <= 0) { + continue; + } + + $points = $skill->userpoints->points ?? 0; + $percentage = ($points / $skillpoint) * 100; + + if ($percentage >= 100) { + $completed[] = $skill->skill; + } + } + + return !empty($completed) ? array_unique($completed) : []; + } + + /** + * Calculate the skills total points assigned for the given courses. + * + * @param array $courseids + * @return int + */ + public static function get_courses_skill_points(array $courseids) { + global $DB; + + list($insql, $inparams) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'skp'); + + $sql = "SELECT tsl.skill, MAX(tsl.points) AS skillpoints + FROM {tool_skills_levels} tsl + JOIN {tool_skills_courses} tsc ON tsc.skill = tsl.skill + WHERE tsc.status = 1 AND tsc.courseid $insql + GROUP BY tsl.skill"; + + $skills = $DB->get_records_sql($sql, $inparams); + + $skillpoints = array_sum(array_column($skills, 'skillpoints')); + + return $skillpoints; + } + + /** + * Get addon extend method. + * + * @param string $method + * @return array + */ + public static function get_addon_extend_method($method) { + $addon = new \tool_skills\plugininfo\skilladdon(); + $methods = $addon->get_plugins_base($method); + return $methods; + } + + /** + * Extend the remove skills addon. + * + * @param int $skillid Id of the skill. + * @return void + */ + public static function extend_addons_remove_skills(int $skillid) { + // Extend the method from sub plugins. + $methods = self::get_addon_extend_method('remove_skills'); + foreach ($methods as $method) { + // Trigger the skill id. + $method->remove_skills($skillid); + } + + } + + + /** + * Remove course instance. + * + * @param int $courseid Course ID. + * @return void + */ + public static function extend_addons_remove_course_instance(int $courseid) { + // Extend the method from sub plugins. + $methods = self::get_addon_extend_method('remove_course_instance'); + foreach ($methods as $method) { + // Trigger the skill id. + $method->remove_course_instance($courseid); + } + } + + /** + * Add the activity method user skills data . + * + * @param int $point + * @return void + */ + public static function extend_addons_add_userskills_data(&$point) { + // Extend the method from sub plugins. + $methods = self::get_addon_extend_method('add_userskills_data'); + foreach ($methods as $method) { + // Trigger the skill id. + $method->add_userskills_data($point); + } + } + + /** + * Add to the user points content in profile page. + * + * @param int $skillstr Course ID. + * @param stdclass $data Data. + * @return void + */ + public static function extend_addons_add_user_points_content(&$skillstr, $data) { + // Extend the method from sub plugins. + $methods = self::get_addon_extend_method('add_user_points_content'); + foreach ($methods as $method) { + // Trigger the skill id. + $method->add_user_points_content($skillstr, $data); + } + } + + /** + * Add the activity method user skills data . + * + * @param \tool_skills\allocation_method $skillobj + * @return string + */ + public static function extend_addons_get_allocation_method($skillobj) { + // Extend the method from sub plugins. + $methods = self::get_addon_extend_method('get_allocation_method'); + foreach ($methods as $method) { + // Trigger the skill id. + $result = $method->get_allocation_method($skillobj); + + // Find the allocation method, break the check. + if ($result) { + break; + } + } + return $result ?? ''; + } + } diff --git a/classes/logs.php b/classes/logs.php index b3ed97b..1297c39 100644 --- a/classes/logs.php +++ b/classes/logs.php @@ -22,6 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace tool_skills; +use moodle_exception; /** * Maintain the user log of points allocations. @@ -49,9 +50,33 @@ public static function get() { return self::$instance; } + /** + * Get logs function. + * + * @param int $skillid ID of the skill + * @param int $userid ID of the user earned the point + * @param int $methodid Method id. ID of the table. + * @param string $method Method of the allocation, Course and activity methods are available current now. + * @param int $status Type of the points awarded. 1 for increase, 0 for negative points. + * @return void + */ + public function get_log(int $skillid, int $userid, int $methodid, string $method, int $status=1) { + global $DB; + + if ($log = $DB->get_record('tool_skills_awardlogs', ['skill' => $skillid, 'userid' => $userid, + 'method' => $method, 'methodid' => $methodid, ])) { + return $log; + } else { + throw new moodle_exception('skillawardnotfound', 'tool_skills'); + } + + return false; + } + /** * Add the allocated user points and method of allocation to the logs. * + * @param int $skillid ID of the skill * @param int $userid ID of the user earned the point * @param int $points Points the user earned, Contains negative points. * @param int $methodid Method id. ID of the table. @@ -59,19 +84,22 @@ public static function get() { * @param int $status Type of the points awarded. 1 for increase, 0 for negative points. * @return int ID of the logs inserted ID. */ - public function add(int $userid, int $points, int $methodid, string $method, int $status=1) { + public function add(int $skillid, int $userid, int $points, int $methodid, string $method, int $status=1) { global $DB; - $record = [ - 'userid' => $userid, - 'points' => $points, - 'methodid' => $methodid, - 'method' => $method, - 'status' => $status, - 'timecreated' => time() - ]; - - return $DB->insert_record('tool_skills_awardlogs', $record); + if (!$DB->record_exists('tool_skills_awardlogs', ['skill' => $skillid, 'userid' => $userid, + 'method' => $method, 'methodid' => $methodid, ])) { + $record = [ + 'skill' => $skillid, + 'userid' => $userid, + 'points' => $points, + 'methodid' => $methodid, + 'method' => $method, + 'status' => $status, + 'timecreated' => time(), + ]; + return $DB->insert_record('tool_skills_awardlogs', $record); + } } /** diff --git a/classes/plugininfo/skilladdon.php b/classes/plugininfo/skilladdon.php new file mode 100644 index 0000000..de5616a --- /dev/null +++ b/classes/plugininfo/skilladdon.php @@ -0,0 +1,80 @@ +. + +/** + * Subplugin type for tool skills - defined. + * + * @package tool_skills + * @copyright 2023, bdecent gmbh bdecent.de + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_skills\plugininfo; + +/** + * Skilladdon is subplugin of tool_skills. + */ +class skilladdon extends \core\plugininfo\base { + /** + * Returns the information about plugin availability + * + * True means that the plugin is enabled. False means that the plugin is + * disabled. Null means that the information is not available, or the + * plugin does not support configurable availability or the availability + * can not be changed. + * + * @return null|bool + */ + public function is_enabled() { + return true; + } + + /** + * Should there be a way to uninstall the plugin via the administration UI. + * + * By default uninstallation is not allowed, plugin developers must enable it explicitly! + * + * @return bool + */ + public function is_uninstall_allowed() { + return true; + } + + /** + * Get the list of action plugins with its base class. + * + * @param string $method + * @return array + */ + public function get_plugins_base($method) { + $plugins = \core_component::get_plugin_list('skilladdon'); + + if (!empty($plugins)) { + foreach ($plugins as $componentname => $pluginpath) { + $classname = "skilladdon_$componentname\manage_skills"; + if (!class_exists($classname)) { + continue; + } + + if (method_exists("skilladdon_$componentname\manage_skills", $method)) { + $instance = new $classname(); + $extend[] = $instance; + } + } + } + return $extend ?? []; + } +} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index f841144..cac3689 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -27,10 +27,10 @@ use context; use core_privacy\local\metadata\collection; -use \core_privacy\local\request\contextlist; -use \core_privacy\local\request\userlist; -use \core_privacy\local\request\approved_userlist; -use \core_privacy\local\request\approved_contextlist; +use core_privacy\local\request\contextlist; +use core_privacy\local\request\userlist; +use core_privacy\local\request\approved_userlist; +use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\helper; use core_privacy\local\request\transform; use core_privacy\local\request\writer; @@ -57,7 +57,7 @@ public static function get_metadata(collection $collection): collection { 'skill' => 'privacy:metadata:userpoints:skill', 'points' => 'privacy:metadata:userpoints:points', 'timecreated' => 'privacy:metadata:userpoints:timecreated', - 'timemodified' => 'privacy:metadata:userpoints:timemodified' + 'timemodified' => 'privacy:metadata:userpoints:timemodified', ]; $collection->add_database_table('tool_skills_userpoints', $userpointsmetadata, 'privacy:metadata:userpoints'); @@ -67,7 +67,7 @@ public static function get_metadata(collection $collection): collection { 'points' => 'privacy:metadata:awardlogs:points', 'methodid' => 'privacy:metadata:awardlogs:methodid', 'method' => 'privacy:metadata:awardlogs:method', - 'timecreated' => 'privacy:metadata:awardlogs:timecreated' + 'timecreated' => 'privacy:metadata:awardlogs:timecreated', ]; $collection->add_database_table('tool_skills_awardlogs', $awardlogsmetadata, 'privacy:metadata:awardlogs'); @@ -191,7 +191,7 @@ public static function delete_data_for_all_users_in_context(\context $context) { return; } - $courses = $DB->get_records('tool_skills_courses', array('courseid' => $course->id)); + $courses = $DB->get_records('tool_skills_courses', ['courseid' => $course->id]); foreach ($courses as $skillcourse) { $log = $DB->get_record('tool_skills_awardlogs', ['methodid' => $skillcourse->id, 'method' => 'course']); $points = $DB->get_record('tool_skill_userpoints', ['skill' => $log->skill, 'userid' => $log->userid]); diff --git a/classes/skills.php b/classes/skills.php index 39ecd51..80ced6a 100644 --- a/classes/skills.php +++ b/classes/skills.php @@ -24,11 +24,17 @@ namespace tool_skills; +defined('MOODLE_INTERNAL') || die(); + use moodle_exception; use stdClass; use context_system; use tool_skills\allocation_method; +require_once($CFG->dirroot.'/grade/lib.php'); +require_once($CFG->dirroot.'/grade/querylib.php'); +require_once($CFG->dirroot.'/admin/tool/skills/lib.php'); + /** * Skills manage instance, doing manage of skills tasks. */ @@ -72,6 +78,12 @@ class skills { */ public const COMPLETIONFORCELEVEL = 3; + /** + * Represent the upon completion result is points grade achieve in the activity. + * @var int + */ + public const COMPLETIONPOINTSGRADE = 4; + /** * Skill instance id. * @@ -238,6 +250,10 @@ public function delete_skill() { \tool_skills\level::remove_skill_levels($this->skillid); // Delete all its actions. \tool_skills\courseskills::remove_skills($this->skillid); + // Extend the addons remove skills. + \tool_skills\helper::extend_addons_remove_skills($this->skillid); + + $DB->delete_records('tool_skills_userpoints', ['skill' => $this->skillid]); return true; } @@ -286,14 +302,14 @@ public function duplicate() { */ public function get_points_to_earnskill() { - $levels = $this->get_levels(); + $levels = $this->get_levels(); // List of levels, available in the skill. - $pointstocomplete = 0; - foreach ($levels as $levelid => $level) { - $pointstocomplete += $level->points; + if (!empty($levels)) { + $levelpoints = array_column($levels, 'points'); + $pointstocomplete = max($levelpoints); } - return $pointstocomplete; + return $pointstocomplete ?? 0; } /** @@ -332,39 +348,6 @@ public function get_levels_count() : int { return count($this->get_levels()); } - /** - * Assign the skills to users. - * - * @param \tool_skills\allocation_method $skillobj - * @param int $userid - * - * @return void - */ - public function assign_skills($skillobj, int $userid) { - global $DB; - - $csdata = $skillobj->get_data(); - // Start the database transaction. - $transaction = $DB->start_delegated_transaction(); - - switch ($csdata->uponcompletion) { - - case self::COMPLETIONFORCELEVEL: - $this->force_level($skillobj, $csdata->level, $userid); - break; - - case self::COMPLETIONSETLEVEL: - $this->moveto_level($skillobj, $csdata->level, $userid); - break; - - case self::COMPLETIONPOINTS: - $this->increase_points($skillobj, $csdata->points, $userid); - break; - } - - $transaction->allow_commit(); - } - /** * Fetch the user skill points table record. * @@ -387,6 +370,7 @@ public function get_user_skill(int $userid, $create=true) { $record['timecreated'] = time(); $DB->insert_record('tool_skills_userpoints', $record); + return $this->get_user_skill($userid, false); // Don't need to create again. } @@ -402,7 +386,7 @@ public function get_user_skill(int $userid, $create=true) { * * @return void */ - protected function force_level(allocation_method $skillobj, int $levelid, int $userid) { + public function force_level($skillobj, int $levelid, int $userid) { // Fetch the level instance for this level. $level = level::get($levelid); @@ -422,7 +406,7 @@ protected function force_level(allocation_method $skillobj, int $levelid, int $u * * @return void */ - protected function moveto_level(allocation_method $skillobj, int $levelid, int $userid) { + public function moveto_level($skillobj, int $levelid, int $userid) { // User skill. $userskill = $this->get_user_skill($userid); // Fetch the level instance for this level. @@ -447,13 +431,12 @@ protected function moveto_level(allocation_method $skillobj, int $levelid, int $ * * @return void */ - protected function increase_points(allocation_method $skillobj, int $points, int $userid) { + public function increase_points($skillobj, int $points, int $userid) { // Get user skill current record, create new one if not found. $userskill = $this->get_user_skill($userid); // Increase the allocated points with current user points. $levelpoints = $userskill->points + $points; - // Find the method of the course skills. - $method = ($skillobj instanceof \tool_skills\courseskills) ? 'course' : ''; + // Update the new points for this user in db. $this->set_userskill_points($userid, $levelpoints); @@ -468,7 +451,7 @@ protected function increase_points(allocation_method $skillobj, int $points, int * @param int $points * @return int */ - protected function set_userskill_points(int $userid, int $points) : int { + public function set_userskill_points(int $userid, int $points) : int { global $DB; $record = ['skill' => $this->skillid, 'userid' => $userid]; @@ -476,6 +459,7 @@ protected function set_userskill_points(int $userid, int $points) : int { if ($data = $DB->get_record('tool_skills_userpoints', $record)) { $id = $data->id; $data->points = $points; + $data->timemodified = time(); $DB->update_record('tool_skills_userpoints', $data); @@ -497,15 +481,43 @@ protected function set_userskill_points(int $userid, int $points) : int { * @param int $points * @return void */ - protected function create_user_point_award(allocation_method $skillobj, int $userid, int $points) { + public function create_user_point_award($skillobj, int $userid, int $points) { + // Find the method of the course skills. - $method = ($skillobj instanceof \tool_skills\courseskills) ? 'course' : ''; + if ($skillobj instanceof \tool_skills\courseskills) { + $method = 'course'; + } else { + $method = helper::extend_addons_get_allocation_method($skillobj); + } // Allocation method id. $methodid = $skillobj->get_data()->id; // Log the point awarded to users and the method. - $this->log->add($userid, $points, $methodid, $method); + $this->log->add($this->skillid, $userid, $points, $methodid, $method); + } + + /** + * Get the list of proficient users in this skill. + * + * @return array + */ + public function get_proficient() { + global $DB; + + // Points to earn this skill. + $proficientpoint = $this->get_points_to_earnskill(); + // User points. + $userpoints = $DB->get_records('tool_skills_userpoints', ['skill' => $this->skillid]); + $list = []; + foreach ($userpoints as $id => $points) { + // Verify the user is reached the maximum points for the level. + if ($points->points >= $proficientpoint) { + $list[] = $points->userid; // This user is proficient in this skill. + } + } + // Return the list of proficient users id. + return $list; } /** @@ -582,4 +594,5 @@ public static function manage_instance($formdata) { return $skillid ?? false; } + } diff --git a/classes/table/archived_skills.php b/classes/table/archived_skills.php index c3d19ff..8989906 100644 --- a/classes/table/archived_skills.php +++ b/classes/table/archived_skills.php @@ -75,6 +75,7 @@ public function query_db($pagesize, $useinitialsbar = true) { global $DB; $condition = 'archived = 1'; + // Filter the category. if ($this->filterset->has_filter('category')) { $values = $this->filterset->get_filter('category')->get_filter_values(); @@ -90,23 +91,23 @@ public function query_db($pagesize, $useinitialsbar = true) { } /** - * Name of the skill column. Format the string to support multilingual. + * Description of the skill. * * @param stdClass $row * @return string */ - public function col_name(stdClass $row) : string { - return format_string($row->name); + public function col_description(stdClass $row) : string { + return format_text($row->description, FORMAT_HTML, ['overflow' => false]); } /** - * Description of the skill. + * Name of the skill column. Format the string to support multilingual. * * @param stdClass $row * @return string */ - public function col_description(stdClass $row) : string { - return format_text($row->description, FORMAT_HTML, ['overflow' => false]); + public function col_name(stdClass $row) : string { + return format_string($row->name); } /** @@ -165,13 +166,13 @@ public function col_actions(stdClass $row) : string { // Base url to edit the skills. $baseurl = new \moodle_url('/admin/tool/skills/manage/edit.php', [ 'id' => $row->id, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); // Skills List URL. $listurl = new \moodle_url('/admin/tool/skills/manage/list.php', [ 'id' => $row->id, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); $actions = []; @@ -180,16 +181,16 @@ public function col_actions(stdClass $row) : string { $actions[] = [ 'url' => new \moodle_url($listurl, ['action' => 'delete', 't' => 'archive']), 'icon' => new \pix_icon('t/delete', \get_string('delete')), - 'attributes' => array('class' => 'action-delete'), - 'action' => new \confirm_action(get_string('deleteskill', 'tool_skills')) + 'attributes' => ['class' => 'action-delete'], + 'action' => new \confirm_action(get_string('deleteskill', 'tool_skills')), ]; // Unarchive the skills. $actions[] = [ 'url' => new \moodle_url($listurl, ['action' => 'active']), 'icon' => new \pix_icon('f/active', \get_string('active', 'tool_skills'), 'tool_skills'), - 'attributes' => array('class' => 'action-active'), - 'action' => new \confirm_action(get_string('activeskillwarning', 'tool_skills')) + 'attributes' => ['class' => 'action-active'], + 'action' => new \confirm_action(get_string('activeskillwarning', 'tool_skills')), ]; $actionshtml = []; @@ -203,7 +204,7 @@ public function col_actions(stdClass $row) : string { $action['url'], $action['icon'], ($action['action'] ?? null), - $action['attributes'] + $action['attributes'], ); } return \html_writer::div(join('', $actionshtml), 'skill-item-actions item-actions mr-0'); diff --git a/classes/table/course_skills_table.php b/classes/table/course_skills_table.php index 399fb11..7c38331 100644 --- a/classes/table/course_skills_table.php +++ b/classes/table/course_skills_table.php @@ -176,13 +176,13 @@ public function col_actions(stdClass $row) : string { $baseurl = new \moodle_url('/admin/tool/skills/manage/editcourse.php', [ 'skill' => $row->id, 'courseid' => $row->courseid ?: $this->courseid, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); // Skills List URL. $listurl = new \moodle_url('/admin/tool/skills/manage/courselist.php', [ 'courseid' => $row->courseid ?: $this->courseid, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); $actions = []; @@ -191,7 +191,7 @@ public function col_actions(stdClass $row) : string { $actions[] = [ 'url' => $baseurl, 'icon' => new \pix_icon('t/edit', \get_string('edit')), - 'attributes' => array('class' => 'action-edit', 'data-target' => "toolskill-edit", "data-skillid" => $row->id) + 'attributes' => ['class' => 'action-edit', 'data-target' => "toolskill-edit", "data-skillid" => $row->id], ]; // Show/Hide. @@ -204,8 +204,9 @@ public function col_actions(stdClass $row) : string { ); // Skills status switch. - $statusurl = new \moodle_url($listurl, array('t' => 'archive', 'skill' => $row->id, - 'action' => ($row->coursestatus) ? 'disable' : 'enable')); + $statusurl = new \moodle_url($listurl, ['t' => 'archive', 'skill' => $row->id, + 'action' => ($row->coursestatus) ? 'disable' : 'enable', + ]); $statusclass = ' toolskills-status-switch '; $statusclass .= $row->coursestatus ? 'action-hide' : 'action-show'; $actions[] = html_writer::link($statusurl->out(false), $checkbox, ['class' => $statusclass]); @@ -221,7 +222,7 @@ public function col_actions(stdClass $row) : string { $action['url'], $action['icon'], ($action['action'] ?? null), - $action['attributes'] + $action['attributes'], ); } return html_writer::div(join('', $actionshtml), 'skill-course-actions skill-actions mr-0'); @@ -238,13 +239,13 @@ public function edit_switch($row) { $temp = (object) [ 'legacyseturl' => (new moodle_url('/admin/tool/skills/manage/courselist.php', [ 'id' => $row->id, - 'sesskey' => sesskey() + 'sesskey' => sesskey(), ]))->out(false), 'pagecontextid' => $PAGE->context->id, 'pageurl' => $PAGE->url, 'sesskey' => sesskey(), 'checked' => $row->coursestatus, - 'id' => $row->skillcourseid + 'id' => $row->skillcourseid, ]; return $OUTPUT->render_from_template('tool_skills/status_switch', $temp); } diff --git a/classes/table/skills_table.php b/classes/table/skills_table.php index cf4d9f1..89865d9 100644 --- a/classes/table/skills_table.php +++ b/classes/table/skills_table.php @@ -158,13 +158,13 @@ public function col_actions(stdClass $row) : string { // Base url to edit the skills. $baseurl = new \moodle_url('/admin/tool/skills/manage/edit.php', [ 'id' => $row->id, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); // Skills List URL. $listurl = new \moodle_url('/admin/tool/skills/manage/list.php', [ 'id' => $row->id, - 'sesskey' => \sesskey() + 'sesskey' => \sesskey(), ]); $actions = []; @@ -173,7 +173,7 @@ public function col_actions(stdClass $row) : string { $actions[] = [ 'url' => $baseurl, 'icon' => new \pix_icon('t/edit', \get_string('edit')), - 'attributes' => array('class' => 'action-edit') + 'attributes' => ['class' => 'action-edit'], ]; // Show/Hide. @@ -184,7 +184,7 @@ public function col_actions(stdClass $row) : string { ) . html_writer::tag('span', '', ['class' => 'custom-control-label']), 'custom-control custom-switch' ); - $statusurl = new \moodle_url($listurl, array('action' => ($row->status) ? 'disable' : 'enable')); + $statusurl = new \moodle_url($listurl, ['action' => ($row->status) ? 'disable' : 'enable']); $statusclass = ' toolskills-status-switch '; $statusclass .= $row->status ? 'action-hide' : 'action-show'; $actions[] = html_writer::link($statusurl->out(false), $checkbox, ['class' => $statusclass]); @@ -193,8 +193,8 @@ public function col_actions(stdClass $row) : string { $actions[] = [ 'url' => new \moodle_url($listurl, ['t' => 'archive', 'action' => 'archive']), 'icon' => new \pix_icon('f/archive', \get_string('archive', 'tool_skills'), 'tool_skills'), - 'attributes' => array('class' => 'action-archive'), - 'action' => new \confirm_action(get_string('archiveskill', 'tool_skills')) + 'attributes' => ['class' => 'action-archive'], + 'action' => new \confirm_action(get_string('archiveskill', 'tool_skills')), ]; $actionshtml = []; @@ -208,7 +208,7 @@ public function col_actions(stdClass $row) : string { $action['url'], $action['icon'], ($action['action'] ?? null), - $action['attributes'] + $action['attributes'], ); } return html_writer::div(join('', $actionshtml), 'skill-item-actions item-actions mr-0'); @@ -225,13 +225,13 @@ public function edit_switch($row) { $temp = (object) [ 'legacyseturl' => (new moodle_url('/admin/tool/skills/manage/list.php', [ 'id' => $row->id, - 'sesskey' => sesskey() + 'sesskey' => sesskey(), ]))->out(false), 'pagecontextid' => $PAGE->context->id, 'pageurl' => $PAGE->url, 'sesskey' => sesskey(), 'checked' => $row->status, - 'id' => $row->id + 'id' => $row->id, ]; return $OUTPUT->render_from_template('tool_skills/status_switch', $temp); } diff --git a/classes/table/users_skills.php b/classes/table/users_skills.php index 8fa9d59..c75eb7a 100644 --- a/classes/table/users_skills.php +++ b/classes/table/users_skills.php @@ -126,6 +126,6 @@ public function query_db($pagesize, $useinitialsbar = true) { public function col_fullname($data) { global $OUTPUT; - return $OUTPUT->user_picture($data, array('size' => 35, 'includefullname' => true)); + return $OUTPUT->user_picture($data, ['size' => 35, 'includefullname' => true]); } } diff --git a/classes/user.php b/classes/user.php index 36a1535..8eb4522 100644 --- a/classes/user.php +++ b/classes/user.php @@ -98,7 +98,7 @@ public function get_userid() { * @return array */ public function get_user_skills() { - global $DB; + global $DB, $CFG; // Fetch the list of user enrolled courses. $courses = enrol_get_users_courses($this->userid, true, 'id'); @@ -116,7 +116,7 @@ public function get_user_skills() { FROM {tool_skills_courses} tscs JOIN {tool_skills} ts ON ts.id = tscs.skill LEFT JOIN {tool_skills_userpoints} up ON up.skill = ts.id AND up.userid = :userid - WHERE tscs.status = :enabled AND tscs.courseid $insql"; + WHERE tscs.status = :enabled AND ts.status = 1 AND ts.archived = 0 AND tscs.courseid $insql"; $list = $DB->get_records_sql($sql, ['userid' => $this->userid, 'enabled' => 1] + $inparams); @@ -129,6 +129,9 @@ public function get_user_skills() { $point->skillcourse = courseskills::get($point->courseid); $point->skillcourse->set_skill_instance($point->id); + // Extend addons to inlcude its skill data. + \tool_skills\helper::extend_addons_add_userskills_data($point); + $point->userpoints = $DB->get_record('tool_skills_userpoints', ['skill' => $point->skill, 'userid' => $this->userid]); // Skill levels. $point->levels = $DB->get_records('tool_skills_levels', ['skill' => $point->skill]); @@ -194,5 +197,37 @@ public function get_user_award_by_method(string $method, int $methodid) { return null; } + /** + * Get user proficiency level. + * + * @param int $skillid + * @param int $points + * @return string + */ + public function get_user_proficency_level(int $skillid, int $points) { + + $skill = skills::get($skillid); + $levels = $skill->get_levels(); + foreach ($levels as $level) { + if ($points >= $level->points) { + $proficiencylevel = $level->name; + } + } + + return $proficiencylevel ?? ''; + } + + /** + * Get the user percentage in the skill. + * + * @param int $skillid Skill ID + * @param int $points + * @return string + */ + public function get_user_percentage(int $skillid, $points) { + $skillpoint = skills::get($skillid)->get_points_to_earnskill(); + $percentage = ($points / $skillpoint) * 100; + return ((int) $percentage) . '%'; + } } diff --git a/db/access.php b/db/access.php index 5fe697d..3995a4d 100644 --- a/db/access.php +++ b/db/access.php @@ -24,35 +24,36 @@ defined('MOODLE_INTERNAL') || die(); -$capabilities = array( +$capabilities = [ // Capability to manage skills. - 'tool/skills:manage' => array( + 'tool/skills:manage' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'riskbitmask' => RISK_XSS | RISK_CONFIG, - 'archetypes' => array( - 'manager' => CAP_ALLOW - ), - ), + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ], + ], - 'tool/skills:managecourseskills' => array( + 'tool/skills:managecourseskillslist' => [ 'captype' => 'write', - 'contextlevel' => CONTEXT_SYSTEM, + 'contextlevel' => CONTEXT_COURSE, 'riskbitmask' => RISK_XSS | RISK_CONFIG, - 'archetypes' => array( - 'manager' => CAP_ALLOW, + 'archetypes' => [ + 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, - ), - ), + 'manager' => CAP_ALLOW, + ], + ], - 'tool/skills:viewotherspoints' => array( + 'tool/skills:viewotherspoints' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'riskbitmask' => RISK_XSS | RISK_CONFIG, - 'archetypes' => array( - 'manager' => CAP_ALLOW - ), - ), + 'archetypes' => [ + 'manager' => CAP_ALLOW, + ], + ], -); +]; diff --git a/db/events.php b/db/events.php index 7b4d07e..776ef2b 100644 --- a/db/events.php +++ b/db/events.php @@ -26,19 +26,18 @@ $observers = [ - array( + [ 'eventname' => 'core\event\course_completed', 'callback' => '\tool_skills\events\observer::course_completed', - ), + ], - array( + [ 'eventname' => 'core\event\course_deleted', 'callback' => '\tool_skills\events\observer::course_deleted', - ), + ], - array( + [ 'eventname' => 'core\event\user_deleted', 'callback' => '\tool_skills\events\observer::user_deleted', - ), - + ], ]; diff --git a/db/install.xml b/db/install.xml index 9fda7d9..32b3337 100644 --- a/db/install.xml +++ b/db/install.xml @@ -73,6 +73,7 @@ + diff --git a/db/subplugins.json b/db/subplugins.json new file mode 100644 index 0000000..c461248 --- /dev/null +++ b/db/subplugins.json @@ -0,0 +1,5 @@ +{ + "plugintypes": { + "skilladdon": "admin/tool/skills/addon" + } + } \ No newline at end of file diff --git a/db/subplugins.php b/db/subplugins.php new file mode 100644 index 0000000..103864e --- /dev/null +++ b/db/subplugins.php @@ -0,0 +1,29 @@ +. + +/** + * List of available subplugins used in tool skills. + * + * @package tool_skills + * @copyright 2023 bdecent GmbH + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$subplugins = [ + 'skilladdon' => 'admin/tool/skills/addon', +]; diff --git a/db/upgrade.php b/db/upgrade.php new file mode 100644 index 0000000..66237a2 --- /dev/null +++ b/db/upgrade.php @@ -0,0 +1,67 @@ +. + +/** + * DB authentication plugin upgrade code + * + * @package tool_skills + * @copyright 2023 bdecent GmbH + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +/** + * Function to upgrade tool_skills. + * @param int $oldversion the version we are upgrading from + * @return bool result + */ +function xmldb_tool_skills_upgrade($oldversion) { + global $CFG, $DB; + + $dbman = $DB->get_manager(); + + if ($oldversion < 2023102505) { + $table = new xmldb_table('tool_skills_awardlogs'); + $field = new xmldb_field('skill', XMLDB_TYPE_INTEGER, 18, null, null, null, null, 'id'); + // Conditionally launch add field timecreated. + if ($dbman->table_exists($table) && !$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + + } + upgrade_plugin_savepoint(true, 2023102505, 'tool', 'skills'); + } + + if ($oldversion < 2024019004) { + + $table = new xmldb_table('tool_skills_awardlogs'); + $field = new xmldb_field('skill', XMLDB_TYPE_INTEGER, 18, null, null, null, null, 'id'); + // Conditionally launch add field timecreated. + if ($dbman->table_exists($table) && $dbman->field_exists($table, $field)) { + + $logs = $DB->get_records('tool_skills_awardlogs', ['method' => 'course']); + $skills = $DB->get_records('tool_skills_courses', []); + + foreach ($logs as $log) { + if (isset($skills[$log->methodid])) { + $skill = $skills[$log->methodid]->skill; + $DB->update_record('tool_skills_awardlogs', ['id' => $log->id, 'skill' => $skill]); + } + } + } + upgrade_plugin_savepoint(true, 2024019004, 'tool', 'skills'); + } + + return true; +} diff --git a/form/element-colorpicker.php b/form/element-colorpicker.php index 5275a8b..408326c 100644 --- a/form/element-colorpicker.php +++ b/form/element-colorpicker.php @@ -58,7 +58,7 @@ public function __construct($elementname=null, $elementlabel=null, $attributes=n if (empty($class)) { $class = ''; } - $this->updateAttributes(array('class' => $class.' tool-skills-form-colour-picker ')); + $this->updateAttributes(['class' => $class.' tool-skills-form-colour-picker ']); } /** diff --git a/lang/en/tool_skills.php b/lang/en/tool_skills.php index cb04b81..807c08d 100644 --- a/lang/en/tool_skills.php +++ b/lang/en/tool_skills.php @@ -28,7 +28,7 @@ $string['skills'] = 'Skills'; // ...Skills capabilities. $string['skills:manage'] = 'Manage skills'; -$string['skills:managecourseskills'] = 'Manage course skills'; +$string['skills:managecourseskillslist'] = 'Manage course skills'; $string['skills:viewotherspoints'] = 'View others points'; // ...error strings. $string['error:skillsnotfound'] = 'Skill record not found for the given id'; @@ -108,6 +108,12 @@
  • Entering "50" will add 50 points.
  • Entering "-20" will deduct 20 points.
  • '; $string['completionlevel_help'] = 'Choose the desired skill level for this course. Upon completion, the student will receive the corresponding number of points required to achieve the selected level.'; +// ...Course module string. +$string['uponmodcompletion'] = 'Upon activity completion'; +$string['uponmodcompletion_help'] = '
    • Add Points: Upon course module completion, award the specified number of skill points. (Note: Entering negative numbers will result in a reduction of points.)
    • Add Points By Grade: Upon course module completion, adds as many points as the grade achieved in the activity.
    • +
    • Set Level: Upon course module completion, grant the points needed to reach the specified level, unless the student already has more points.
    • +
    • Force Level: Upon course module completion, adjust the points to match the amount required for the chosen level, regardless of the student\'s prior level/points. This may lead to students having fewer points than before.
    '; +$string['completionpointsgrade'] = 'Points by grade'; // ...Course skill table strings. $string['assignskills'] = 'Assign skills'; $string['assignskills_desc'] = 'Customize the skills associated with this course. Activate or deactivate specific skills to align with your teaching objectives. By default, all skills are disabled. Simply enable the ones that fit your course content and goals. '; @@ -121,7 +127,7 @@ $string['skillpoints'] = '{$a} - users points'; $string['skillsotherspoint_desc'] = 'This table displays the points earned by other users in this skill. It provides an overview of the achievements and progress of peers within the same skill category'; // ...Privacy API strings. -$string['userpoints'] = 'User points earned'; +$string['userpoints'] = 'User earned points'; $string['privacy:userpoint'] = 'User point'; $string['privacy:awardlogs'] = 'Points awarded'; @@ -139,3 +145,36 @@ $string['privacy:metadata:awardlogs'] = 'Metadata for logs recording user points awarded for each method'; $string['privacy:metadata:userpointsexplanation'] = 'The skills store the points users earned for the skill and log the method by which they earned it, either through course completion or activity completion.'; +// ...Reports source builder. +$string['formtab'] = 'Skills'; +$string['maximum'] = 'Maximum'; +$string['skillsrpeort'] = 'Skills'; +$string['skillstats'] = 'Skill statistics'; +$string['coursesused'] = 'Courses using the skill'; +$string['skillusers'] = 'Users that have any points for skill'; +$string['skillproficients'] = 'Users that are proficient in this skill'; +$string['userskillentity'] = 'User skill'; +$string['timemodified'] = 'Time modified'; +$string['activitiesentity'] = 'Activities'; +$string['modname'] = 'Mod name'; +$string['modcompletionstatus'] = 'Completion status'; + +$string['activitiestatsentity'] = 'Activity completion'; +$string['incomplete'] = 'In complete'; +$string['complete'] = 'Complete'; +$string['complete_pass'] = 'Passed'; +$string['complete_fail'] = 'Failed'; +$string['activityname'] = 'Activity name'; +$string['entitycategory'] = 'Category'; +$string['categoryname'] = 'Category name'; +$string['categoryidnumber'] = 'Category idnumber'; +$string['coursecount'] = 'Number of courses'; +$string['categoryvisiblity'] = 'Category visibility'; +$string['depth'] = 'Depth'; +$string['path'] = 'Category path'; +$string['conditionassignedusers'] = 'Relative role users'; +$string['conditionusercohort'] = 'Users in same cohort'; +$string['skillsdatasource'] = 'Skills'; +$string['userproficiency'] = 'Proficiency'; +$string['userpercentage'] = 'Percentage'; +$string['grade'] = 'Grade'; diff --git a/lib.php b/lib.php index 324ffe6..aad4f12 100644 --- a/lib.php +++ b/lib.php @@ -37,7 +37,7 @@ function tool_skills_extend_navigation_course(navigation_node $navigation, stdCl global $PAGE; $addnode = $context->contextlevel === CONTEXT_COURSE; - $addnode = $addnode && has_capability('tool/skills:managecourseskills', $context); + $addnode = $addnode && has_capability('tool/skills:managecourseskillslist', $context); if ($addnode) { $id = $context->instanceid; $url = new moodle_url('/admin/tool/skills/manage/courselist.php', [ @@ -52,7 +52,7 @@ function tool_skills_extend_navigation_course(navigation_node $navigation, stdCl if (empty($navigation->get_children_key_list())) { $navigation->add_node($node, null); } else { - $navigation->add_node($node, 'gradebooksetup'); + $navigation->add_node($node, 'coursereports'); } } } @@ -68,7 +68,7 @@ function tool_skills_extend_navigation_course(navigation_node $navigation, stdCl * @return bool */ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $course) { - global $USER; + global $USER, $DB; // Get the learningtools category. if (!array_key_exists('toolskills', $tree->__get('categories'))) { @@ -101,7 +101,6 @@ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $co } foreach ($newskills as $skillid => $skills) { - $skill = $skillslist[$skillid]; $skillpoints = $skill->get_points_to_earnskill(); @@ -135,16 +134,19 @@ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $co $li .= html_writer::tag('p', $coursepointstr, ['class' => 'skills-points-'.$course->shortname]); $skillstr .= html_writer::tag('li', $li); + + \tool_skills\helper::extend_addons_add_user_points_content($skillstr, $data); } $skillstr .= html_writer::end_tag('ul'); // End the skill list. - $report = new \moodle_url('/admin/tool/skills/manage/usersreport.php', ['id' => $skillid]); - $skillstr .= html_writer::link($report, get_string('usersreport', 'tool_skills')); + if (has_capability('tool/skills:viewotherspoints', $systemcontext)) { + $report = new \moodle_url('/admin/tool/skills/manage/usersreport.php', ['id' => $skillid]); + $skillstr .= html_writer::link($report, get_string('usersreport', 'tool_skills')); + } $coursenode = new core_user\output\myprofile\node('toolskills', "skill_".$skill->get_data()->id, - '', null, null, $skillstr, null, 'toolskill-courses-points'); - + '', null, null, $skillstr, null, 'toolskill-courses-points'); $tree->add_node($coursenode); } @@ -163,3 +165,40 @@ function tool_skills_get_fontawesome_icon_map() { 'tool_skills:f/active' => 'fa-undo', ]; } + + +/** + * File serving callback + * + * @param stdClass $course course object + * @param stdClass $cm course module object + * @param stdClass $context context object + * @param string $filearea file area + * @param array $args extra arguments + * @param bool $forcedownload whether or not force download + * @param array $options additional options affecting the file serving + * @return bool false if the file was not found, just send the file otherwise and do not return anything + */ +function tool_skills_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=array()) { + + if ($context->contextlevel != CONTEXT_SYSTEM) { + return false; + } + + require_login(); + + if ($filearea == 'levelimage') { + + $relativepath = implode('/', $args); + + $fullpath = "/$context->id/tool_skills/$filearea/$relativepath"; + + $fs = get_file_storage(); + $file = $fs->get_file_by_hash(sha1($fullpath)); + if (!$file || $file->is_directory()) { + return false; + } + + send_stored_file($file, null, 0, $forcedownload, $options); + } +} diff --git a/manage/courselist.php b/manage/courselist.php index a9641bc..e29cab4 100644 --- a/manage/courselist.php +++ b/manage/courselist.php @@ -48,7 +48,7 @@ // Login check required. require_login(); // Access checks. -require_capability('tool/skills:managecourseskills', $context); +require_capability('tool/skills:managecourseskillslist', $context); // Prepare the page (to make sure that all necessary information is already set even if we just handle the actions as a start). $PAGE->set_context($context); @@ -104,10 +104,11 @@ echo get_string('assignskills_desc', 'tool_skills'); // Create skills button to create new skill. +$createbutton = ''; if (has_capability('tool/skills:manage', \context_system::instance())) { - $createbutton = $OUTPUT->box_start(); + $createbutton .= $OUTPUT->box_start(); $createbutton .= $OUTPUT->single_button( - new \moodle_url('/admin/tool/skills/manage/edit.php', array('sesskey' => sesskey())), + new \moodle_url('/admin/tool/skills/manage/edit.php', ['sesskey' => sesskey()]), get_string('createskill', 'tool_skills'), 'get'); $createbutton .= $OUTPUT->box_end(); } diff --git a/manage/edit.php b/manage/edit.php index 2da5f12..eeb88e6 100644 --- a/manage/edit.php +++ b/manage/edit.php @@ -32,7 +32,7 @@ $id = optional_param('id', null, PARAM_INT); // Get system context. -$context = context_system::instance(); +$context = \context_system::instance(); // Access checks. require_login(); @@ -44,10 +44,10 @@ // Prepare the page. $PAGE->set_context($context); -$PAGE->set_url(new moodle_url('/admin/tool/skills/manage/edit.php', array('id' => $id, 'sesskey' => sesskey()))); +$PAGE->set_url(new moodle_url('/admin/tool/skills/manage/edit.php', ['id' => $id, 'sesskey' => sesskey()])); $PAGE->set_cacheable(false); -$PAGE->navbar->add(get_string('tools', 'admin'), new moodle_url('/admin/category.php', array('category' => 'tool'))); +$PAGE->navbar->add(get_string('tools', 'admin'), new moodle_url('/admin/category.php', ['category' => 'tool'])); $PAGE->navbar->add(get_string('pluginname', 'tool_skills'), $listurl); $PAGE->set_title(get_string('skills', 'tool_skills')); @@ -65,7 +65,7 @@ // Init form. -$form = new \tool_skills\form\skills_form(null, array('id' => $id)); +$form = new \tool_skills\form\skills_form(null, ['id' => $id]); // If the form was submitted. if ($data = $form->get_data()) { diff --git a/manage/list.php b/manage/list.php index eb9f0e5..bee641d 100644 --- a/manage/list.php +++ b/manage/list.php @@ -56,7 +56,7 @@ $PAGE->set_heading(get_string('skillslisthead', 'tool_skills')); // Setup the breadcrumb. -$PAGE->navbar->add(get_string('tools', 'admin'), new moodle_url('/admin/category.php', array('category' => 'tool'))); +$PAGE->navbar->add(get_string('tools', 'admin'), new moodle_url('/admin/category.php', ['category' => 'tool'])); $PAGE->navbar->add(get_string('pluginname', 'tool_skills'), $pageurl); // Process actions. diff --git a/styles.css b/styles.css index 5a6cfb6..0e1bd5f 100644 --- a/styles.css +++ b/styles.css @@ -50,3 +50,6 @@ body.behat-site .toolskills-status-switch input.custom-control-input { #page-user-profile li.contentnode.toolskill-courses-points { padding-top: 20px; } +body.module-skills .activity-header { + display: none; +} diff --git a/tests/behat/behat_tool_skills.php b/tests/behat/behat_tool_skills.php index 2635a4f..5966e94 100644 --- a/tests/behat/behat_tool_skills.php +++ b/tests/behat/behat_tool_skills.php @@ -75,4 +75,35 @@ public function i_create_skill_with_the_following_fields_to_these_values(TableNo $this->execute("behat_general::i_click_on", ["Save changes", "button"]); } + /** + * This can be used on confirmation message css element. + * + * @Given /^I navigate to confirmation$/ + * + * @throws ExpectationException + * @return void + */ + public function i_navigate_to_confirmation() { + global $CFG; + + $cssclass = ($CFG->branch <= "402") ? '.confirmation-dialogue' : '.modal-footer'; + $this->execute("behat_general::i_click_on", ["Yes", "button", $cssclass, "css_element"]); + } + + /** + * Confirmation messages text. + * + * @Given /^I should see "(?P(?:[^"]|\\")*)" message confirmation$/ + * @param string $messagetext Messagetext. + */ + public function i_should_see_message_confirmation($messagetext) { + global $CFG; + + $cssclass = ($CFG->branch <= "402") ? '.confirmation-dialogue' : '.modal-body'; + $this->execute("behat_general::assert_element_contains_text", [ + "Are you sure! do you want to $messagetext this skill and its levels", + $cssclass, "css_element", + ]); + } + } diff --git a/tests/behat/tool_skills_general.feature b/tests/behat/tool_skills_general.feature index 1dfd709..ba0040d 100644 --- a/tests/behat/tool_skills_general.feature +++ b/tests/behat/tool_skills_general.feature @@ -21,50 +21,50 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I should see "Skills are not created yet or not in use" And I click on "Create skill" "button" And I set the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | And I click on "Save changes" "button" - And I should see "Begineer" in the "tool_skills_list" "table" + And I should see "Beginner" in the "tool_skills_list" "table" @javascript Scenario: Change the skills visibility When I log in as "admin" And I create skill with the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | - And ".skill-item-actions .custom-control-input:checked" "css_element" should exist in the "begineer" "table_row" + And ".skill-item-actions .custom-control-input:checked" "css_element" should exist in the "beginner" "table_row" Then I am on "Course 1" course homepage And I click on "More" "link" in the ".secondary-navigation" "css_element" And I click on "Manage skills" "link" - Then I should see "Begineer" + Then I should see "Beginner" And I navigate to skills - And I click on ".skill-item-actions .toolskills-status-switch" "css_element" in the "begineer" "table_row" - And ".skill-item-actions .toolskills-status-switch.action-show" "css_element" should exist in the "begineer" "table_row" + And I click on ".skill-item-actions .toolskills-status-switch" "css_element" in the "beginner" "table_row" + And ".skill-item-actions .toolskills-status-switch.action-show" "css_element" should exist in the "beginner" "table_row" Then I am on "Course 1" course homepage And I click on "More" "link" in the ".secondary-navigation" "css_element" And I click on "Manage skills" "link" - Then I should not see "Begineer" + Then I should not see "Beginner" @javascript Scenario: Update the existing skills and levels When I log in as "admin" And I create skill with the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | - Then I should see "Begineer" - And ".skill-item-actions .action-edit" "css_element" should exist in the "begineer" "table_row" - And I click on ".skill-item-actions .action-edit" "css_element" in the "begineer" "table_row" + Then I should see "Beginner" + And ".skill-item-actions .action-edit" "css_element" should exist in the "beginner" "table_row" + And I click on ".skill-item-actions .action-edit" "css_element" in the "beginner" "table_row" And I set the following fields to these values: | Skill name | Critical thinker | And I click on "Save changes" "button" - Then I should not see "Begineer" in the "begineer" "table_row" - And I should see "Critical thinker" in the "begineer" "table_row" + Then I should not see "Beginner" in the "beginner" "table_row" + And I should see "Critical thinker" in the "beginner" "table_row" @javascript Scenario: Archive and unarchive the skills @@ -72,44 +72,44 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I navigate to skills And I click on "Create skill" "button" And I set the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | And I click on "Save changes" "button" - And I should see "Begineer" in the "tool_skills_list" "table" - And I click on ".skill-item-actions .action-archive" "css_element" in the "begineer" "table_row" - And I should see "Are you sure! do you want to archive this skill and its levels" in the ".confirmation-dialogue" "css_element" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" - And I should see "Begineer" in the "tool_skills_archived_list" "table" + And I should see "Beginner" in the "tool_skills_list" "table" + And I click on ".skill-item-actions .action-archive" "css_element" in the "beginner" "table_row" + And I should see "archive" message confirmation + And I navigate to confirmation + And I should see "Beginner" in the "tool_skills_archived_list" "table" And I click on "Active skills" "link" Then I should see "Skills are not created yet or not in use" And I click on "Archived skills" "link" - And I click on ".skill-item-actions .action-active" "css_element" in the "begineer" "table_row" - And I should see "Are you sure! do you want to activate this skill and its levels" in the ".confirmation-dialogue" "css_element" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" - And I should see "Begineer" in the "tool_skills_list" "table" + And I click on ".skill-item-actions .action-active" "css_element" in the "beginner" "table_row" + And I should see "activate" message confirmation + And I navigate to confirmation + And I should see "Beginner" in the "tool_skills_list" "table" @javascript Scenario: Delete skills and its levels When I log in as "admin" And I create skill with the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | - And I should see "Begineer" in the "tool_skills_list" "table" - And I click on ".skill-item-actions .action-archive" "css_element" in the "begineer" "table_row" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" + And I should see "Beginner" in the "tool_skills_list" "table" + And I click on ".skill-item-actions .action-archive" "css_element" in the "beginner" "table_row" + And I navigate to confirmation And I click on "Archived skills" "link" - And I click on ".skill-item-actions .action-delete" "css_element" in the "begineer" "table_row" - And I should see "Are you sure! do you want to delete this skill and its levels" in the ".confirmation-dialogue" "css_element" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" - Then I should not see "Begineer" in the "#region-main" "css_element" + And I click on ".skill-item-actions .action-delete" "css_element" in the "beginner" "table_row" + And I should see "delete" message confirmation + And I navigate to confirmation + Then I should not see "Beginner" in the "#region-main" "css_element" And I create skill with the following fields to these values: | Skill name | Critical thinker | | Key | critical-thinker | - | Level #0 name | begineer | + | Level #0 name | beginner | | Level #0 point | 20 | Then I am on "Course 1" course homepage And I click on "More" "link" in the ".secondary-navigation" "css_element" @@ -117,11 +117,11 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe Then I should see "Critical thinker" And I navigate to skills And I click on ".skill-item-actions .action-archive" "css_element" in the "critical-thinker" "table_row" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" + And I navigate to confirmation And I click on "Archived skills" "link" And I click on ".skill-item-actions .action-delete" "css_element" in the "critical-thinker" "table_row" - And I should see "Are you sure! do you want to delete this skill and its levels" in the ".confirmation-dialogue" "css_element" - And I click on "Yes" "button" in the ".confirmation-dialogue" "css_element" + And I should see "delete" message confirmation + And I navigate to confirmation Then I am on "Course 1" course homepage And I click on "More" "link" in the ".secondary-navigation" "css_element" And I click on "Manage skills" "link" @@ -131,12 +131,12 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe Scenario: Create multiple levels When I log in as "admin" And I create skill with the following fields to these values: - | Skill name | Begineer | - | Key | begineer | - | Level #0 name | begineer | + | Skill name | Beginner | + | Key | beginner | + | Level #0 name | beginner | | Level #0 point | 10 | - Then I should see "Begineer" - And ".skill-item-actions .action-edit" "css_element" should exist in the "begineer" "table_row" + Then I should see "Beginner" + And ".skill-item-actions .action-edit" "css_element" should exist in the "beginner" "table_row" And I click on ".skill-item-actions .action-edit" "css_element" And I set the field "Number of levels" to "3" And I set the following fields to these values: @@ -145,9 +145,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe | Level #3 name | Level 3 | And I press "Save changes" Then I am on "Course 1" course homepage - And I click on "More" "link" in the ".secondary-navigation" "css_element" - And I click on "Manage skills" "link" - And I should see "Begineer" + And I navigate to "Manage skills" in current page administration + And I should see "Beginner" And I click on ".skill-course-actions .action-edit" "css_element" And I should see "Set course skills" in the ".modal-header" "css_element" And I set the field "Status" to "Enabled" diff --git a/tests/behat/tool_skills_managelevels.feature b/tests/behat/tool_skills_managelevels.feature index 8bbe352..87e4aca 100644 --- a/tests/behat/tool_skills_managelevels.feature +++ b/tests/behat/tool_skills_managelevels.feature @@ -39,10 +39,10 @@ Feature: Allocate points to users, need to manage levels and assign skills to co And I set the field "Test page1" to "1" And I press "Save changes" And I create skill with the following fields to these values: - | Skill name | Begineer | - | Key | begineer | + | Skill name | Beginner | + | Key | beginner | | Number of levels | 2 | - | Level #0 name | begineer | + | Level #0 name | beginner | | Level #0 point | 10 | | Level #1 name | Level 1 | | Level #1 point | 20 | @@ -58,11 +58,10 @@ Feature: Allocate points to users, need to manage levels and assign skills to co | Upon course completion | Points | | Points | 200 | And I press "Save changes" - Then I should see "Points - 200" in the "begineer" "table_row" + Then I should see "Points - 200" in the "beginner" "table_row" And I log out And I am on the "Course 1" course page logged in as student1 And I am on the "student1" "user > profile" page - Then I should see "Skills earned" And I should see "Earned: 0" And I am on "Course 1" course homepage And I press "Mark as done" @@ -79,11 +78,10 @@ Feature: Allocate points to users, need to manage levels and assign skills to co | Upon course completion | Set level | | Level | Level 1 | And I press "Save changes" - Then I should see "Set level - Level 1" in the "begineer" "table_row" + Then I should see "Set level - Level 1" in the "beginner" "table_row" And I log out And I am on the "Course 1" course page logged in as student1 And I am on the "student1" "user > profile" page - Then I should see "Skills earned" And I should see "Earned: 0" And I am on "Course 1" course homepage And I press "Mark as done" @@ -113,7 +111,7 @@ Feature: Allocate points to users, need to manage levels and assign skills to co | Upon course completion | Force level | | Level | Level 2 | And I press "Save changes" - Then I should see "Force level - Level 2" in the "begineer" "table_row" + Then I should see "Force level - Level 2" in the "beginner" "table_row" And I log out And I am on the "Course 1" course page logged in as student1 And I press "Mark as done" @@ -147,7 +145,7 @@ Feature: Allocate points to users, need to manage levels and assign skills to co | Upon course completion | Force level | | Level | Level 2 | And I press "Save changes" - Then I should see "Force level - Level 2" in the "begineer" "table_row" + Then I should see "Force level - Level 2" in the "beginner" "table_row" And I log out And I am on the "Course 1" course page logged in as student1 And I press "Mark as done" @@ -171,4 +169,4 @@ Feature: Allocate points to users, need to manage levels and assign skills to co And I wait until "Done" "button" exists And I am on the "student1" "user > profile" page Then I should see "Earned: -50" in the ".skills-points-C2" "css_element" - And I should see "Earned: -20" in the ".skill-begineer" "css_element" + And I should see "Earned: -20" in the ".skill-beginner" "css_element" diff --git a/version.php b/version.php index 4c939b7..0e0e833 100644 --- a/version.php +++ b/version.php @@ -22,11 +22,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -defined('MOODLE_INTERNAL') || die('No direct access'); +defined('MOODLE_INTERNAL') || die; -$plugin->version = 2023102504; +$plugin->version = 2024019004; $plugin->requires = 2021051700; // Requires this Moodle version. $plugin->component = 'tool_skills'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE; -$plugin->release = '1.0'; -$plugin->supported = [400, 402]; +$plugin->release = '1.1'; +$plugin->supported = [400, 403];