diff --git a/.github/workflows/moodle-ci.yml b/.github/workflows/moodle-ci.yml index cd82f6f..ac7e874 100644 --- a/.github/workflows/moodle-ci.yml +++ b/.github/workflows/moodle-ci.yml @@ -15,8 +15,9 @@ jobs: ports: - 5432:5432 options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3 + mariadb: - image: mariadb:10.6.7 + image: mariadb:10 env: MYSQL_USER: 'root' MYSQL_ALLOW_EMPTY_PASSWORD: "true" @@ -45,7 +46,7 @@ jobs: steps: - name: Check out repository code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: path: plugin @@ -55,65 +56,68 @@ jobs: php-version: ${{ matrix.php }} extensions: ${{ matrix.extensions }} ini-values: max_input_vars=5000 + # If you are not using code coverage, keep "none". Otherwise, use "pcov" (Moodle 3.10 and up) or "xdebug". + # If you try to use code coverage with "none", it will fallback to phpdbg (which has known problems). coverage: none - name: Initialise moodle-plugin-ci run: | - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 + composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^4 echo $(cd ci/bin; pwd) >> $GITHUB_PATH echo $(cd ci/vendor/bin; pwd) >> $GITHUB_PATH sudo locale-gen en_AU.UTF-8 echo "NVM_DIR=$HOME/.nvm" >> $GITHUB_ENV - name: Install moodle-plugin-ci - run: | - moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 + run: moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 env: DB: ${{ matrix.database }} MOODLE_BRANCH: ${{ matrix.moodle-branch }} + # Uncomment this to run Behat tests using the Moodle App. + # MOODLE_APP: 'true' - name: PHP Lint - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci phplint - - name: PHP Copy/Paste Detector - continue-on-error: true # This step will show errors but will not fail - if: ${{ always() }} - run: moodle-plugin-ci phpcpd - - name: PHP Mess Detector continue-on-error: true # This step will show errors but will not fail - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci phpmd - name: Moodle Code Checker - if: ${{ always() }} - run: moodle-plugin-ci codechecker --max-warnings 0 + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpcs --max-warnings 0 - name: Moodle PHPDoc Checker - if: ${{ always() }} - run: moodle-plugin-ci phpdoc + if: ${{ !cancelled() }} + run: moodle-plugin-ci phpdoc --max-warnings 0 - name: Validating - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci validate - name: Check upgrade savepoints - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci savepoints - name: Mustache Lint - if: ${{ always() }} - run: moodle-plugin-ci mustache || true + if: ${{ !cancelled() }} + run: moodle-plugin-ci mustache - name: Grunt - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci grunt --max-lint-warnings 0 - name: PHPUnit tests - if: ${{ always() }} + if: ${{ !cancelled() }} run: moodle-plugin-ci phpunit --fail-on-warning - name: Behat features - if: ${{ always() }} - run: moodle-plugin-ci behat --profile chrome --auto-rerun 0 + id: behat + if: ${{ !cancelled() }} + run: moodle-plugin-ci behat --profile chrome + + - name: Mark cancelled jobs as failed. + if: ${{ cancelled() }} + run: exit 1 diff --git a/README.md b/README.md index 0f868c7..fca04d6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ See http://docs.moodle.org/en/Installing_plugins for details on installing Moodl # Quick start guide -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. +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: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. diff --git a/amd/build/skills.min.js b/amd/build/skills.min.js index 3e04f33..e0191e9 100644 --- a/amd/build/skills.min.js +++ b/amd/build/skills.min.js @@ -1,3 +1,10 @@ +/** + * Tool skills - Manage skills, course skills. + * + * @module tool_skills/skills + * @copyright 2023 bdecent GmbH + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ define("tool_skills/skills",["jquery","core/modal_factory","core/str","core_form/modalform"],(function($,ModalFactory,Str,ModalForm){const SELECTORS={table:"#tool_skills_list",editskill:'[data-target="toolskill-edit"]',skillsRow:"#tool_skills_list .skill-actions a.action-edit"};class ToolSkillsCourses{constructor(skillID,courseID){this.SELECTORS=SELECTORS,this.skillCourseID="",this.skillID=skillID,this.courseID=courseID,this.SELECTORS.root='#tool_skills_list [data-skillid="'+this.skillID+'"]',this.addActionListiners()}getRoot(){return document.querySelector(this.SELECTORS.root)}showContentForm(){const modalForm=new ModalForm({formClass:"tool_skills\\form\\course_form",args:{courseid:this.courseID,skill:this.skillID},modalConfig:{title:Str.get_string("courseskills","tool_skills")}});modalForm.show(),modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(function(){window.location.reload()}))}addActionListiners(){var self=this;this.getRoot().addEventListener("click",(function(e){e.target.closest(SELECTORS.editskill)&&(e.preventDefault(),self.showContentForm())}))}static createInstances(courseID){let skills=document.querySelectorAll(SELECTORS.skillsRow);const skillsList=[];var skill;null!==skills&&skills.forEach((skl=>{var skillID=skl.dataset.skillid;skillID in skillsList?skill=skillsList[skillID]:(skill=new ToolSkillsCourses(skillID,courseID),skillsList[skillID]=skill)}))}static refresh(blockID){var block="#inst"+blockID;0==$(block).find("select:eq(1)").length&&$(block).find(".filter-form").append(''),$(block).find(".filter-form").find("select").trigger("change")}}return{init:function(courseID){ToolSkillsCourses.createInstances(courseID)}}})); //# sourceMappingURL=skills.min.js.map \ No newline at end of file diff --git a/amd/build/skills.min.js.map b/amd/build/skills.min.js.map index 1a506fc..e251351 100644 --- a/amd/build/skills.min.js.map +++ b/amd/build/skills.min.js.map @@ -1 +1 @@ -{"version":3,"file":"skills.min.js","sources":["../src/skills.js"],"sourcesContent":["define(['jquery', 'core/modal_factory', 'core/str', 'core_form/modalform'], function($, ModalFactory, Str, ModalForm) {\n\n const SELECTORS = {\n table: '#tool_skills_list',\n editskill: '[data-target=\"toolskill-edit\"]',\n skillsRow: '#tool_skills_list .skill-actions a.action-edit'\n };\n\n class ToolSkillsCourses {\n\n constructor(skillID, courseID) {\n\n this.SELECTORS = SELECTORS;\n this.skillCourseID = '';\n this.skillID = skillID;\n this.courseID = courseID;\n\n this.SELECTORS.root = '#tool_skills_list [data-skillid=\"' + this.skillID + '\"]';\n this.addActionListiners();\n }\n\n getRoot() {\n return document.querySelector(this.SELECTORS.root);\n }\n\n showContentForm() {\n\n var formClass = 'tool_skills\\\\form\\\\course_form';\n\n const modalForm = new ModalForm({\n\n formClass: formClass,\n // Add as many arguments as you need, they will be passed to the form:\n args: {courseid: this.courseID, skill: this.skillID},\n // Modal configurations, here set modal title.\n modalConfig: {title: Str.get_string('courseskills', 'tool_skills')},\n });\n\n modalForm.show();\n\n // Listen to events if you want to execute something on form submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, function() {\n window.location.reload();\n });\n }\n\n\n addActionListiners() {\n\n var self = this;\n\n this.getRoot().addEventListener('click', function(e) {\n\n if (e.target.closest(SELECTORS.editskill)) {\n e.preventDefault();\n self.showContentForm();\n }\n });\n }\n\n /**\n * Add event listenrs.\n *\n * @param {Integer} courseID\n */\n static createInstances(courseID) {\n\n let skills = document.querySelectorAll(SELECTORS.skillsRow);\n\n const skillsList = [];\n\n if (skills !== null) {\n\n var skill;\n skills.forEach((skl) => {\n var skillID = skl.dataset.skillid;\n if (skillID in skillsList) {\n skill = skillsList[skillID];\n } else {\n skill = new ToolSkillsCourses(skillID, courseID);\n skillsList[skillID] = skill;\n }\n });\n }\n }\n\n /**\n * Trigger the filter form to submit. to refresh the course content.\n *\n * @param {int} blockID\n */\n static refresh(blockID) {\n // Quick fix. TODO: Need to implement the method in Dashinstance.js to referesh the content from anywhere.\n var block = '#inst' + blockID;\n if ($(block).find('select:eq(1)').length == 0) {\n $(block).find('.filter-form').append('');\n }\n\n $(block).find('.filter-form').find('select').trigger('change');\n }\n\n }\n\n return {\n\n init: function(courseID) {\n ToolSkillsCourses.createInstances(courseID);\n }\n };\n});\n"],"names":["define","$","ModalFactory","Str","ModalForm","SELECTORS","table","editskill","skillsRow","ToolSkillsCourses","constructor","skillID","courseID","skillCourseID","root","this","addActionListiners","getRoot","document","querySelector","showContentForm","modalForm","formClass","args","courseid","skill","modalConfig","title","get_string","show","addEventListener","events","FORM_SUBMITTED","window","location","reload","self","e","target","closest","preventDefault","skills","querySelectorAll","skillsList","forEach","skl","dataset","skillid","blockID","block","find","length","append","trigger","init","createInstances"],"mappings":"AAAAA,4BAAO,CAAC,SAAU,qBAAsB,WAAY,wBAAwB,SAASC,EAAGC,aAAcC,IAAKC,iBAEjGC,UAAY,CACdC,MAAO,oBACPC,UAAW,iCACXC,UAAW,wDAGTC,kBAEFC,YAAYC,QAASC,eAEZP,UAAYA,eACZQ,cAAgB,QAChBF,QAAUA,aACVC,SAAWA,cAEXP,UAAUS,KAAO,oCAAsCC,KAAKJ,QAAU,UACtEK,qBAGTC,iBACWC,SAASC,cAAcJ,KAAKV,UAAUS,MAGjDM,wBAIUC,UAAY,IAAIjB,UAAU,CAE5BkB,UAJY,iCAMZC,KAAM,CAACC,SAAUT,KAAKH,SAAUa,MAAOV,KAAKJ,SAE5Ce,YAAa,CAACC,MAAOxB,IAAIyB,WAAW,eAAgB,kBAGxDP,UAAUQ,OAGVR,UAAUS,iBAAiBT,UAAUU,OAAOC,gBAAgB,WACxDC,OAAOC,SAASC,YAKxBnB,yBAEQoB,KAAOrB,UAENE,UAAUa,iBAAiB,SAAS,SAASO,GAE1CA,EAAEC,OAAOC,QAAQlC,UAAUE,aAC3B8B,EAAEG,iBACFJ,KAAKhB,6CAUMR,cAEf6B,OAASvB,SAASwB,iBAAiBrC,UAAUG,iBAE3CmC,WAAa,OAIXlB,MAFO,OAAXgB,QAGAA,OAAOG,SAASC,UACRlC,QAAUkC,IAAIC,QAAQC,QACtBpC,WAAWgC,WACXlB,MAAQkB,WAAWhC,UAEnBc,MAAQ,IAAIhB,kBAAkBE,QAASC,UACvC+B,WAAWhC,SAAWc,yBAWvBuB,aAERC,MAAQ,QAAUD,QACsB,GAAxC/C,EAAEgD,OAAOC,KAAK,gBAAgBC,QAC9BlD,EAAEgD,OAAOC,KAAK,gBAAgBE,OAAO,6DAGzCnD,EAAEgD,OAAOC,KAAK,gBAAgBA,KAAK,UAAUG,QAAQ,iBAKrD,CAEHC,KAAM,SAAS1C,UACXH,kBAAkB8C,gBAAgB3C"} \ No newline at end of file +{"version":3,"file":"skills.min.js","sources":["../src/skills.js"],"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 * Tool skills - Manage skills, course skills.\n *\n * @module tool_skills/skills\n * @copyright 2023 bdecent GmbH \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/modal_factory', 'core/str', 'core_form/modalform'], function($, ModalFactory, Str, ModalForm) {\n\n const SELECTORS = {\n table: '#tool_skills_list',\n editskill: '[data-target=\"toolskill-edit\"]',\n skillsRow: '#tool_skills_list .skill-actions a.action-edit'\n };\n\n class ToolSkillsCourses {\n\n constructor(skillID, courseID) {\n\n this.SELECTORS = SELECTORS;\n this.skillCourseID = '';\n this.skillID = skillID;\n this.courseID = courseID;\n\n this.SELECTORS.root = '#tool_skills_list [data-skillid=\"' + this.skillID + '\"]';\n this.addActionListiners();\n }\n\n getRoot() {\n return document.querySelector(this.SELECTORS.root);\n }\n\n showContentForm() {\n\n var formClass = 'tool_skills\\\\form\\\\course_form';\n\n const modalForm = new ModalForm({\n\n formClass: formClass,\n // Add as many arguments as you need, they will be passed to the form:\n args: {courseid: this.courseID, skill: this.skillID},\n // Modal configurations, here set modal title.\n modalConfig: {title: Str.get_string('courseskills', 'tool_skills')},\n });\n\n modalForm.show();\n\n // Listen to events if you want to execute something on form submit.\n modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, function() {\n window.location.reload();\n });\n }\n\n\n addActionListiners() {\n\n var self = this;\n\n this.getRoot().addEventListener('click', function(e) {\n\n if (e.target.closest(SELECTORS.editskill)) {\n e.preventDefault();\n self.showContentForm();\n }\n });\n }\n\n /**\n * Add event listenrs.\n *\n * @param {Integer} courseID\n */\n static createInstances(courseID) {\n\n let skills = document.querySelectorAll(SELECTORS.skillsRow);\n\n const skillsList = [];\n\n if (skills !== null) {\n\n var skill;\n skills.forEach((skl) => {\n var skillID = skl.dataset.skillid;\n if (skillID in skillsList) {\n skill = skillsList[skillID];\n } else {\n skill = new ToolSkillsCourses(skillID, courseID);\n skillsList[skillID] = skill;\n }\n });\n }\n }\n\n /**\n * Trigger the filter form to submit. to refresh the course content.\n *\n * @param {int} blockID\n */\n static refresh(blockID) {\n // Quick fix. TODO: Need to implement the method in Dashinstance.js to referesh the content from anywhere.\n var block = '#inst' + blockID;\n if ($(block).find('select:eq(1)').length == 0) {\n $(block).find('.filter-form').append('');\n }\n\n $(block).find('.filter-form').find('select').trigger('change');\n }\n\n }\n\n return {\n\n init: function(courseID) {\n ToolSkillsCourses.createInstances(courseID);\n }\n };\n});\n"],"names":["define","$","ModalFactory","Str","ModalForm","SELECTORS","table","editskill","skillsRow","ToolSkillsCourses","constructor","skillID","courseID","skillCourseID","root","this","addActionListiners","getRoot","document","querySelector","showContentForm","modalForm","formClass","args","courseid","skill","modalConfig","title","get_string","show","addEventListener","events","FORM_SUBMITTED","window","location","reload","self","e","target","closest","preventDefault","skills","querySelectorAll","skillsList","forEach","skl","dataset","skillid","blockID","block","find","length","append","trigger","init","createInstances"],"mappings":";;;;;;;AAuBAA,4BAAO,CAAC,SAAU,qBAAsB,WAAY,wBAAwB,SAASC,EAAGC,aAAcC,IAAKC,iBAEjGC,UAAY,CACdC,MAAO,oBACPC,UAAW,iCACXC,UAAW,wDAGTC,kBAEFC,YAAYC,QAASC,eAEZP,UAAYA,eACZQ,cAAgB,QAChBF,QAAUA,aACVC,SAAWA,cAEXP,UAAUS,KAAO,oCAAsCC,KAAKJ,QAAU,UACtEK,qBAGTC,iBACWC,SAASC,cAAcJ,KAAKV,UAAUS,MAGjDM,wBAIUC,UAAY,IAAIjB,UAAU,CAE5BkB,UAJY,iCAMZC,KAAM,CAACC,SAAUT,KAAKH,SAAUa,MAAOV,KAAKJ,SAE5Ce,YAAa,CAACC,MAAOxB,IAAIyB,WAAW,eAAgB,kBAGxDP,UAAUQ,OAGVR,UAAUS,iBAAiBT,UAAUU,OAAOC,gBAAgB,WACxDC,OAAOC,SAASC,YAKxBnB,yBAEQoB,KAAOrB,UAENE,UAAUa,iBAAiB,SAAS,SAASO,GAE1CA,EAAEC,OAAOC,QAAQlC,UAAUE,aAC3B8B,EAAEG,iBACFJ,KAAKhB,6CAUMR,cAEf6B,OAASvB,SAASwB,iBAAiBrC,UAAUG,iBAE3CmC,WAAa,OAIXlB,MAFO,OAAXgB,QAGAA,OAAOG,SAASC,UACRlC,QAAUkC,IAAIC,QAAQC,QACtBpC,WAAWgC,WACXlB,MAAQkB,WAAWhC,UAEnBc,MAAQ,IAAIhB,kBAAkBE,QAASC,UACvC+B,WAAWhC,SAAWc,yBAWvBuB,aAERC,MAAQ,QAAUD,QACsB,GAAxC/C,EAAEgD,OAAOC,KAAK,gBAAgBC,QAC9BlD,EAAEgD,OAAOC,KAAK,gBAAgBE,OAAO,6DAGzCnD,EAAEgD,OAAOC,KAAK,gBAAgBA,KAAK,UAAUG,QAAQ,iBAKrD,CAEHC,KAAM,SAAS1C,UACXH,kBAAkB8C,gBAAgB3C"} \ No newline at end of file diff --git a/amd/src/skills.js b/amd/src/skills.js index 1754b20..2aca0b1 100644 --- a/amd/src/skills.js +++ b/amd/src/skills.js @@ -1,3 +1,26 @@ +// 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 . + +/** + * Tool skills - Manage skills, course skills. + * + * @module tool_skills/skills + * @copyright 2023 bdecent GmbH + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + define(['jquery', 'core/modal_factory', 'core/str', 'core_form/modalform'], function($, ModalFactory, Str, ModalForm) { const SELECTORS = { diff --git a/classes/allocation_method.php b/classes/allocation_method.php index 2c7d88b..5603918 100644 --- a/classes/allocation_method.php +++ b/classes/allocation_method.php @@ -119,7 +119,7 @@ public function get_data() { * * @return \tool_skills\logs */ - public function get_logs() : \tool_skills\logs { + public function get_logs(): \tool_skills\logs { if ($this->logs == null) { $this->logs = new \tool_skills\logs(); diff --git a/classes/courseskills.php b/classes/courseskills.php index 0d938c9..586101e 100644 --- a/classes/courseskills.php +++ b/classes/courseskills.php @@ -69,7 +69,7 @@ protected function __construct(int $courseid) { * @param int $courseid * @return self */ - public static function get(int $courseid) : self { + public static function get(int $courseid): self { return new self($courseid); } @@ -79,7 +79,7 @@ public static function get(int $courseid) : self { * @param int $skillid * @return self */ - public static function get_for_skill(int $skillid) : array { + public static function get_for_skill(int $skillid): array { global $DB; $courses = $DB->get_records('tool_skills_courses', ['skill' => $skillid]); @@ -92,23 +92,47 @@ public static function get_for_skill(int $skillid) : array { * * @return stdClass Course record data. */ - public function get_course() : stdClass { + public function get_course(): stdClass { return get_course($this->courseid); } /** * Fetch the skills assigned/enabled for this course. * + * @param int $skillid Id of the skill. * @return array */ - public function get_instance_skills(): array { + public function get_instance_skills($skillid=null): array { global $DB; - $skills = $DB->get_records('tool_skills_courses', ['courseid' => $this->courseid, 'status' => 1]); + $condition = ['courseid' => $this->courseid, 'status' => 1]; + if ($skillid !== null) { + $condition['skill'] = $skillid; + } + + $skills = $DB->get_records('tool_skills_courses', $condition); return array_map(fn($sk) => skills::get($sk->skill), $skills); } + /** + * Fetch the skills assigned/enabled for this course. + * + * @param int $skillid Id of the skill. + * @return array + */ + public function get_instance_disabled_skills($skillid=null): array { + global $DB; + + $condition = ['courseid' => $this->courseid, 'status' => 0]; + if ($skillid !== null) { + $condition['skill'] = $skillid; + } + + $skills = $DB->get_records('tool_skills_courses', $condition); + + return array_map(fn($sk) => skills::get($sk->skill), $skills); + } /** * Remove the course skills records. @@ -197,9 +221,11 @@ public function get_user_earned_points(int $userid) { * Trigger the skills to update the user points based on the upon completion option for this skill added in courses. * * @param int $userid + * @param array $skills + * @param bool|null $status * @return void */ - public function manage_course_completions(int $userid) { + public function manage_course_completions(int $userid, array $skills=[], bool $status=null) { global $CFG, $DB; require_once($CFG->dirroot . '/lib/completionlib.php'); @@ -209,54 +235,156 @@ public function manage_course_completions(int $userid) { // User completes the course, allocate the points for the levels for the enabled skills. if ($coursecompletion) { // Get course skills records. - $skills = $this->get_instance_skills(); - foreach ($skills as $skillcourseid => $skill) { + if ($status === null || $status == 1) { + $skills = $skills ?: $this->get_instance_skills(); + foreach ($skills as $skillcourseid => $skill) { + $this->manage_user_skill_points($skill, $userid, $skillcourseid); + } + } + + // Disable user points award if the instance is disabled. + $disabledskills = $this->get_instance_disabled_skills(); + if ($status !== null && $status === 0 && $skills) { + $disabledskills = $skills; + } + + if (!empty($disabledskills)) { + foreach ($disabledskills as $skillcourseid => $skill) { + $this->disable_user_skill_points($skill, $userid, $skillcourseid); + } + } + } + } + + /** + * Manage the points award to the user for a skill. + * + * @param tool_skills\skills $skill + * @param int $userid + * @param int $skillcourseid + * + * @return void + */ + protected function manage_user_skill_points($skill, $userid, $skillcourseid) { + global $DB; - // Create a skill course record instance for this skill. - $this->set_skill_instance($skillcourseid); - // Get the data. - $csdata = $this->build_data(); + // Create a skill course record instance for this skill. + $this->set_skill_instance($skillcourseid); + // Get the data. + $csdata = $this->build_data(); + // Start the database transaction. + $transaction = $DB->start_delegated_transaction(); + $updateskill = true; // Update the course skills until it doesn't already awarded. - // Start the database transaction. - $transaction = $DB->start_delegated_transaction(); + if ($record = $DB->get_record('tool_skills_awardlogs', ['userid' => $userid, 'skill' => $csdata->skill, + 'methodid' => $csdata->id, 'method' => 'course', + ])) { - switch ($csdata->uponcompletion) { + // Course points user will earned upon the course completion. + $coursepoints = $this->get_points_earned_fromcourse(); + $currentpoints = $record->points; // Previous points user earned stored in the log. + $updateskill = false; // No need to update the points until the course skill is updated in its points. - case skills::COMPLETIONFORCELEVEL: - $skill->force_level($this, $csdata->level, $userid); - break; + if ($coursepoints != $currentpoints) { - case skills::COMPLETIONSETLEVEL: - $skill->moveto_level($this, $csdata->level, $userid); - break; + $updateskill = true; // Verified the course skill points updated, then update the user points. + $skillpoint = $skill->get_user_skill($userid)->points; + $skillpoint -= $currentpoints; // Remove the previously awarded course skill points. - case skills::COMPLETIONPOINTS: - $skill->increase_points($this, $csdata->points, $userid); - break; - } + // Update the skill point for the user. + $skill->set_userskill_points($userid, $skillpoint); + } + } + + if ($updateskill) { - // End the database transaction. - $transaction->allow_commit(); + 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; + + case skills::COMPLETIONNOTHING: + $skill->create_user_point_award($this, $userid, 0); + break; } } + // End the database transaction. + $transaction->allow_commit(); } + /** + * Remove the user earned points for this course from the skill points. + * + * @param tool_skills\skills $skill + * @param int $userid + * @param int $skillcourseid Course skill instance id (skill_courses table id). + * @return void + */ + protected function disable_user_skill_points($skill, $userid, $skillcourseid) { + global $DB; + + // Create a skill course record instance for this skill. + $this->set_skill_instance($skillcourseid); + // Get the data. + $csdata = $this->build_data(); + // Start the database transaction. + $transaction = $DB->start_delegated_transaction(); + + if ($record = $DB->get_record('tool_skills_awardlogs', ['userid' => $userid, 'skill' => $csdata->skill, + 'methodid' => $csdata->id, 'method' => 'course'])) { + + $currentpoints = $record->points; // Previous points user earned stored in the log. + $skillpoint = $skill->get_user_skill($userid)->points; + $skillpoint -= $currentpoints; // Remove the previously awarded course skill points. + // Update the skill point for the user. + $skill->set_userskill_points($userid, $skillpoint); + // Update the log of points earned from this instance to 0. + $skill->create_user_point_award($this, $userid, 0); + } + + // End the database transaction. + $transaction->allow_commit(); + + } /** - * Manage users completion. + * Manage the user completion actions, award or reduce the updated/completed courses result for the enrolled users. + * + * @param int $skillid + * @param bool $status * * @return void */ - public function manage_users_completion() { + public function manage_users_completion(int $skillid=null, bool $status=null) { global $CFG; require_once($CFG->dirroot . '/lib/enrollib.php'); $context = \context_course::instance($this->courseid); + $skills = []; + if ($skillid) { + $skills = $this->get_instance_skills($skillid); // Fetch the enabled skills. + } + + // If the given skill is not enabled, then fetch the disabled skills. + if ($skillid && empty($skills)) { + $skills = $this->get_instance_disabled_skills($skillid); + $status = 0; + } + // Enrolled users. $enrolledusers = get_enrolled_users($context); foreach ($enrolledusers as $user) { - $this->manage_course_completions($user->id); + $this->manage_course_completions($user->id, $skills, $status); } } diff --git a/classes/events/observer.php b/classes/events/observer.php index e9cfd17..81a4ad6 100644 --- a/classes/events/observer.php +++ b/classes/events/observer.php @@ -47,7 +47,7 @@ public static function course_completed(\core\event\course_completed $event) { $relateduserid = $data['relateduserid']; // Completed user id. // Manage the upon completion options for various skills assigned in this course. - courseskills::get($courseid)->manage_course_completions($relateduserid, $data); + courseskills::get($courseid)->manage_course_completions($relateduserid); } /** diff --git a/classes/form/course_form.php b/classes/form/course_form.php index 11eee9d..375f3c0 100644 --- a/classes/form/course_form.php +++ b/classes/form/course_form.php @@ -102,7 +102,7 @@ public function definition() { * @return bool */ protected function check_access_for_dynamic_submission(): void { - // TODO: Validatation of user capability goes here. + // ...TODO: Validatation of user capability goes here. } /** @@ -141,8 +141,8 @@ public function process_dynamic_submission() { // Insert the record of the new skill. $skillcourseid = $DB->insert_record('tool_skills_courses', $record); } - - courseskills::get($record->courseid)->manage_users_completion(); + // Increase or decrease the course points based on the updated course skill data. + courseskills::get($record->courseid)->manage_users_completion($record->skill, $record->status); return true; } @@ -190,7 +190,7 @@ protected function get_page_url_for_dynamic_submission(): moodle_url { * @param bool $status * @return void */ - public static function update_status(int $skillid, int $courseid, bool $status) : void { + public static function update_status(int $skillid, int $courseid, bool $status): void { global $DB; $record = new stdClass; @@ -204,6 +204,9 @@ public static function update_status(int $skillid, int $courseid, bool $status) $record->status = $status; $DB->insert_record('tool_skills_courses', $record); } + + // Manage the users completion data. + courseskills::get($record->courseid)->manage_users_completion($record->skill, $status); } } diff --git a/classes/form/skills_form.php b/classes/form/skills_form.php index 288fd8a..cbfc2f6 100644 --- a/classes/form/skills_form.php +++ b/classes/form/skills_form.php @@ -29,7 +29,6 @@ // Require forms library. require_once($CFG->libdir.'/formslib.php'); -use context_system; use html_writer; use tool_skills\skills; @@ -52,13 +51,6 @@ public function definition() { $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); - require_once($CFG->dirroot.'/admin/tool/skills/form/element-colorpicker.php'); - \MoodleQuickForm::registerElementType( - 'tool_skills_colorpicker', - $CFG->dirroot.'/admin/tool/skills/form/element-colorpicker.php', - 'moodlequickform_toolskills_colorpicker' - ); - // General section. $mform->addElement('header', 'general', get_string('general', 'core')); @@ -95,11 +87,6 @@ public function definition() { $mform->setDefault('learningtime', 90 * DAYSECS); $mform->addHelpButton('learningtime', 'learningtime', 'tool_skills'); - // Skill color element. - $mform->addElement('tool_skills_colorpicker', 'color', get_string('skillcolor', 'tool_skills')); - $mform->addHelpButton('color', 'skillcolor', 'tool_skills'); - $mform->setType('color', PARAM_TEXT); - // Add the Available in Course Categories element. $categories = \core_course_category::make_categories_list(); $cate = $mform->addElement('autocomplete', 'categories', get_string('availableincoursecategories', 'tool_skills'), @@ -126,7 +113,6 @@ public function definition() { ); } - /** * Definied the levels form fields to attach with form after the forms are defined, * Levels are created based on the number of levels. @@ -142,37 +128,32 @@ public function definition_after_data() { for ($i = 0; $i <= $levelscount; $i++) { // Static heading. - $mform->addElement('static', "level[$i]", html_writer::tag('h5', get_string('levelsnohead', 'tool_skills', $i))); + $name = ($i == 0) ? get_string('baselevelheading', 'tool_skills') : get_string('levelsnohead', 'tool_skills', $i); + $mform->addElement('static', "level[$i]", html_writer::tag('h5', $name)); $mform->addElement('hidden', "levels[$i][id]"); $mform->setType("levels[$i][id]", PARAM_INT); // Level name. - $mform->addElement('text', "levels[$i][name]", get_string('levelsname', 'tool_skills', $i), ''); + $name = ($i == 0) ? get_string('baselevelname', 'tool_skills') : get_string('levelsname', 'tool_skills', $i); + $mform->addElement('text', "levels[$i][name]", $name, ''); $mform->setType("levels[$i][name]", PARAM_TEXT); $mform->addRule("levels[$i][name]", get_string('required'), 'required', '', 'client'); - $mform->addHelpButton("levels[$i][name]", 'levelsname', 'tool_skills'); + $mform->addHelpButton("levels[$i][name]", (($i == 0) ? 'baselevelname' : 'levelsname'), 'tool_skills'); // Level points. - $mform->addElement('text', "levels[$i][points]", get_string('levelspoint', 'tool_skills', $i), ''); + $name = ($i == 0) ? get_string('baselevelpoint', 'tool_skills') : get_string('levelspoint', 'tool_skills', $i); + $mform->addElement('text', "levels[$i][points]", $name, ''); $mform->setType("levels[$i][points]", PARAM_INT); $mform->addRule("levels[$i][points]", get_string('required'), 'required', '', 'client'); $mform->addRule("levels[$i][points]", get_string('error:numeric', 'tool_skills'), 'numeric', '', 'client'); - $mform->addHelpButton("levels[$i][points]", 'levelspoint', 'tool_skills'); + $mform->addHelpButton("levels[$i][points]", (($i == 0) ? 'baselevelpoint' : 'levelspoint'), 'tool_skills'); // Set the default point for this level. if ($mform->getElementValue("levels[$i][points]") === null) { $leveldefaultpoint = $i * 10; // Find the default point. $mform->setDefault("levels[$i][points]", $leveldefaultpoint); } - // Level color. - $mform->addElement('tool_skills_colorpicker', "levels[$i][color]", get_string('levelscolor', 'tool_skills', $i), ''); - $mform->setType("levels[$i][color]", PARAM_TEXT); - $mform->addHelpButton("levels[$i][color]", 'levelscolor', 'tool_skills'); - - // Level image. - $mform->addElement('filemanager', "levels[$i][image]", get_string('levelsimage', 'tool_skills', $i)); - $mform->addHelpButton("levels[$i][image]", 'levelsimage', 'tool_skills'); // Set the default values for the level 0. if ($i == 0 && !$mform->getElementValue("levels[$i][name]")) { @@ -187,126 +168,6 @@ public function definition_after_data() { $this->add_action_buttons(); } - /** - * Load in existing data as form defaults. Usually new entry defaults are stored directly in - * form definition (new entry form); this function is used to load in data where values - * already exist and data is being edited (edit entry form). - * - * note: $slashed param removed - * - * @param stdClass|array $defaultvalues object or array of default values - */ - public function set_data($defaultvalues) { - - $this->data_preprocessing($defaultvalues); // Include to store the files. - - parent::set_data($defaultvalues); - } - - /** - * Return submitted data if properly submitted or returns NULL if validation fails or - * if there is no submitted data. - * - * Do not override this method, override data_postprocessing() instead. - * - * @return object submitted data; NULL if not valid or not submitted or cancelled - */ - public function get_data() { - $data = parent::get_data(); - if ($data) { - $this->data_postprocessing($data); - } - return $data; - } - - /** - * Process the skills module data before set the default. - * - * @param mixed $defaultvalues default values - * @return void - */ - public function data_preprocessing(&$defaultvalues) { - // System context. - $context = context_system::instance(); - - // Convert to object, file manager methods require the objects. - $defaultvalues = (object) $defaultvalues; - - $filemanagers = [ - 'image' => 'levelimage', - ]; - - // Levels count. - $levelscount = $defaultvalues->levelscount; - - // Prepare the file manager fields to store images. - foreach ($filemanagers as $configname => $filearea) { - // For all levels in this skills. - for ($i = 0; $i <= $levelscount; $i++) { - - if (empty($defaultvalues->levels[$i])) { - continue; - } - // Draft item id. - $draftitemid = file_get_submitted_draft_itemid($filearea); - // Use the level id as item id. - $levelid = $defaultvalues->levels[$i]['id'] ?? 0; - // Store the draft files to area files. - file_prepare_draft_area( - $draftitemid, $context->id, 'tool_skills', $filearea, $levelid, [ - 'subdirs' => 0, - 'accepted_types' => ['web_image'], - ] - ); - $defaultvalues->levels[$i][$configname] = $draftitemid; - } - } - - } - - /** - * Prepare the data after form was submited. - * - * Store all the editor files and update the structure and the file urls with placeholders. - * It used the ludemy block instance id (ludemyid) as item id and uses the local_ludemy as component. - * Also use the name of the editor as filearea. - * - * @param mixed $data submitted data - * @return void - */ - public function data_postprocessing(&$data) { - - $context = context_system::instance(); - // Prepare the editor to support files. - $data = (object) $data; - - $filemanagers = [ - 'image' => 'levelimage', - ]; - - $levelscount = $data->levelscount; - - // Prepare the file manager fields to store images. - foreach ($filemanagers as $configname => $filearea) { - - 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. - file_save_draft_area_files( - $data->levels[$i][$configname], $context->id, 'tool_skills', - $filearea, $levelid, $this->get_editor_options($context) - ); - } - } - } - /** * Editor form element options. * diff --git a/classes/level.php b/classes/level.php index 2b3b55d..b3d992b 100644 --- a/classes/level.php +++ b/classes/level.php @@ -94,7 +94,7 @@ protected function __construct(int $levelid) { * * @return stdClass Level record. */ - public function get_level_record() : stdClass { + public function get_level_record(): stdClass { return $this->levelrecord; } @@ -103,7 +103,7 @@ public function get_level_record() : stdClass { * * @return stdClass */ - public function get_data() : stdClass { + public function get_data(): stdClass { return $this->data; } @@ -112,7 +112,7 @@ public function get_data() : stdClass { * * @return stdClass|bool */ - protected function fetch_record() : ?stdClass { + protected function fetch_record(): ?stdClass { global $DB; if ($skill = $DB->get_record('tool_skills_levels', ['id' => $this->levelid])) { @@ -161,7 +161,7 @@ public function get_points() { * * @param int $levelid */ - public static function get(int $levelid) : \tool_skills\level { + public static function get(int $levelid): \tool_skills\level { // Create the instance for this skill and return. return new self($levelid); } diff --git a/classes/logs.php b/classes/logs.php index 1297c39..6482294 100644 --- a/classes/logs.php +++ b/classes/logs.php @@ -66,10 +66,7 @@ public function get_log(int $skillid, int $userid, int $methodid, string $method 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; } @@ -87,18 +84,26 @@ public function get_log(int $skillid, int $userid, int $methodid, string $method public function add(int $skillid, int $userid, int $points, int $methodid, string $method, int $status=1) { global $DB; - 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); + if ($record = $DB->get_record('tool_skills_awardlogs', ['skill' => $skillid, 'userid' => $userid, + 'method' => $method, 'methodid' => $methodid, ], '*', IGNORE_MULTIPLE)) { + + $record->points = $points; + $record->status = $status; + $record->timecreated = time(); + // Update the existing record log. + return $DB->update_record('tool_skills_awardlogs', $record); + + } else { + $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 index de5616a..a21ff71 100644 --- a/classes/plugininfo/skilladdon.php +++ b/classes/plugininfo/skilladdon.php @@ -28,6 +28,7 @@ * Skilladdon is subplugin of tool_skills. */ class skilladdon extends \core\plugininfo\base { + /** * Returns the information about plugin availability * diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index cac3689..b06aa5a 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -83,7 +83,7 @@ public static function get_metadata(collection $collection): collection { * @param int $userid The user to search. * @return contextlist $contextlist The list of contexts used in this plugin. */ - public static function get_contexts_for_userid(int $userid) : contextlist { + public static function get_contexts_for_userid(int $userid): contextlist { $contextlist = new \core_privacy\local\request\contextlist(); // User completions. $sql = "SELECT ctx.id @@ -238,7 +238,7 @@ public static function export_user_data(approved_contextlist $contextlist) { get_string('privacy:awardlogs', 'tool_skills'), array_filter( $userpoints, - function(stdClass $point) use ($contextlist) : bool { + function(stdClass $point) use ($contextlist): bool { return $point->userid == $contextlist->get_user()->id; } ), @@ -299,7 +299,7 @@ private static function export_user_points(string $path, array $userpoints, $use private static function group_by_property(array $classes, string $property): array { return array_reduce( $classes, - function (array $classes, stdClass $class) use ($property) : array { + function (array $classes, stdClass $class) use ($property): array { $classes[$class->{$property}][] = $class; return $classes; }, diff --git a/classes/skills.php b/classes/skills.php index 80ced6a..d73bc63 100644 --- a/classes/skills.php +++ b/classes/skills.php @@ -141,7 +141,7 @@ protected function __construct(int $skillid) { * * @return stdClass Skill record. */ - public function get_skill_record() : stdClass { + public function get_skill_record(): stdClass { return $this->skillrecord; } @@ -150,7 +150,7 @@ public function get_skill_record() : stdClass { * * @return int */ - public function get_id() : int { + public function get_id(): int { return $this->data->id; } @@ -159,7 +159,7 @@ public function get_id() : int { * * @return stdClass */ - public function get_data() : stdClass { + public function get_data(): stdClass { return $this->data; } @@ -168,7 +168,7 @@ public function get_data() : stdClass { * * @return string */ - public function get_name() : string { + public function get_name(): string { return format_string($this->data->name); } @@ -177,7 +177,7 @@ public function get_name() : string { * * @return stdClass|bool */ - protected function fetch_skill_record() : ?stdClass { + protected function fetch_skill_record(): ?stdClass { global $DB; if ($skill = $DB->get_record('tool_skills', ['id' => $this->skillid])) { @@ -194,7 +194,7 @@ protected function fetch_skill_record() : ?stdClass { * * @return stdClass */ - protected function update_data_structure() : stdClass { + protected function update_data_structure(): stdClass { // Clone the skill record. $data = clone $this->skillrecord; @@ -252,8 +252,10 @@ public function delete_skill() { \tool_skills\courseskills::remove_skills($this->skillid); // Extend the addons remove skills. \tool_skills\helper::extend_addons_remove_skills($this->skillid); - + // Remove the user points for the skill. $DB->delete_records('tool_skills_userpoints', ['skill' => $this->skillid]); + // Remove skills points award logs. + $DB->delete_records('tool_skills_awardlogs', ['skill' => $this->skillid]); return true; } @@ -317,7 +319,7 @@ public function get_points_to_earnskill() { * * @return void */ - protected function fetch_levels() : void { + protected function fetch_levels(): void { global $DB; $this->levels = $DB->get_records('tool_skills_levels', ['skill' => $this->skillid]); @@ -328,7 +330,7 @@ protected function fetch_levels() : void { * * @return array */ - public function get_levels() : array { + public function get_levels(): array { if (empty($this->levels)) { $this->fetch_levels(); @@ -342,7 +344,7 @@ public function get_levels() : array { * * @return int */ - public function get_levels_count() : int { + public function get_levels_count(): int { global $DB; return count($this->get_levels()); @@ -418,7 +420,7 @@ public function moveto_level($skillobj, int $levelid, int $userid) { // Update the new points for this user in db. $this->set_userskill_points($userid, $levelpoints); // Create a award log for this user point increase. - $this->create_user_point_award($skillobj, $userid, $levelpoints); + $this->create_user_point_award($skillobj, $userid, $levelpoints - $userskill->points); } } @@ -451,7 +453,7 @@ public function increase_points($skillobj, int $points, int $userid) { * @param int $points * @return int */ - public 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]; @@ -525,7 +527,7 @@ public function get_proficient() { * * @param int $skillid */ - public static function get(int $skillid) : \tool_skills\skills { + public static function get(int $skillid): \tool_skills\skills { // Create the instance for this skill and return. return new self($skillid); } @@ -543,8 +545,6 @@ public static function manage_instance($formdata) { // Verfiy the current user has capability to manage skills. require_capability('tool/skills:manage', context_system::instance()); - // TODO: Try catch. - $record = clone $formdata; $record->categories = json_encode($record->categories); diff --git a/classes/table/archived_skills.php b/classes/table/archived_skills.php index 8989906..f29c5f7 100644 --- a/classes/table/archived_skills.php +++ b/classes/table/archived_skills.php @@ -96,8 +96,8 @@ public function query_db($pagesize, $useinitialsbar = true) { * @param stdClass $row * @return string */ - public function col_description(stdClass $row) : string { - return format_text($row->description, FORMAT_HTML, ['overflow' => false]); + public function col_description(stdClass $row): string { + return format_text($row->description, FORMAT_HTML, ['overflowdiv' => false]); } /** @@ -106,7 +106,7 @@ public function col_description(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_name(stdClass $row) : string { + public function col_name(stdClass $row): string { return format_string($row->name); } @@ -116,7 +116,7 @@ public function col_name(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_categories(stdClass $row) : string { + public function col_categories(stdClass $row): string { $categories = $row->categories ?? []; if (empty($categories)) { @@ -139,7 +139,7 @@ public function col_categories(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_timecreated(stdClass $row) :string { + public function col_timecreated(stdClass $row): string { return userdate($row->timecreated); } @@ -150,7 +150,7 @@ public function col_timecreated(stdClass $row) :string { * @param stdClass $row * @return string */ - public function col_timearchived(stdClass $row) :string { + public function col_timearchived(stdClass $row): string { return userdate($row->timearchived); } @@ -160,7 +160,7 @@ public function col_timearchived(stdClass $row) :string { * @param stdClass $row * @return string */ - public function col_actions(stdClass $row) : string { + public function col_actions(stdClass $row): string { global $OUTPUT; // Base url to edit the skills. diff --git a/classes/table/course_skills_table.php b/classes/table/course_skills_table.php index 7c38331..98c5ee4 100644 --- a/classes/table/course_skills_table.php +++ b/classes/table/course_skills_table.php @@ -118,7 +118,7 @@ public function query_db($pagesize, $useinitialsbar = true) { * @param stdClass $row * @return string */ - public function col_name(stdClass $row) : string { + public function col_name(stdClass $row): string { return format_string($row->name); } @@ -128,8 +128,8 @@ public function col_name(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_description(stdClass $row) : string { - return format_text($row->description, FORMAT_HTML, ['overflow' => false]); + public function col_description(stdClass $row): string { + return format_text($row->description, FORMAT_HTML, ['overflowdiv' => false]); } /** @@ -138,7 +138,7 @@ public function col_description(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_uponcompletion(stdClass $row) : string { + public function col_uponcompletion(stdClass $row): string { $completion = $row->uponcompletion ?? 0; @@ -169,7 +169,7 @@ public function col_uponcompletion(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_actions(stdClass $row) : string { + public function col_actions(stdClass $row): string { global $OUTPUT; // Base url to edit the skills. diff --git a/classes/table/skills_table.php b/classes/table/skills_table.php index 89865d9..ff6cd98 100644 --- a/classes/table/skills_table.php +++ b/classes/table/skills_table.php @@ -102,7 +102,7 @@ public function query_db($pagesize, $useinitialsbar = true) { * @param stdClass $row * @return string */ - public function col_name(stdClass $row) : string { + public function col_name(stdClass $row): string { return format_string($row->name); } @@ -112,8 +112,8 @@ public function col_name(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_description(stdClass $row) : string { - return format_text($row->description, FORMAT_HTML, ['overflow' => false]); + public function col_description(stdClass $row): string { + return format_text($row->description, FORMAT_HTML, ['overflowdiv' => false]); } /** @@ -122,7 +122,7 @@ public function col_description(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_categories(stdClass $row) : string { + public function col_categories(stdClass $row): string { $categories = $row->categories ?? []; if (empty($categories)) { @@ -142,7 +142,7 @@ public function col_categories(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_timecreated(stdClass $row) : string { + public function col_timecreated(stdClass $row): string { return userdate($row->timecreated); } @@ -152,7 +152,7 @@ public function col_timecreated(stdClass $row) : string { * @param stdClass $row * @return string */ - public function col_actions(stdClass $row) : string { + public function col_actions(stdClass $row): string { global $OUTPUT; // Base url to edit the skills. diff --git a/classes/user.php b/classes/user.php index ea1d823..5376203 100644 --- a/classes/user.php +++ b/classes/user.php @@ -93,7 +93,6 @@ public function get_userid() { /** * Get the current user skills list. - * TODO: Not used anymore. * * @return array */ diff --git a/db/install.xml b/db/install.xml index 32b3337..f533dc5 100644 --- a/db/install.xml +++ b/db/install.xml @@ -13,7 +13,6 @@ - @@ -31,7 +30,6 @@ - diff --git a/db/subplugins.json b/db/subplugins.json index c461248..f3cc168 100644 --- a/db/subplugins.json +++ b/db/subplugins.json @@ -1,5 +1,5 @@ { "plugintypes": { - "skilladdon": "admin/tool/skills/addon" + "skilladdon": "admin/tool/skills/addon" } - } \ No newline at end of file +} diff --git a/db/upgrade.php b/db/upgrade.php index 66237a2..b0503f2 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -32,7 +32,7 @@ function xmldb_tool_skills_upgrade($oldversion) { $dbman = $DB->get_manager(); - if ($oldversion < 2023102505) { + if ($oldversion < 2024020700) { $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. @@ -40,10 +40,10 @@ function xmldb_tool_skills_upgrade($oldversion) { $dbman->add_field($table, $field); } - upgrade_plugin_savepoint(true, 2023102505, 'tool', 'skills'); + upgrade_plugin_savepoint(true, 2024020700, 'tool', 'skills'); } - if ($oldversion < 2024019004) { + if ($oldversion < 2024020802) { $table = new xmldb_table('tool_skills_awardlogs'); $field = new xmldb_field('skill', XMLDB_TYPE_INTEGER, 18, null, null, null, null, 'id'); @@ -60,7 +60,7 @@ function xmldb_tool_skills_upgrade($oldversion) { } } } - upgrade_plugin_savepoint(true, 2024019004, 'tool', 'skills'); + upgrade_plugin_savepoint(true, 2024020802, 'tool', 'skills'); } return true; diff --git a/lang/en/tool_skills.php b/lang/en/tool_skills.php index 807c08d..cc283f7 100644 --- a/lang/en/tool_skills.php +++ b/lang/en/tool_skills.php @@ -35,6 +35,7 @@ $string['error:identityexists'] = 'Given skill identity is exists, Please use unique value'; $string['error:numeric'] = 'Value should be in numbers'; // ...List page strings. +$string['subplugintype_skilladdon_plural'] = 'Skill addons'; $string['skillslist'] = 'List of skills'; $string['skillslisthead'] = 'Manage skills'; $string['skillslist_desc'] = 'Create a new skill and edit exsiting skills'; @@ -72,22 +73,21 @@ $string['identitykey_help'] = 'Key to identity the skill, this should be unique value'; $string['learningtime'] = 'Learning time'; $string['learningtime_help'] = 'Time to spend in the course to complete this skill'; -$string['skillcolor'] = 'Skill color'; -$string['skillcolor_help'] = 'Color of the skill'; $string['levelscount'] = 'Number of levels'; $string['updatelevelscount'] = 'Update levels count'; // ...Levels form fields string. $string['skillslevels'] = 'Levels'; $string['skillslevel'] = 'Level'; $string['levelscount_help'] = 'Choose the number of levels that exist for this skill. Each level may have a specific number of points required for achievement.'; +$string['baselevelname'] = 'Base level name'; +$string['baselevelpoint'] = 'Base level point'; +$string['baselevelheading'] = 'Base level info'; +$string['baselevelname_help'] = 'This is the name assigned to the base level of the skill. The base level represents the starting point for skill progression.'; +$string['baselevelpoint_help'] = 'The number of points required to achieve the base level of the skill. This indicates the minimum proficiency level for the skill.'; $string['levelsname'] = 'Level #{$a} name'; $string['levelsname_help'] = 'Enter the name for level'; $string['levelspoint'] = 'Level #{$a} point'; $string['levelspoint_help'] = 'Enter the number of points required to achieve Level. This field is required.'; -$string['levelscolor'] = 'Level #{$a} color'; -$string['levelscolor_help'] = 'Select a color to represent Level. This will override the general skill color for visualization purposes.'; -$string['levelsimage'] = 'Level #{$a} image'; -$string['levelsimage_help'] = 'Upload an image that depicts Level of skill. This will be used for visualization.'; $string['levelsnohead'] = 'Level #{$a} info'; // ...course menu strings. $string['courseskills'] = 'Set course skills'; @@ -97,6 +97,11 @@ $string['completionpoints'] = 'Points'; $string['completionsetlevel'] = 'Set level'; $string['completionforcelevel'] = 'Force level'; +// ...result in user profile page. +$string['completionnothingresult'] = 'Nothing'; +$string['completionpointsresult'] = 'Points upto '; +$string['completionsetlevelresult'] = 'Set to '; +$string['completionforcelevelresult'] = 'Force to '; $string['completionlevel'] = 'Level'; $string['coursestatus'] = 'Assign skill to course'; $string['coursestatus_help'] = 'Select enable to assign the skill to course, then user will receive the points/level for course completion'; @@ -122,7 +127,8 @@ $string['earned'] = 'Earned'; $string['pointsearned'] = 'Points earned'; $string['pointscomplete'] = 'Points to complete this skill: {$a} '; -$string['pointsforcompletion'] = 'Points for completion'; +$string['pointsforcompletion'] = 'Max points'; +$string['uponcompletionresult'] = 'Upon completion'; $string['usersreport'] = 'View users points for this skill'; $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'; diff --git a/lib.php b/lib.php index aad4f12..63274ad 100644 --- a/lib.php +++ b/lib.php @@ -100,6 +100,14 @@ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $co $skillslist[$skillid] = $data->skillobj; // Skill instance. } + // Upon completion. + $options = [ + \tool_skills\skills::COMPLETIONNOTHING => get_string('completionnothingresult', 'tool_skills'), + \tool_skills\skills::COMPLETIONPOINTS => get_string('completionpointsresult', 'tool_skills'), + \tool_skills\skills::COMPLETIONSETLEVEL => get_string('completionsetlevelresult', 'tool_skills'), + \tool_skills\skills::COMPLETIONFORCELEVEL => get_string('completionforcelevelresult', 'tool_skills'), + ]; + foreach ($newskills as $skillid => $skills) { $skill = $skillslist[$skillid]; $skillpoints = $skill->get_points_to_earnskill(); @@ -116,6 +124,7 @@ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $co $skillstr .= html_writer::start_tag('ul'); // Start the list of skills courses. foreach ($skills as $id => $data) { + // Course skill object. $skillcourse = $data->skillcourse; $pointstoearn = $skillcourse->get_points_earned_fromcourse(); @@ -127,11 +136,29 @@ function tool_skills_myprofile_navigation(tree $tree, $user, $iscurrentuser, $co $course = $data->skillcourse->get_course(); $li = html_writer::link($courseurl, format_string($course->fullname)); - $coursepointstr = get_string('pointsforcompletion', 'tool_skills') . " : " . $pointstoearn; - $coursepointstr .= html_writer::tag('b', + // Generate the expected result of the course completion. + $pointstr = get_string('uponcompletionresult', 'tool_skills') . ": "; + if (isset($options[$data->uponcompletion])) { + $pointstr .= $options[$data->uponcompletion]; + + // Upon compeltion of course user will reached the levels. + $resultstring = ''; + if (in_array($data->uponcompletion, [\tool_skills\skills::COMPLETIONSETLEVEL, + tool_skills\skills::COMPLETIONFORCELEVEL])) { + $resultstring = isset($data->levels[$data->level]) ? format_string($data->levels[$data->level]->name) : ''; + + } else if ($data->uponcompletion == \tool_skills\skills::COMPLETIONPOINTS) { // Points. + $resultstring = $data->points; + } + $resultstring .= '
'; + $pointstr .= html_writer::tag('span', $resultstring, ['class' => 'course-completion-result']); + } + + $pointstr .= get_string('pointsforcompletion', 'tool_skills') . " : " . $pointstoearn; + $pointstr .= html_writer::tag('b', " (".get_string('earned', 'tool_skills') . ": " .( $pointsfromcourse ?? 0) . ")" ); - $li .= html_writer::tag('p', $coursepointstr, ['class' => 'skills-points-'.$course->shortname]); + $li .= html_writer::tag('p', $pointstr, ['class' => 'skills-points-'.$course->shortname]); $skillstr .= html_writer::tag('li', $li); @@ -179,7 +206,7 @@ function tool_skills_get_fontawesome_icon_map() { * @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()) { +function tool_skills_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload, array $options=[]) { if ($context->contextlevel != CONTEXT_SYSTEM) { return false; diff --git a/styles.css b/styles.css index 0e1bd5f..5ed7129 100644 --- a/styles.css +++ b/styles.css @@ -53,3 +53,7 @@ body.behat-site .toolskills-status-switch input.custom-control-input { body.module-skills .activity-header { display: none; } +/* Profile page user completion result. */ +#page-user-profile li.contentnode.toolskill-courses-points span.course-completion-result { + font-weight: 600; +} diff --git a/tests/behat/tool_skills_general.feature b/tests/behat/tool_skills_general.feature index ba0040d..960e36f 100644 --- a/tests/behat/tool_skills_general.feature +++ b/tests/behat/tool_skills_general.feature @@ -23,8 +23,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I set the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | And I click on "Save changes" "button" And I should see "Beginner" in the "tool_skills_list" "table" @@ -34,8 +34,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I create skill with the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | 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" @@ -55,8 +55,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I create skill with the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | 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" @@ -74,8 +74,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I set the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | And I click on "Save changes" "button" 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" @@ -96,8 +96,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I create skill with the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | 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 @@ -109,8 +109,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I create skill with the following fields to these values: | Skill name | Critical thinker | | Key | critical-thinker | - | Level #0 name | beginner | - | Level #0 point | 20 | + | Base level name | beginner | + | Base level point | 20 | 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" @@ -133,8 +133,8 @@ Feature: Configuring the tool_skills plugin on the "Skills" page, applying diffe And I create skill with the following fields to these values: | Skill name | Beginner | | Key | beginner | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | 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" diff --git a/tests/behat/tool_skills_managelevels.feature b/tests/behat/tool_skills_managelevels.feature index 87e4aca..bdcc3d5 100644 --- a/tests/behat/tool_skills_managelevels.feature +++ b/tests/behat/tool_skills_managelevels.feature @@ -42,8 +42,8 @@ Feature: Allocate points to users, need to manage levels and assign skills to co | Skill name | Beginner | | Key | beginner | | Number of levels | 2 | - | Level #0 name | beginner | - | Level #0 point | 10 | + | Base level name | beginner | + | Base level point | 10 | | Level #1 name | Level 1 | | Level #1 point | 20 | | Level #2 name | Level 2 | diff --git a/version.php b/version.php index 0e0e833..bb7b34d 100644 --- a/version.php +++ b/version.php @@ -24,7 +24,7 @@ defined('MOODLE_INTERNAL') || die; -$plugin->version = 2024019004; +$plugin->version = 2024020805; $plugin->requires = 2021051700; // Requires this Moodle version. $plugin->component = 'tool_skills'; // Full name of the plugin (used for diagnostics). $plugin->maturity = MATURITY_STABLE;