Merge branch 'MDL-53635-master' of git://github.com/jleyva/moodle
authorDavid Monllao <davidm@moodle.com>
Mon, 2 May 2016 07:38:11 +0000 (15:38 +0800)
committerDavid Monllao <davidm@moodle.com>
Mon, 2 May 2016 07:38:11 +0000 (15:38 +0800)
320 files changed:
admin/tool/cohortroles/index.php
admin/tool/cohortroles/settings.php
admin/tool/lp/amd/build/competency_plan_navigation.min.js [new file with mode: 0644]
admin/tool/lp/amd/build/competencyactions.min.js
admin/tool/lp/amd/build/competencydialogue.min.js
admin/tool/lp/amd/build/menubar.min.js
admin/tool/lp/amd/build/parentcompetency_form.min.js
admin/tool/lp/amd/build/user_competency_plan_popup.min.js [new file with mode: 0644]
admin/tool/lp/amd/src/competency_plan_navigation.js [new file with mode: 0644]
admin/tool/lp/amd/src/competencyactions.js
admin/tool/lp/amd/src/competencydialogue.js
admin/tool/lp/amd/src/menubar.js
admin/tool/lp/amd/src/parentcompetency_form.js
admin/tool/lp/amd/src/user_competency_plan_popup.js [new file with mode: 0644]
admin/tool/lp/classes/course_competencies_form_element.php
admin/tool/lp/classes/external.php
admin/tool/lp/classes/external/user_competency_summary_exporter.php
admin/tool/lp/classes/external/user_evidence_summary_exporter.php
admin/tool/lp/classes/form/competency.php
admin/tool/lp/classes/form/competency_framework.php
admin/tool/lp/classes/form/plan.php
admin/tool/lp/classes/form/template.php
admin/tool/lp/classes/form/user_evidence.php
admin/tool/lp/classes/output/competency_plan_navigation.php [new file with mode: 0644]
admin/tool/lp/classes/output/course_competencies_page.php
admin/tool/lp/classes/output/renderer.php
admin/tool/lp/classes/output/user_competency_course_navigation.php
admin/tool/lp/competencyframeworks.php
admin/tool/lp/lang/en/tool_lp.php
admin/tool/lp/lib.php
admin/tool/lp/styles.css
admin/tool/lp/templates/competency_path.mustache
admin/tool/lp/templates/competency_plan_navigation.mustache [new file with mode: 0644]
admin/tool/lp/templates/competency_summary.mustache
admin/tool/lp/templates/course_competencies_page.mustache
admin/tool/lp/templates/course_competency_statistics.mustache
admin/tool/lp/templates/manage_competency_frameworks_page.mustache
admin/tool/lp/templates/manage_templates_page.mustache
admin/tool/lp/templates/plan_page.mustache
admin/tool/lp/templates/plans_page.mustache
admin/tool/lp/templates/related_competencies.mustache
admin/tool/lp/templates/template_competencies_page.mustache
admin/tool/lp/templates/template_statistics.mustache
admin/tool/lp/templates/user_competency_course_navigation.mustache
admin/tool/lp/templates/user_evidence_list_page.mustache
admin/tool/lp/templates/user_evidence_page.mustache
admin/tool/lp/tests/behat/behat_tool_lp.php
admin/tool/lp/tests/behat/framework_crud.feature
admin/tool/lp/tests/behat/plan_crud.feature
admin/tool/lp/tests/behat/template_crud.feature
admin/tool/lp/user_competency_in_plan.php
admin/tool/lpmigrate/db/access.php
admin/tool/lpmigrate/lang/en/tool_lpmigrate.php
admin/tool/lpmigrate/templates/migrate_frameworks_results.mustache
admin/tool/uploaduser/index.php
admin/webservice/testclient_forms.php
auth/db/lang/en/auth_db.php
auth/ldap/lang/en/auth_ldap.php
auth/lti/auth.php [new file with mode: 0644]
auth/lti/lang/en/auth_lti.php [new file with mode: 0644]
auth/lti/version.php [new file with mode: 0644]
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_stepslib.php
blocks/activity_modules/tests/behat/block_activity_modules.feature
blocks/feedback/block_feedback.php
blocks/feedback/db/install.php
blocks/feedback/lang/en/block_feedback.php
blocks/lp/block_lp.php
blocks/lp/lang/en/block_lp.php
cache/stores/memcached/lib.php
cohort/lib.php
competency/classes/api.php
competency/classes/competency.php
competency/classes/competency_framework.php
competency/classes/user_evidence.php
competency/tests/api_test.php
competency/tests/competency_test.php [new file with mode: 0644]
competency/tests/external_test.php
competency/tests/hooks_test.php
competency/tests/plan_test.php
course/externallib.php
enrol/editinstance.php
enrol/editinstance_form.php
enrol/externallib.php
enrol/lti/backup/moodle2/backup_enrol_lti_plugin.class.php [new file with mode: 0644]
enrol/lti/backup/moodle2/restore_enrol_lti_plugin.class.php [new file with mode: 0644]
enrol/lti/classes/helper.php [new file with mode: 0644]
enrol/lti/classes/manage_table.php [new file with mode: 0644]
enrol/lti/classes/task/sync_grades.php [new file with mode: 0644]
enrol/lti/classes/task/sync_members.php [new file with mode: 0644]
enrol/lti/db/access.php [new file with mode: 0644]
enrol/lti/db/install.xml [new file with mode: 0644]
enrol/lti/db/tasks.php [new file with mode: 0644]
enrol/lti/ims-blti/LICENSE.txt [new file with mode: 0644]
enrol/lti/ims-blti/OAuth.php [new file with mode: 0644]
enrol/lti/ims-blti/OAuthBody.php [new file with mode: 0644]
enrol/lti/ims-blti/TrivialOAuthDataStore.php [new file with mode: 0644]
enrol/lti/ims-blti/blti.php [new file with mode: 0644]
enrol/lti/ims-blti/blti_util.php [new file with mode: 0644]
enrol/lti/ims-blti/moodle_readme.txt [new file with mode: 0644]
enrol/lti/index.php [new file with mode: 0644]
enrol/lti/lang/en/enrol_lti.php [new file with mode: 0644]
enrol/lti/lib.php [new file with mode: 0644]
enrol/lti/settings.php [new file with mode: 0644]
enrol/lti/tests/behat/basic_settings.feature [new file with mode: 0644]
enrol/lti/tests/behat/index_page.feature [new file with mode: 0644]
enrol/lti/tests/helper_test.php [new file with mode: 0644]
enrol/lti/thirdpartylibs.xml [new file with mode: 0644]
enrol/lti/tool.php [new file with mode: 0644]
enrol/lti/version.php [new file with mode: 0644]
enrol/manual/db/services.php
enrol/manual/externallib.php
files/externallib.php
files/tests/externallib_test.php
grade/externallib.php [deleted file]
grade/grading/form/guide/amd/build/comment_chooser.min.js
grade/grading/form/guide/amd/src/comment_chooser.js
group/externallib.php
lang/en/auth.php
lang/en/cohort.php
lang/en/competency.php
lang/en/error.php
lang/en/grades.php
lang/en/group.php
lang/en/moodle.php
lang/en/role.php
lang/en/tag.php
lib/amd/build/form-autocomplete.min.js
lib/amd/build/templates.min.js
lib/amd/src/form-autocomplete.js
lib/amd/src/templates.js
lib/blocklib.php
lib/classes/dataformat/spout_base.php
lib/classes/event/competency_user_competency_rated.php
lib/classes/event/competency_user_competency_rated_in_course.php
lib/classes/event/competency_user_competency_rated_in_plan.php
lib/classes/plugin_manager.php
lib/db/access.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js
lib/editor/atto/plugins/image/yui/src/button/js/button.js
lib/formslib.php
lib/outputrenderers.php
lib/pagelib.php
lib/tablelib.php
lib/upgrade.txt
message/externallib.php
mod/assign/feedback/editpdf/classes/task/convert_submissions.php
mod/assign/locallib.php
mod/assign/templates/grading_navigation_user_selector.mustache
mod/feedback/README.txt [deleted file]
mod/feedback/TODO.txt [deleted file]
mod/feedback/amd/build/edit.min.js [new file with mode: 0644]
mod/feedback/amd/src/edit.js [new file with mode: 0644]
mod/feedback/analysis.php
mod/feedback/analysis_course.php
mod/feedback/analysis_to_excel.php [deleted file]
mod/feedback/backup/moodle2/backup_feedback_stepslib.php
mod/feedback/backup/moodle2/restore_feedback_stepslib.php
mod/feedback/classes/complete_form.php [new file with mode: 0644]
mod/feedback/classes/completion.php [new file with mode: 0644]
mod/feedback/classes/course_select_form.php [new file with mode: 0644]
mod/feedback/classes/event/course_module_viewed.php
mod/feedback/classes/event/response_deleted.php
mod/feedback/classes/event/response_submitted.php
mod/feedback/classes/output/summary.php [new file with mode: 0644]
mod/feedback/classes/responses_anon_table.php
mod/feedback/classes/responses_table.php
mod/feedback/classes/structure.php [new file with mode: 0644]
mod/feedback/classes/templates_table.php
mod/feedback/complete.php
mod/feedback/db/install.php
mod/feedback/db/install.xml
mod/feedback/db/upgrade.php
mod/feedback/db/upgradelib.php [new file with mode: 0644]
mod/feedback/delete_item.php [deleted file]
mod/feedback/delete_item_form.php [deleted file]
mod/feedback/delete_template.php
mod/feedback/edit.php
mod/feedback/edit_item.php
mod/feedback/item/captcha/lib.php
mod/feedback/item/feedback_item_class.php
mod/feedback/item/feedback_item_form_class.php
mod/feedback/item/info/lib.php
mod/feedback/item/label/lib.php
mod/feedback/item/multichoice/lib.php
mod/feedback/item/multichoice/multichoice_form.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/item/multichoicerated/multichoicerated_form.php
mod/feedback/item/numeric/lib.php
mod/feedback/item/textarea/lib.php
mod/feedback/item/textfield/lib.php
mod/feedback/lang/en/deprecated.txt
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/print.php
mod/feedback/show_entries.php
mod/feedback/styles.css
mod/feedback/tabs.php
mod/feedback/templates/summary.mustache [new file with mode: 0644]
mod/feedback/tests/behat/anonymous.feature
mod/feedback/tests/behat/coursemapping.feature
mod/feedback/tests/behat/export_import.feature
mod/feedback/tests/behat/groups.feature
mod/feedback/tests/behat/multichoice.feature
mod/feedback/tests/behat/non_anonymous.feature
mod/feedback/tests/behat/question_types.feature
mod/feedback/tests/behat/question_types_non_anon.feature [new file with mode: 0644]
mod/feedback/tests/behat/show_nonrespondents.feature
mod/feedback/tests/behat/templates.feature
mod/feedback/tests/events_test.php
mod/feedback/tests/upgradelib_test.php [new file with mode: 0644]
mod/feedback/upgrade.txt
mod/feedback/use_templ.php
mod/feedback/version.php
mod/feedback/view.php
mod/feedback/yui/dragdrop/dragdrop.js
mod/forum/db/services.php
mod/forum/externallib.php
mod/forum/lang/en/forum.php
mod/forum/tests/externallib_test.php
mod/lti/ajax.php
mod/lti/amd/build/cartridge_registration_form.min.js [new file with mode: 0644]
mod/lti/amd/build/events.min.js [new file with mode: 0644]
mod/lti/amd/build/external_registration.min.js [new file with mode: 0644]
mod/lti/amd/build/external_registration_return.min.js [new file with mode: 0644]
mod/lti/amd/build/keys.min.js [new file with mode: 0644]
mod/lti/amd/build/tool_card_controller.min.js [new file with mode: 0644]
mod/lti/amd/build/tool_configure_controller.min.js [new file with mode: 0644]
mod/lti/amd/build/tool_proxy.min.js [new file with mode: 0644]
mod/lti/amd/build/tool_type.min.js [new file with mode: 0644]
mod/lti/amd/src/cartridge_registration_form.js [new file with mode: 0644]
mod/lti/amd/src/events.js [new file with mode: 0644]
mod/lti/amd/src/external_registration.js [new file with mode: 0644]
mod/lti/amd/src/external_registration_return.js [new file with mode: 0644]
mod/lti/amd/src/keys.js [new file with mode: 0644]
mod/lti/amd/src/tool_card_controller.js [new file with mode: 0644]
mod/lti/amd/src/tool_configure_controller.js [new file with mode: 0644]
mod/lti/amd/src/tool_proxy.js [new file with mode: 0644]
mod/lti/amd/src/tool_type.js [new file with mode: 0644]
mod/lti/classes/external.php
mod/lti/classes/output/external_registration_return_page.php [new file with mode: 0644]
mod/lti/classes/output/renderer.php [new file with mode: 0644]
mod/lti/classes/output/tool_configure_page.php [new file with mode: 0644]
mod/lti/db/install.xml
mod/lti/db/services.php
mod/lti/db/upgrade.php
mod/lti/edit_form.php
mod/lti/externalregistrationreturn.php [new file with mode: 0644]
mod/lti/instructor_edit_tool_type.php
mod/lti/lang/en/lti.php
mod/lti/lib.php
mod/lti/locallib.php
mod/lti/mod_form.js
mod/lti/mod_form.php
mod/lti/service/toolsettings/classes/local/resource/contextsettings.php
mod/lti/service/toolsettings/classes/local/resource/linksettings.php
mod/lti/service/toolsettings/classes/local/resource/systemsettings.php
mod/lti/service/toolsettings/classes/local/service/toolsettings.php
mod/lti/service/toolsettings/version.php
mod/lti/settings.php
mod/lti/styles.css
mod/lti/templates/cartridge_registration_form.mustache [new file with mode: 0644]
mod/lti/templates/external_registration.mustache [new file with mode: 0644]
mod/lti/templates/loader.mustache [new file with mode: 0644]
mod/lti/templates/registration_feedback.mustache [new file with mode: 0644]
mod/lti/templates/tool_card.mustache [new file with mode: 0644]
mod/lti/templates/tool_configure.mustache [new file with mode: 0644]
mod/lti/templates/tool_list.mustache [new file with mode: 0644]
mod/lti/templates/tool_proxy_registration_form.mustache [new file with mode: 0644]
mod/lti/templates/tool_type_capabilities_agree.mustache [new file with mode: 0644]
mod/lti/tests/behat/addtool.feature
mod/lti/tests/behat/addtype.feature [new file with mode: 0644]
mod/lti/tests/behat/toolconfigure.feature [new file with mode: 0644]
mod/lti/tests/externallib_test.php
mod/lti/tests/fixtures/ims_cartridge_basic_lti_link.xml [new file with mode: 0644]
mod/lti/tests/locallib_test.php
mod/lti/toolconfigure.php [new file with mode: 0644]
mod/lti/toolssettings.php
mod/lti/typessettings.php
mod/lti/version.php
mod/quiz/attemptlib.php
mod/wiki/classes/external.php
mod/wiki/db/services.php
mod/wiki/lang/en/wiki.php
mod/wiki/tests/externallib_test.php
mod/wiki/version.php
mod/workshop/lang/en/workshop.php
notes/externallib.php
report/competency/classes/output/user_course_navigation.php
report/competency/templates/report.mustache
report/competency/templates/user_course_navigation.mustache
search/classes/engine.php
search/classes/manager.php
search/classes/output/form/search.php
search/classes/output/renderer.php
search/engine/solr/classes/engine.php
search/engine/solr/tests/engine_test.php
search/engine/solr/tests/fixtures/testable_engine.php [new file with mode: 0644]
search/index.php
search/tests/fixtures/mock_search_area.php
search/tests/fixtures/mock_search_engine.php
search/tests/generator/lib.php [new file with mode: 0644]
tag/tests/behat/edit_tag.feature
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/renderers/core_renderer.php
theme/bootstrapbase/style/moodle.css
user/externallib.php
user/tests/externallib_test.php
version.php
webservice/externallib.php
webservice/lib.php
webservice/renderer.php
webservice/upgrade.txt
webservice/upload.php

index a202be6..d53af3e 100644 (file)
@@ -57,15 +57,15 @@ if ($removeid) {
     require_sesskey();
     // We must create them all or none.
     $saved = 0;
-    foreach ($data->userids as $userid) {
-        if (empty($data->cohortids)) {
-            $data->cohortids = array();
-        }
-        foreach ($data->cohortids as $cohortid) {
-            $params = (object) array('userid' => $userid, 'cohortid' => $cohortid, 'roleid' => $data->roleid);
-            $result = \tool_cohortroles\api::create_cohort_role_assignment($params);
-            if ($result) {
-                $saved++;
+    // Loop through userids and cohortids only if both of them are not empty.
+    if (!empty($data->userids) && !empty($data->cohortids)) {
+        foreach ($data->userids as $userid) {
+            foreach ($data->cohortids as $cohortid) {
+                $params = (object) array('userid' => $userid, 'cohortid' => $cohortid, 'roleid' => $data->roleid);
+                $result = \tool_cohortroles\api::create_cohort_role_assignment($params);
+                if ($result) {
+                    $saved++;
+                }
             }
         }
     }
index 98613c1..139a425 100644 (file)
  */
 
 defined('MOODLE_INTERNAL') || die;
-$str = get_string('managecohortroles', 'tool_cohortroles');
-$ADMIN->add('roles', new admin_externalpage('toolcohortroles', $str, '/admin/tool/cohortroles/index.php', 'moodle/role:manage'));
+
+// This tool's required capabilities.
+$capabilities = [
+    'moodle/cohort:view',
+    'moodle/role:manage'
+];
+
+// Check if the user has all of the required capabilities.
+$context = context_system::instance();
+$hasaccess = has_all_capabilities($capabilities, $context);
+
+// Add this admin page only if the user has all of the required capabilities.
+if ($hasaccess) {
+    $str = get_string('managecohortroles', 'tool_cohortroles');
+    $url = new moodle_url('/admin/tool/cohortroles/index.php');
+    $ADMIN->add('roles', new admin_externalpage('toolcohortroles', $str, $url, $capabilities));
+}
diff --git a/admin/tool/lp/amd/build/competency_plan_navigation.min.js b/admin/tool/lp/amd/build/competency_plan_navigation.min.js
new file mode 100644 (file)
index 0000000..1862243
Binary files /dev/null and b/admin/tool/lp/amd/build/competency_plan_navigation.min.js differ
index 76bb677..2ca219d 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencyactions.min.js and b/admin/tool/lp/amd/build/competencyactions.min.js differ
index b22cdb3..2ef99e4 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencydialogue.min.js and b/admin/tool/lp/amd/build/competencydialogue.min.js differ
index e5ed4ae..7ab1169 100644 (file)
Binary files a/admin/tool/lp/amd/build/menubar.min.js and b/admin/tool/lp/amd/build/menubar.min.js differ
index 0161053..d7732ee 100644 (file)
Binary files a/admin/tool/lp/amd/build/parentcompetency_form.min.js and b/admin/tool/lp/amd/build/parentcompetency_form.min.js differ
diff --git a/admin/tool/lp/amd/build/user_competency_plan_popup.min.js b/admin/tool/lp/amd/build/user_competency_plan_popup.min.js
new file mode 100644 (file)
index 0000000..1c320c8
Binary files /dev/null and b/admin/tool/lp/amd/build/user_competency_plan_popup.min.js differ
diff --git a/admin/tool/lp/amd/src/competency_plan_navigation.js b/admin/tool/lp/amd/src/competency_plan_navigation.js
new file mode 100644 (file)
index 0000000..34a70c7
--- /dev/null
@@ -0,0 +1,74 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Event click on selecting competency in the competency autocomplete.
+ *
+ * @package    tool_lp
+ * @copyright  2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery'], function($) {
+
+    /**
+     * CompetencyPlanNavigation
+     *
+     * @param {String} The selector of the competency element.
+     * @param {String} The base url for the page (no params).
+     * @param {Number} The user id
+     * @param {Number} The competency id
+     * @param {Number} The plan id
+     */
+    var CompetencyPlanNavigation = function(competencySelector, baseUrl, userId, competencyId, planId) {
+        this._baseUrl = baseUrl;
+        this._userId = userId + '';
+        this._competencyId = competencyId + '';
+        this._planId = planId;
+        this._ignoreFirstCompetency = true;
+
+        $(competencySelector).on('change', this._competencyChanged.bind(this));
+    };
+
+    /**
+     * The competency was changed in the select list.
+     *
+     * @method _competencyChanged
+     * @param {Event} e
+     */
+    CompetencyPlanNavigation.prototype._competencyChanged = function(e) {
+        if (this._ignoreFirstCompetency) {
+            this._ignoreFirstCompetency = false;
+            return;
+        }
+        var newCompetencyId = $(e.target).val();
+        var queryStr = '?userid=' + this._userId + '&planid=' + this._planId + '&competencyid=' + newCompetencyId;
+        document.location = this._baseUrl + queryStr;
+    };
+
+    /** @type {Number} The id of the competency. */
+    CompetencyPlanNavigation.prototype._competencyId = null;
+    /** @type {Number} The id of the user. */
+    CompetencyPlanNavigation.prototype._userId = null;
+    /** @type {Number} The id of the plan. */
+    CompetencyPlanNavigation.prototype._planId = null;
+    /** @type {String} Plugin base url. */
+    CompetencyPlanNavigation.prototype._baseUrl = null;
+    /** @type {Boolean} Ignore the first change event for competencies. */
+    CompetencyPlanNavigation.prototype._ignoreFirstCompetency = null;
+
+    return /** @alias module:tool_lp/competency_plan_navigation */ CompetencyPlanNavigation;
+
+});
index 2c1c4c3..21e084e 100644 (file)
@@ -628,17 +628,6 @@ define(['jquery',
         }
     };
 
-    /**
-     * Return if the level has a sub level.
-     *
-     * @param  {Number} level The level.
-     * @return {Boolean}
-     * @function hasSubLevel
-     */
-    var hasSubLevel = function(level) {
-        return typeof taxonomiesConstants[level + 1] !== 'undefined';
-    };
-
     /**
      * Return the taxonomy constant for a level.
      *
@@ -764,11 +753,7 @@ define(['jquery',
             var competency = treeModel.getCompetency(id);
 
             level = treeModel.getCompetencyLevel(id);
-            if (!hasSubLevel(level)) {
-                sublevel = false;
-            } else {
-                sublevel = level + 1;
-            }
+            sublevel = level + 1;
 
             actionMenu.show();
             $('[data-region="competencyactions"]').data('competency', competency);
@@ -781,15 +766,12 @@ define(['jquery',
             selectedTitle.text(str);
         });
 
-        if (!sublevel) {
-            btn.hide();
-        } else {
-            strAddTaxonomy(sublevel).then(function(str) {
-                btn.show()
-                    .find('[data-region="term"]')
-                    .text(str);
-            });
-        }
+        strAddTaxonomy(sublevel).then(function(str) {
+            btn.show()
+                .find('[data-region="term"]')
+                .text(str);
+        });
+
         // We handled this event so consume it.
         evt.preventDefault();
         return false;
index 747b9b0..affcea6 100644 (file)
@@ -29,18 +29,20 @@ define(['jquery',
         'tool_lp/dialogue'],
        function($, notification, ajax, templates, str, Dialogue) {
 
+    /**
+     * The main instance we'll be working with.
+     *
+     * @type {Competencydialogue}
+     */
+    var instance;
+
     /**
      * Constructor for CompetencyDialogue.
      *
      * @param {Object} options
      *
      */
-    var Competencydialogue = function(options) {
-        this.options = {
-            includerelated: false,
-            includecourses: false
-        };
-        $.extend(this.options, options);
+    var Competencydialogue = function() {
     };
 
     /**
@@ -56,29 +58,16 @@ define(['jquery',
         }]);
     };
 
-    /**
-     * Callback on dialogue display, it apply enhance on competencies dialogue.
-     *
-     * @param {Dialogue} dialogue
-     * @method enhanceDialogue
-     */
-    Competencydialogue.prototype.enhanceDialogue = function(dialogue) {
-        //Apply watch on the related competencies and competencies in the dialogue.
-        var comprelated = new Competencydialogue({includerelated : false});
-        comprelated.watch(dialogue.getContent());
-    };
-
     /**
      * Display a dialogue box by competencyid.
      *
-     * @param {Number} the competency id
-     * @param {Object} Options for tool_lp_data_for_competency_summary service
-     * @param {Object} dataSource data to be used to display dialogue box
+     * @param {Number} competencyid The competency ID.
+     * @param {Object} options The options.
      * @method showDialogue
      */
-    Competencydialogue.prototype.showDialogue = function(competencyid) {
+    Competencydialogue.prototype.showDialogue = function(competencyid, options) {
 
-        var datapromise = this.getCompetencyDataPromise(competencyid);
+        var datapromise = this.getCompetencyDataPromise(competencyid, options);
         var localthis = this;
         datapromise.done(function(data) {
             // Inner Html in the dialogue content.
@@ -90,8 +79,7 @@ define(['jquery',
                     // Show the dialogue.
                     new Dialogue(
                         data.competency.shortname,
-                        html,
-                        localthis.enhanceDialogue
+                        html
                     );
                 }).fail(notification.exception);
         }).fail(notification.exception);
@@ -130,10 +118,16 @@ define(['jquery',
     Competencydialogue.prototype.clickEventHandler = function(e) {
 
         var compdialogue = e.data.compdialogue;
-        var competencyid = $(e.currentTarget).data('id');
+        var currentTarget = $(e.currentTarget);
+        var competencyid = currentTarget.data('id');
+        var includerelated = !(currentTarget.data('excluderelated'));
+        var includecourses = currentTarget.data('includecourses');
 
         // Show the dialogue box.
-        compdialogue.showDialogue(competencyid);
+        compdialogue.showDialogue(competencyid, {
+            includerelated: includerelated,
+            includecourses: includecourses
+        });
         e.preventDefault();
     };
 
@@ -144,13 +138,13 @@ define(['jquery',
      * @return {Promise} return promise on data request
      * @method getCompetencyDataPromise
      */
-    Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid) {
+    Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid, options) {
 
         var requests = ajax.call([
             { methodname: 'tool_lp_data_for_competency_summary',
               args: { competencyid: competencyid,
-                      includerelated: this.options.includerelated,
-                      includecourses: this.options.includecourses
+                      includerelated: options.includerelated || false,
+                      includecourses: options.includecourses || false
                     }
             }
         ]);
@@ -160,16 +154,24 @@ define(['jquery',
         }).fail(notification.exception);
     };
 
-    /**
-     * Watch the competencies links in container.
-     *
-     * @param {String} container selector of node containing competencies links
-     * @method watch
-     */
-    Competencydialogue.prototype.watch = function(containerSelector) {
-        $(containerSelector).off('click', '[data-action="competency-dialogue"]', this.clickEventHandler);
-        $(containerSelector).on('click', '[data-action="competency-dialogue"]', { compdialogue: this }, this.clickEventHandler);
-    };
+    return /** @alias module:tool_lp/competencydialogue */ {
+
+        /**
+         * Initialise the competency dialogue module.
+         *
+         * Only the first call matters.
+         *
+         * @return {Void}
+         */
+        init: function() {
+            if (typeof instance !== 'undefined') {
+                return;
+            }
 
-    return Competencydialogue;
+            // Instantiate the one instance and delegate event on the body.
+            instance = new Competencydialogue();
+            $('body').delegate('[data-action="competency-dialogue"]', 'click', { compdialogue: instance },
+                instance.clickEventHandler.bind(instance));
+        }
+    };
 });
index cf1b773..a91d770 100644 (file)
@@ -258,16 +258,41 @@ define(['jquery'], function($) {
     Menubar.prototype.setOpenDirection = function() {
         var pos = this.menuRoot.offset();
         var isRTL = $(document.body).hasClass('dir-rtl');
-        var menuMinWidth = 160;
         var openLeft = false;
+        var heightmenuRoot = this.rootMenus.outerHeight();
+        var widthmenuRoot = this.rootMenus.outerWidth();
+        // Sometimes the menuMinWidth is not enough to figure out if menu exceeds the window width.
+        // So we have to calculate the real menu width.
+        var subMenuContainer = this.rootMenus.find('ul.tool-lp-sub-menu');
+
+        // Reset margins.
+        subMenuContainer.css('margin-right', '');
+        subMenuContainer.css('margin-left', '');
+        subMenuContainer.css('margin-top', '');
+
+        subMenuContainer.attr('aria-hidden', false);
+        var menuRealWidth = subMenuContainer.outerWidth(),
+            menuRealHeight = subMenuContainer.outerHeight();
+
+        var margintop = null,
+            marginright = null,
+            marginleft = null;
+        var top = pos.top - $(window).scrollTop();
+        // Top is the same for RTL and LTR.
+        if (top + menuRealHeight > $(window).height()) {
+            margintop = menuRealHeight + heightmenuRoot;
+            subMenuContainer.css('margin-top', '-' + margintop + 'px');
+        }
 
         if (isRTL) {
-            if (pos.left - menuMinWidth < 0) {
-                openLeft = true;
+            if (pos.left - menuRealWidth < 0) {
+                marginright = menuRealWidth - widthmenuRoot;
+                subMenuContainer.css('margin-right', '-' + marginright + 'px');
             }
         } else {
-            if (pos.left + menuMinWidth > $( window ).width()) {
-                openLeft = true;
+            if ( pos.left + menuRealWidth > $(window).width()) {
+                marginleft = menuRealWidth - widthmenuRoot;
+                subMenuContainer.css('margin-left', '-' + marginleft + 'px');
             }
         }
 
index 1e0e3d7..2dabb3c 100644 (file)
@@ -30,20 +30,17 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
      * @param {String} inputHiddenSelector The hidden input field selector.
      * @param {String} staticElementSelector The static element displaying the parent competency.
      * @param {Number} frameworkId The competency framework ID.
-     * @param {Number} frameworkMaxLevel The framework max level.
      * @param {Number} pageContextId The page context ID.
      */
     var ParentCompetencyForm = function(buttonSelector,
                                         inputHiddenSelector,
                                         staticElementSelector,
                                         frameworkId,
-                                        frameworkMaxLevel,
                                         pageContextId) {
         this.buttonSelector = buttonSelector;
         this.inputHiddenSelector = inputHiddenSelector;
         this.staticElementSelector = staticElementSelector;
         this.frameworkId = frameworkId;
-        this.frameworkMaxLevel = frameworkMaxLevel;
         this.pageContextId = pageContextId;
 
         // Register the events.
@@ -58,8 +55,6 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
     ParentCompetencyForm.prototype.staticElementSelector = null;
     /** @var {Number} The competency framework ID. */
     ParentCompetencyForm.prototype.frameworkId = null;
-    /** @var {Number} The framework max level. */
-    ParentCompetencyForm.prototype.frameworkMaxLevel = null;
     /** @var {Number} The page context ID. */
     ParentCompetencyForm.prototype.pageContextId = null;
 
@@ -103,55 +98,7 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
             e.preventDefault();
 
             var picker = new Picker(self.pageContextId, self.frameworkId, 'self', false);
-            var maxlevel = self.frameworkMaxLevel;
-            // Override the fetchcompetencies method to filter by max level.
-            picker._fetchCompetencies = function(frameworkId, searchText) {
-                var self = this;
-
-                return ajax.call([
-                    { methodname: 'core_competency_search_competencies', args: {
-                        searchtext: searchText,
-                        competencyframeworkid: frameworkId
-                    }}
-                ])[0].done(function(competencies) {
-
-                    var disabledcompetencies = [];
-                    function addCompetencyChildren(parent, competencies) {
-                        for (var i = 0; i < competencies.length; i++) {
-                            // Check if competency does not exceed the framework max level.
-                            var path = String(competencies[i].path),
-                            level = path.split('/').length - 2;
-                            if (level >= maxlevel && competencies[i].id !== "0") {
-                                disabledcompetencies.push(competencies[i].id);
-                            }
-
-                            if (competencies[i].parentid == parent.id) {
-                                parent.haschildren = true;
-                                competencies[i].children = [];
-                                competencies[i].haschildren = false;
-                                parent.children[parent.children.length] = competencies[i];
-                                addCompetencyChildren(competencies[i], competencies);
-                            }
-                        }
-                    }
 
-                    // Expand the list of competencies into a tree.
-                    var i, tree = [], comp;
-                    for (i = 0; i < competencies.length; i++) {
-                        comp = competencies[i];
-                        if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
-                            comp.children = [];
-                            comp.haschildren = 0;
-                            tree[tree.length] = comp;
-                            addCompetencyChildren(comp, competencies);
-                        }
-                    }
-
-                    self._competencies = tree;
-                    self.setDisallowedCompetencyIDs(disabledcompetencies);
-
-                }.bind(self)).fail(Notification.exception);
-            };
             // Override the render method to make framework selectable.
             picker._render = function() {
                 var self = this;
@@ -185,7 +132,6 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
          * @param {String} inputHiddenSelector The hidden input field selector.
          * @param {String} staticElementSelector The static element displaying the parent competency.
          * @param {Number} frameworkId The competency framework ID.
-         * @param {Number} frameworkMaxLevel The framework max level.
          * @param {Number} pageContextId The page context ID.
          * @method init
          */
@@ -193,14 +139,12 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
                         inputSelector,
                         staticElementSelector,
                         frameworkId,
-                        frameworkMaxLevel,
                         pageContextId) {
             // Create instance.
             new ParentCompetencyForm(buttonSelector,
                                     inputSelector,
                                     staticElementSelector,
                                     frameworkId,
-                                    frameworkMaxLevel,
                                     pageContextId);
         }
     };
diff --git a/admin/tool/lp/amd/src/user_competency_plan_popup.js b/admin/tool/lp/amd/src/user_competency_plan_popup.js
new file mode 100644 (file)
index 0000000..87c977c
--- /dev/null
@@ -0,0 +1,130 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Module to open user competency plan in popup
+ *
+ * @package    report_competency
+ * @copyright  2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates', 'tool_lp/dialogue'],
+       function($, notification, str, ajax, templates, Dialogue) {
+
+    /**
+     * UserCompetencyPopup
+     *
+     * @param {String} The regionSelector
+     * @param {String} The userCompetencySelector
+     * @param {Number} The plan ID
+     */
+    var UserCompetencyPopup = function(regionSelector, userCompetencySelector, planId) {
+        this._regionSelector = regionSelector;
+        this._userCompetencySelector = userCompetencySelector;
+        this._planId = planId;
+
+        $(this._regionSelector).on('click', this._userCompetencySelector, this._handleClick.bind(this));
+    };
+
+    /**
+     * Get the data from the closest TR and open the popup.
+     *
+     * @method _handleClick
+     * @param {Event} e
+     */
+    UserCompetencyPopup.prototype._handleClick = function(e) {
+        e.preventDefault();
+        var tr = $(e.target).closest('tr');
+        var competencyId = $(tr).data('competencyid');
+        var userId = $(tr).data('userid');
+        var planId = this._planId;
+
+        var requests = ajax.call([{
+            methodname : 'tool_lp_data_for_user_competency_summary_in_plan',
+            args: { competencyid: competencyId, planid: planId },
+            done: this._contextLoaded.bind(this),
+            fail: notification.exception
+        }]);
+
+        // Log the user competency viewed in plan event.
+        requests[0].then(function (result) {
+            var eventMethodName = 'core_competency_user_competency_viewed_in_plan';
+            // Trigger core_competency_user_competency_plan_viewed event instead if plan is already completed.
+            if (result.plan.iscompleted) {
+                eventMethodName = 'core_competency_user_competency_plan_viewed';
+            }
+            ajax.call([{
+                methodname: eventMethodName,
+                args: {competencyid: competencyId, userid: userId, planid: planId},
+                fail: notification.exception
+            }]);
+        });
+    };
+
+    /**
+     * We loaded the context, now render the template.
+     *
+     * @method _contextLoaded
+     * @param {Object} context
+     */
+    UserCompetencyPopup.prototype._contextLoaded = function(context) {
+        var self = this;
+        templates.render('tool_lp/user_competency_summary_in_plan', context).done(function(html, js) {
+            str.get_string('usercompetencysummary', 'report_competency').done(function(title) {
+                (new Dialogue(title, html, templates.runTemplateJS.bind(templates, js), self._refresh.bind(self), true));
+            }).fail(notification.exception);
+        }).fail(notification.exception);
+    };
+
+    /**
+     * Refresh the page.
+     *
+     * @method _refresh
+     */
+    UserCompetencyPopup.prototype._refresh = function() {
+        var planId = this._planId;
+
+        ajax.call([{
+            methodname : 'tool_lp_data_for_plan_page',
+            args: { planid: planId},
+            done: this._pageContextLoaded.bind(this),
+            fail: notification.exception
+        }]);
+    };
+
+    /**
+     * We loaded the context, now render the template.
+     *
+     * @method _pageContextLoaded
+     * @param {Object} context
+     */
+    UserCompetencyPopup.prototype._pageContextLoaded = function(context) {
+        var self = this;
+        templates.render('tool_lp/plan_page', context).done(function(html, js) {
+            templates.replaceNode(self._regionSelector, html, js);
+        }).fail(notification.exception);
+    };
+
+    /** @type {String} The selector for the region with the user competencies */
+    UserCompetencyPopup.prototype._regionSelector = null;
+    /** @type {String} The selector for the region with a single user competencies */
+    UserCompetencyPopup.prototype._userCompetencySelector = null;
+    /** @type {Number} The plan Id */
+    UserCompetencyPopup.prototype._planId = null;
+
+    return /** @alias module:tool_lp/user_competency_plan_popup */ UserCompetencyPopup;
+
+});
index 035d2cd..4683e64 100644 (file)
@@ -75,6 +75,8 @@ class tool_lp_course_competencies_form_element extends MoodleQuickForm_autocompl
 
         $context = context_course::instance($courseid);
         foreach ($competencies as $competency) {
+            // We don't need to show the description as part of the options, so just set this to null.
+            $competency['competency']->set_description(null);
             $exporter = new competency_exporter($competency['competency'], array('context' => $context));
             $templatecontext = array('competency' => $exporter->export($OUTPUT));
             $id = $competency['competency']->get_id();
index 61a922f..59608c5 100644 (file)
@@ -404,6 +404,7 @@ class external extends external_api {
             'canmanagecompetencyframeworks' => new external_value(PARAM_BOOL, 'User can manage competency frameworks'),
             'canmanagecoursecompetencies' => new external_value(PARAM_BOOL, 'User can manage linked course competencies'),
             'canconfigurecoursecompetencies' => new external_value(PARAM_BOOL, 'User can configure course competency settings'),
+            'cangradecompetencies' => new external_value(PARAM_BOOL, 'User can grade competencies.'),
             'settings' => course_competency_settings_exporter::get_read_structure(),
             'statistics' => course_competency_statistics_exporter::get_read_structure(),
             'competencies' => new external_multiple_structure(new external_single_structure(array(
index cc43377..b43dde8 100644 (file)
@@ -72,7 +72,7 @@ class user_competency_summary_exporter extends \core_competency\external\exporte
                 'optional' => true
             ),
             'usercompetencyplan' => array(
-                'type' => user_competency_exporter::read_properties_definition(),
+                'type' => user_competency_plan_exporter::read_properties_definition(),
                 'optional' => true
             ),
             'usercompetencycourse' => array(
index 828724d..c3851a6 100644 (file)
@@ -66,6 +66,9 @@ class user_evidence_summary_exporter extends \core_competency\external\persisten
                 'optional' => true,
                 'multiple' => true
             ),
+            'userhasplan' => array(
+                'type' => PARAM_BOOL
+            ),
         );
     }
 
@@ -124,6 +127,7 @@ class user_evidence_summary_exporter extends \core_competency\external\persisten
             'canmanage' => $this->persistent->can_manage(),
             'filecount' => count($files),
             'files' => $files,
+            'userhasplan' => $this->persistent->user_has_plan(),
             'hasurlorfiles' => !empty($files) || !empty($url),
             'urlshort' => $urlshort,
             'competencycount' => count($userevidencecompetencies),
index 7073f22..915ea6f 100644 (file)
@@ -88,21 +88,23 @@ class competency extends persistent {
                 '#tool_lp_parentcompetency',
                 '#id_parentdesc',
                 $framework->get_id(),
-                \core_competency\competency_framework::get_taxonomies_max_level(),
                 $pagecontextid));
         }
 
-        $mform->addElement('text', 'shortname',
-                           get_string('shortname', 'tool_lp'));
+        // Name.
+        $mform->addElement('text', 'shortname', get_string('shortname', 'tool_lp'), 'maxlength="100"');
         $mform->setType('shortname', PARAM_TEXT);
         $mform->addRule('shortname', null, 'required', null, 'client');
+        $mform->addRule('shortname', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
+        // Description.
         $mform->addElement('editor', 'description',
                            get_string('description', 'tool_lp'), array('rows' => 4));
         $mform->setType('description', PARAM_RAW);
-        $mform->addElement('text', 'idnumber',
-                           get_string('idnumber', 'tool_lp'));
+        // ID number.
+        $mform->addElement('text', 'idnumber', get_string('idnumber', 'tool_lp'), 'maxlength="100"');
         $mform->setType('idnumber', PARAM_TEXT);
         $mform->addRule('idnumber', null, 'required', null, 'client');
+        $mform->addRule('idnumber', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
 
         $scales = array(null => get_string('inheritfromframework', 'tool_lp')) + get_scales_menu();
         $scaleid = $mform->addElement('select', 'scaleid', get_string('scale', 'tool_lp'), $scales);
index c60ecdd..6dde66c 100644 (file)
@@ -54,17 +54,20 @@ class competency_framework extends persistent {
 
         $mform->addElement('header', 'generalhdr', get_string('general'));
 
-        $mform->addElement('text', 'shortname',
-                           get_string('shortname', 'tool_lp'));
+        // Name.
+        $mform->addElement('text', 'shortname', get_string('shortname', 'tool_lp'), 'maxlength="100"');
         $mform->setType('shortname', PARAM_TEXT);
         $mform->addRule('shortname', null, 'required', null, 'client');
+        $mform->addRule('shortname', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
+        // Description.
         $mform->addElement('editor', 'description',
                            get_string('description', 'tool_lp'), array('rows' => 4));
         $mform->setType('description', PARAM_RAW);
-        $mform->addElement('text', 'idnumber',
-                           get_string('idnumber', 'tool_lp'));
+        // ID number.
+        $mform->addElement('text', 'idnumber', get_string('idnumber', 'tool_lp'), 'maxlength="100"');
         $mform->setType('idnumber', PARAM_TEXT);
         $mform->addRule('idnumber', null, 'required', null, 'client');
+        $mform->addRule('idnumber', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
 
         $scales = get_scales_menu();
         $scaleid = $mform->addElement('select', 'scaleid', get_string('scale', 'tool_lp'), $scales);
@@ -97,7 +100,8 @@ class competency_framework extends persistent {
         $mform->addElement('header', 'taxonomyhdr', get_string('taxonomies', 'tool_lp'));
         $taxonomies = \core_competency\competency_framework::get_taxonomies_list();
         $taxdefaults = array();
-        for ($i = 1; $i <= \core_competency\competency_framework::get_taxonomies_max_level(); $i++) {
+        $taxcount = max($framework ? $framework->get_depth() : 4, 4);
+        for ($i = 1; $i <= $taxcount; $i++) {
             $mform->addElement('select', "taxonomies[$i]", get_string('levela', 'tool_lp', $i), $taxonomies);
             $taxdefaults[$i] = \core_competency\competency_framework::TAXONOMY_COMPETENCY;
         }
index 54604ee..461f8c2 100644 (file)
@@ -52,9 +52,12 @@ class plan extends persistent {
 
         $mform->addElement('header', 'generalhdr', get_string('general'));
 
-        $mform->addElement('text', 'name', get_string('planname', 'tool_lp'));
+        // Name.
+        $mform->addElement('text', 'name', get_string('planname', 'tool_lp'), 'maxlength="100"');
         $mform->setType('name', PARAM_TEXT);
         $mform->addRule('name', null, 'required', null, 'client');
+        $mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
+        // Description.
         $mform->addElement('editor', 'description', get_string('plandescription', 'tool_lp'), array('rows' => 4));
         $mform->setType('description', PARAM_RAW);
 
index e5f6940..8922d24 100644 (file)
@@ -50,10 +50,12 @@ class template extends persistent {
 
         $mform->addElement('header', 'generalhdr', get_string('general'));
 
-        $mform->addElement('text', 'shortname',
-                           get_string('shortname', 'tool_lp'));
+        // Name.
+        $mform->addElement('text', 'shortname', get_string('shortname', 'tool_lp'), 'maxlength="100"');
         $mform->setType('shortname', PARAM_TEXT);
         $mform->addRule('shortname', null, 'required', null, 'client');
+        $mform->addRule('shortname', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
+        // Description.
         $mform->addElement('editor', 'description',
                            get_string('description', 'tool_lp'), array('rows' => 4));
         $mform->setType('description', PARAM_RAW);
index 7741444..28de24c 100644 (file)
@@ -47,15 +47,18 @@ class user_evidence extends persistent {
 
         $mform->addElement('header', 'generalhdr', get_string('general'));
 
+        // Name.
         $mform->addElement('text', 'name', get_string('userevidencename', 'tool_lp'), 'maxlength="100"');
         $mform->setType('name', PARAM_TEXT);
         $mform->addRule('name', null, 'required', null, 'client');
-
+        $mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'client');
+        // Description.
         $mform->addElement('editor', 'description', get_string('userevidencedescription', 'tool_lp'), array('rows' => 10));
         $mform->setType('description', PARAM_RAW);
 
         $mform->addElement('url', 'url', get_string('userevidenceurl', 'tool_lp'), array(), array('usefilepicker' => false));
         $mform->setType('url', PARAM_RAW_TRIMMED);      // Can not use PARAM_URL, it silently converts bad URLs to ''.
+        $mform->addHelpButton('url', 'userevidenceurl', 'tool_lp');
 
         $mform->addElement('filemanager', 'files', get_string('userevidencefiles', 'tool_lp'), array(),
             $this->_customdata['fileareaoptions']);
diff --git a/admin/tool/lp/classes/output/competency_plan_navigation.php b/admin/tool/lp/classes/output/competency_plan_navigation.php
new file mode 100644 (file)
index 0000000..4ffde89
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * User competency plan page class.
+ *
+ * @package    tool_lp
+ * @copyright  2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_lp\output;
+
+use renderable;
+use renderer_base;
+use templatable;
+use context_course;
+use \core_competency\external\competency_exporter;
+use stdClass;
+
+/**
+ * User competency plan navigation class.
+ *
+ * @package    tool_lp
+ * @copyright  2016 Issam Taboubi <issam.taboubi@umontreal.ca>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class competency_plan_navigation implements renderable, templatable {
+
+    /** @var userid */
+    protected $userid;
+
+    /** @var competencyid */
+    protected $competencyid;
+
+    /** @var planid */
+    protected $planid;
+
+    /** @var baseurl */
+    protected $baseurl;
+
+    /**
+     * Construct.
+     *
+     * @param int $userid
+     * @param int $competencyid
+     * @param int $planid
+     * @param string $baseurl
+     */
+    public function __construct($userid, $competencyid, $planid, $baseurl) {
+        $this->userid = $userid;
+        $this->competencyid = $competencyid;
+        $this->planid = $planid;
+        $this->baseurl = $baseurl;
+    }
+
+    /**
+     * Export the data.
+     *
+     * @param renderer_base $output
+     * @return stdClass
+     */
+    public function export_for_template(renderer_base $output) {
+
+        $data = new stdClass();
+        $data->userid = $this->userid;
+        $data->competencyid = $this->competencyid;
+        $data->planid = $this->planid;
+        $data->baseurl = $this->baseurl;
+        $data->jumptocompetency = get_string('jumptocompetency', 'tool_lp');
+
+        $plancompetencies = \core_competency\api::list_plan_competencies($data->planid);
+        $data->competencies = array();
+        $contextcache = array();
+        foreach ($plancompetencies as $plancompetency) {
+            $frameworkid = $plancompetency->competency->get_competencyframeworkid();
+            if (!isset($contextcache[$frameworkid])) {
+                $contextcache[$frameworkid] = $plancompetency->competency->get_context();
+            }
+            $context = $contextcache[$frameworkid];
+            $exporter = new competency_exporter($plancompetency->competency, array('context' => $context));
+            $competency = $exporter->export($output);
+            if ($competency->id == $this->competencyid) {
+                $competency->selected = true;
+            }
+            $data->competencies[] = $competency;
+        }
+        $data->hascompetencies = count($data->competencies);
+        return $data;
+    }
+}
index 45c5cbf..395561f 100644 (file)
@@ -80,6 +80,7 @@ class course_competencies_page implements renderable, templatable {
         $this->coursecompetencylist = api::list_course_competencies($courseid);
         $this->canmanagecoursecompetencies = has_capability('moodle/competency:coursecompetencymanage', $this->context);
         $this->canconfigurecoursecompetencies = has_capability('moodle/competency:coursecompetencyconfigure', $this->context);
+        $this->cangradecompetencies = has_capability('moodle/competency:competencygrade', $this->context);
         $this->coursecompetencysettings = api::read_course_competency_settings($courseid);
         $this->coursecompetencystatistics = new course_competency_statistics($courseid);
 
@@ -183,6 +184,7 @@ class course_competencies_page implements renderable, templatable {
         $data->canmanagecompetencyframeworks = $this->canmanagecompetencyframeworks;
         $data->canmanagecoursecompetencies = $this->canmanagecoursecompetencies;
         $data->canconfigurecoursecompetencies = $this->canconfigurecoursecompetencies;
+        $data->cangradecompetencies = $this->cangradecompetencies;
         $exporter = new course_competency_settings_exporter($this->coursecompetencysettings);
         $data->settings = $exporter->export($output);
         $related = array('context' => $this->context);
index f2c68c0..ca45e29 100644 (file)
@@ -206,6 +206,17 @@ class renderer extends plugin_renderer_base {
         return parent::render_from_template('tool_lp/user_competency_course_navigation', $data);
     }
 
+    /**
+     * Defer to template.
+     *
+     * @param competency_plan_navigation $nav
+     * @return string
+     */
+    public function render_competency_plan_navigation(competency_plan_navigation $nav) {
+        $data = $nav->export_for_template($this);
+        return parent::render_from_template('tool_lp/competency_plan_navigation', $data);
+    }
+
     /**
      * Defer to template.
      *
index ef40ba4..411a296 100644 (file)
@@ -84,8 +84,11 @@ class user_competency_course_navigation implements renderable, templatable {
         $data->courseid = $this->courseid;
         $data->baseurl = $this->baseurl;
         $data->groupselector = '';
+        $data->jumptocompetency = get_string('jumptocompetency', 'tool_lp');
+        $data->jumptouser = get_string('jumptouser', 'tool_lp');
 
-        if (has_capability('moodle/competency:coursecompetencymanage', $context)) {
+        if (has_any_capability(array('moodle/competency:usercompetencyview', 'moodle/competency:coursecompetencymanage'),
+                $context)) {
             $course = $DB->get_record('course', array('id' => $this->courseid));
             $currentgroup = groups_get_course_group($course, true);
             if ($currentgroup !== false) {
index 5b76b5f..8f484ad 100644 (file)
@@ -40,7 +40,6 @@ if (!\core_competency\competency_framework::can_read_context($context)) {
 
 $title = get_string('competencies', 'core_competency');
 $pagetitle = get_string('competencyframeworks', 'tool_lp');
-$pagesubtitle = get_string('listcompetencyframeworkscaption', 'tool_lp');
 
 // Set up the page.
 $PAGE->set_context($context);
@@ -51,7 +50,6 @@ $PAGE->set_heading($title);
 $output = $PAGE->get_renderer('tool_lp');
 echo $output->header();
 echo $output->heading($pagetitle, 2);
-echo $output->heading($pagesubtitle, 3);
 
 $page = new \tool_lp\output\manage_competency_frameworks_page($context);
 echo $output->render($page);
index d4269a7..71f4af2 100644 (file)
@@ -39,19 +39,19 @@ $string['addnewtemplate'] = 'Add new learning plan template';
 $string['addnewuserevidence'] = 'Add new evidence';
 $string['addtemplatecompetencies'] = 'Add competencies to learning plan template';
 $string['aisrequired'] = '\'{$a}\' is required';
-$string['aplanswerecreated'] = '{$a} plans were created.';
-$string['aplanswerecreatedmoremayrequiresync'] = '{$a} plans were created, more will be created during the next synchronization.';
+$string['aplanswerecreated'] = '{$a} learning plans were created.';
+$string['aplanswerecreatedmoremayrequiresync'] = '{$a} learning plans were created; more will be created during the next synchronisation.';
 $string['assigncohorts'] = 'Assign cohorts';
-$string['averageproficiencyrate'] = 'The average proficiency rate for completed plans based on this template is {$a} %';
+$string['averageproficiencyrate'] = 'The average proficiency rate for completed learning plans based on this template is {$a} %';
 $string['cancelreviewrequest'] = 'Cancel review request';
 $string['cannotaddrules'] = 'This competency cannot be configured.';
-$string['cannotcreateuserplanswhentemplateduedateispassed'] = 'New user plans can not be created: this template\'s due date has, or is about to, expire.';
-$string['cannotcreateuserplanswhentemplatehidden'] = 'New user plans can not be created while this template is hidden.';
+$string['cannotcreateuserplanswhentemplateduedateispassed'] = 'New learning plans cannot be created. The template due date has expired, or is about to expire.';
+$string['cannotcreateuserplanswhentemplatehidden'] = 'New learning plans cannot be created while this template is hidden.';
 $string['category'] = 'Category';
 $string['chooserating'] = 'Choose a rating...';
-$string['cohortssyncedtotemplate'] = 'Cohorts synced to this template';
+$string['cohortssyncedtotemplate'] = 'Cohorts synced to this learning plan template';
 $string['competenciesforframework'] = 'Competencies for {$a}';
-$string['competenciesmostoftennotproficient'] = 'Competencies most often not proficient in completed plans';
+$string['competenciesmostoftennotproficient'] = 'Competencies most often not proficient in completed learning plans';
 $string['competenciesmostoftennotproficientincourse'] = 'Competencies most often not proficient in this course';
 $string['competencycannotbedeleted'] = 'The competency \'{$a}\' can not be deleted';
 $string['competencycreated'] = 'Competency created';
@@ -59,8 +59,8 @@ $string['competencycrossreferencedcompetencies'] = '{$a} cross-referenced compet
 $string['competencyframework'] = 'Competency framework';
 $string['competencyframeworkcreated'] = 'Competency framework created.';
 $string['competencyframeworkname'] = 'Name';
-$string['competencyframeworkroot'] = 'No parent (Top level competency)';
-$string['competencyframeworks'] = 'Competency Frameworks';
+$string['competencyframeworkroot'] = 'No parent (top-level competency)';
+$string['competencyframeworks'] = 'Competency frameworks';
 $string['competencyframeworkupdated'] = 'Competency framework updated.';
 $string['competencyoutcome_complete'] = 'Mark as complete';
 $string['competencyoutcome_evidence'] = 'Attach an evidence';
@@ -70,26 +70,26 @@ $string['competencypicker'] = 'Competency picker';
 $string['competencyrule'] = 'Competency rule';
 $string['competencyupdated'] = 'Competency updated';
 $string['completeplan'] = 'Complete this learning plan';
-$string['completeplanconfirm'] = 'Set the plan \'{$a}\' to completed? The current status of all the users competencies in this plan will be recorded, and the plan will become read only.';
+$string['completeplanconfirm'] = 'Set the learning plan \'{$a}\' to completed? If so, the current status of all users\' competencies will be recorded, and the plan will become read only.';
 $string['configurecoursecompetencysettings'] = 'Configure course competencies';
 $string['configurescale'] = 'Configure scales';
 $string['coursecompetencies'] = 'Course competencies';
-$string['coursecompetencyratingsarenotpushedtouserplans'] = 'Competency ratings in this course do not affect individual learning plans.';
-$string['coursecompetencyratingsarepushedtouserplans'] = 'Competency ratings in this course are updated immediately in individual learning plans.';
-$string['coursecompetencyratingsquestion'] = 'When a course competency is rated, does the rating update the competency in the users learning plans, or is it only applied to the course?';
+$string['coursecompetencyratingsarenotpushedtouserplans'] = 'Competency ratings in this course do not affect learning plans.';
+$string['coursecompetencyratingsarepushedtouserplans'] = 'Competency ratings in this course are updated immediately in learning plans.';
+$string['coursecompetencyratingsquestion'] = 'When a course competency is rated, does the rating update the competency in the learning plans, or is it only applied to the course?';
 $string['coursesusingthiscompetency'] = 'Courses linked to this competency';
 $string['coveragesummary'] = '{$a->competenciescoveredcount} of {$a->competenciescount} competencies are covered ( {$a->coveragepercentage} % )';
-$string['createplans'] = 'Create plans';
+$string['createplans'] = 'Create learning plans';
 $string['createlearningplans'] = 'Create learning plans';
 $string['crossreferencedcompetencies'] = 'Cross-referenced competencies';
 $string['default'] = 'Default';
 $string['deletecompetency'] = 'Delete competency \'{$a}\'?';
 $string['deletecompetencyframework'] = 'Delete competency framework \'{$a}\'?';
-$string['deletecompetencyparenthasrule'] = 'Delete competency \'{$a}\'? This will also remove the rule set on its parent.';
-$string['deleteplan'] = 'Delete plan \'{$a}\'?';
-$string['deleteplans'] = 'Delete the plans';
+$string['deletecompetencyparenthasrule'] = 'Delete competency \'{$a}\'? This will also remove the rule set for its parent.';
+$string['deleteplan'] = 'Delete learning plan \'{$a}\'?';
+$string['deleteplans'] = 'Delete the learning plans';
 $string['deletetemplate'] = 'Delete learning plan template \'{$a}\'?';
-$string['deletetemplatewithplans'] = 'This learning plan template has user plans associated. You have to indicate how to process those plans.';
+$string['deletetemplatewithplans'] = 'This template has learning plans associated. You have to indicate how to process those plans.';
 $string['deletethisplan'] = 'Delete this learning plan';
 $string['deletethisuserevidence'] = 'Delete this evidence';
 $string['deleteuserevidence'] = 'Delete the evidence of prior learning \'{$a}\'?';
@@ -106,11 +106,11 @@ $string['editthisuserevidence'] = 'Edit this evidence';
 $string['edituserevidence'] = 'Edit evidence';
 $string['evidence'] = 'Evidence';
 $string['findcourses'] = 'Find courses';
-$string['frameworkcannotbedeleted'] = 'The competency framework \'{$a}\' can not be deleted';
+$string['frameworkcannotbedeleted'] = 'The competency framework \'{$a}\' cannot be deleted';
 $string['hidden'] = 'Hidden';
 $string['hiddenhint'] = '(hidden)';
-$string['idnumber'] = 'Id number';
-$string['inheritfromframework'] = 'Inherit from framework (default)';
+$string['idnumber'] = 'ID number';
+$string['inheritfromframework'] = 'Inherit from competency framework (default)';
 $string['itemstoadd'] = 'Items to add';
 $string['jumptocompetency'] = 'Jump to competency';
 $string['jumptouser'] = 'Jump to user';
@@ -135,16 +135,16 @@ $string['move'] = 'Move';
 $string['movecompetency'] = 'Move competency';
 $string['movecompetencyafter'] = 'Move competency after \'{$a}\'';
 $string['movecompetencyframework'] = 'Move competency framework';
-$string['movecompetencytochildofselfwillresetrules'] = 'Moving the competency will remove its own rule, and the rules set on its parent and destination. Do you want to continune?';
-$string['movecompetencywillresetrules'] = 'Moving the competency will remove the rules set on its parent and destination. Do you want to continune?';
+$string['movecompetencytochildofselfwillresetrules'] = 'Moving the competency will remove its own rule, and the rules set for its parent and destination. Do you want to continue?';
+$string['movecompetencywillresetrules'] = 'Moving the competency will remove the rules set for its parent and destination. Do you want to continue?';
 $string['moveframeworkafter'] = 'Move competency framework after \'{$a}\'';
 $string['movetonewparent'] = 'Relocate';
-$string['myplans'] = 'My plans';
+$string['myplans'] = 'My learning plans';
 $string['nfiles'] = '{$a} file(s)';
 $string['noactivities'] = 'No activities';
 $string['nocompetencies'] = 'No competencies have been created in this framework.';
 $string['nocompetenciesincourse'] = 'No competencies have been linked to this course.';
-$string['nocompetenciesintemplate'] = 'No competencies have been linked to this template.';
+$string['nocompetenciesintemplate'] = 'No competencies have been linked to this learning plan template.';
 $string['nocompetencyframeworks'] = 'No competency frameworks have been created yet.';
 $string['nocompetencyselected'] = 'No competency selected';
 $string['nocrossreferencedcompetencies'] = 'No other competencies have been cross-referenced to this competency.';
@@ -152,27 +152,27 @@ $string['noevidence'] = 'No evidence';
 $string['nofiles'] = 'No files';
 $string['nolinkedcourses'] = 'No courses are linked to this competency';
 $string['noparticipants'] = 'No participants found.';
-$string['noplanswerecreated'] = 'No plans were created.';
+$string['noplanswerecreated'] = 'No learning plans were created.';
 $string['notemplates'] = 'No learning plan templates have been created yet.';
 $string['nourl'] = 'No URL';
-$string['nouserevidence'] = 'No evidence of prior learning have been added yet.';
+$string['nouserevidence'] = 'No evidence of prior learning has been added yet.';
 $string['nouserplans'] = 'No learning plans have been created yet.';
-$string['oneplanwascreated'] = 'A plan was created';
+$string['oneplanwascreated'] = 'A learning plan was created';
 $string['outcome'] = 'Outcome';
 $string['path'] = 'Path:';
 $string['parentcompetency'] = 'Parent';
 $string['parentcompetency_edit'] = 'Edit parent';
-$string['parentcompetency_help'] = 'Define the parent under which the competency will be added. It can either be another competency within the same framework, or the root of the competency framework for a top level competency.';
+$string['parentcompetency_help'] = 'Define the parent under which the competency will be added. It can either be another competency within the same framework, or the root of the competency framework for a top-level competency.';
 $string['planapprove'] = 'Make active';
-$string['plancompleted'] = 'Plan completed';
+$string['plancompleted'] = 'Learning plan completed';
 $string['plancreated'] = 'Learning plan created';
 $string['plandescription'] = 'Description';
 $string['planname'] = 'Name';
-$string['plantemplate'] = 'Select template';
+$string['plantemplate'] = 'Select learning plan template';
 $string['plantemplate_help'] = 'A learning plan created from a template will contain a list of competencies that match the template. Updates to the template will be reflected in any plan created from that template.';
 $string['planunapprove'] = 'Send back to draft';
 $string['planupdated'] = 'Learning plan updated';
-$string['pluginname'] = 'Learning Plans';
+$string['pluginname'] = 'Learning plans';
 $string['points'] = 'Points';
 $string['pointsgivenfor'] = 'Points given for \'{$a}\'';
 $string['proficient'] = 'Proficient';
@@ -181,21 +181,24 @@ $string['rate'] = 'Rate';
 $string['ratecomment'] = 'Evidence notes';
 $string['rating'] = 'Rating';
 $string['ratingaffectsonlycourse'] = 'Rating a competency only updates the competency in this course';
-$string['ratingaffectsuserplans'] = 'Rating a competency also updates the competency in all of the users learning plans';
+$string['ratingaffectsuserplans'] = 'Rating a competency also updates the competency in all of the learning plans';
 $string['reopenplan'] = 'Reopen this learning plan';
-$string['reopenplanconfirm'] = 'Reopen the plan \'{$a}\'? The status of the users competencies that was recorded at the time the plan was previously completed will be deleted, and the plan will become active again.';
+$string['reopenplanconfirm'] = 'Reopen the learning plan \'{$a}\'? If so, the status of the users\' competencies that was recorded at the time the plan was previously completed will be deleted, and the plan will become active again.';
 $string['requestreview'] = 'Request review';
 $string['reviewer'] = 'Reviewer';
 $string['reviewstatus'] = 'Review status';
 $string['savechanges'] = 'Save changes';
 $string['scale'] = 'Scale';
-$string['scale_help'] = 'A scale determines how proficiency is measured in a competency. After selecting a scale, you must configure it. The item selected for Default is the rating that will be assigned when a competency is set to be automatically completed. The items selected for Proficient indicate which values will mark the competencies as proficient when they are rated.';
+$string['scale_help'] = 'A scale determines how proficiency is measured in a competency. After selecting a scale, it needs to be configured.
+
+* The item selected as \'Default\' is the rating given when a competency is automatically completed.
+* The item(s) selected as \'Proficient\' indicate(s) which value(s) will mark the competencies as proficient when they are rated.';
 $string['scalevalue'] = 'Scale value';
 $string['search'] = 'Search...';
 $string['selectcohortstosync'] = 'Select cohorts to sync';
 $string['selectcompetencymovetarget'] = 'Select a location to move this competency to:';
 $string['selectedcompetency'] = 'Selected competency';
-$string['selectuserstocreateplansfor'] = 'Select users to create plans for';
+$string['selectuserstocreateplansfor'] = 'Select users to create learning plans for';
 $string['sendallcompetenciestoreview'] = 'Send all competencies in review for evidence of prior learning \'{$a}\'';
 $string['sendcompetenciestoreview'] = 'Send competencies for review';
 $string['shortname'] = 'Name';
@@ -250,25 +253,25 @@ $string['taxonomy_selected_practice'] = 'Selected practice';
 $string['taxonomy_selected_proficiency'] = 'Selected proficiency';
 $string['taxonomy_selected_skill'] = 'Selected skill';
 $string['taxonomy_selected_value'] = 'Selected value';
-$string['template'] = 'Template';
+$string['template'] = 'Learning plan template';
 $string['templatebased'] = 'Template based';
-$string['templatecohortnotsyncedwhileduedateispassed'] = 'Cohorts will not be synchronized while the template\'s due date is passed.';
-$string['templatecohortnotsyncedwhilehidden'] = 'Cohorts will not be synchronized while this template is hidden.';
-$string['templatecompetencies'] = 'Template competencies';
+$string['templatecohortnotsyncedwhileduedateispassed'] = 'Cohorts will not be synchronised if the template\'s due date has passed.';
+$string['templatecohortnotsyncedwhilehidden'] = 'Cohorts will not be synchronised while this template is hidden.';
+$string['templatecompetencies'] = 'Learning plan template competencies';
 $string['templatecreated'] = 'Learning plan template created';
 $string['templatename'] = 'Name';
 $string['templates'] = 'Learning plan templates';
 $string['templateupdated'] = 'Learning plan template updated';
 $string['totalrequiredtocomplete'] = 'Total required to complete';
 $string['unlinkcompetencycourse'] = 'Unlink the competency \'{$a}\' from the course?';
-$string['unlinkcompetencyplan'] = 'Unlink the competency \'{$a}\' from the plan?';
-$string['unlinkcompetencytemplate'] = 'Unlink the competency \'{$a}\' from the template?';
-$string['unlinkplanstemplate'] = 'Unlink the plans from template';
-$string['unlinkplantemplate'] = 'Unlink from template';
-$string['unlinkplantemplateconfirm'] = 'Unlink the plan \'{$a}\' from its template? Any change made to the template will no longer be applied to the plan. This action can not be undone.';
+$string['unlinkcompetencyplan'] = 'Unlink the competency \'{$a}\' from the learning plan?';
+$string['unlinkcompetencytemplate'] = 'Unlink the competency \'{$a}\' from the learning plan template?';
+$string['unlinkplanstemplate'] = 'Unlink the learning plans from their template';
+$string['unlinkplantemplate'] = 'Unlink from learning plan template';
+$string['unlinkplantemplateconfirm'] = 'Unlink the learning plan \'{$a}\' from its template? Any change made to the template will no longer be applied to the plan. This action can not be undone.';
 $string['uponcoursecompletion'] = 'Upon course completion:';
 $string['uponcoursemodulecompletion'] = 'Upon activity completion:';
-$string['usercompetencyfrozen'] = 'This record is now frozen. It reflects the state of the user\'s competency when their plan was marked as complete.';
+$string['usercompetencyfrozen'] = 'This record is now frozen. It reflects the state of the user\'s competency when their learning plan was marked as complete.';
 $string['userevidence'] = 'Evidence of prior learning';
 $string['userevidencecreated'] = 'Evidence of prior learning created';
 $string['userevidencedescription'] = 'Description';
@@ -277,11 +280,12 @@ $string['userevidencename'] = 'Name';
 $string['userevidencesummary'] = 'Summary';
 $string['userevidenceupdated'] = 'Evidence of prior learning updated';
 $string['userevidenceurl'] = 'URL';
+$string['userevidenceurl_help'] = 'The URL must start with \'http://\' or \'https://\'.';
 $string['viewdetails'] = 'View details';
 $string['visible'] = 'Visible';
-$string['visible_help'] = 'A competency framework can be hidden from teachers. This could be useful if a framework is still in the process of being developed.';
+$string['visible_help'] = 'A competency framework can be hidden whilst it is being set up or updated to a new version.';
 $string['when'] = 'When';
 $string['xcompetencieslinkedoutofy'] = '{$a->x} out of {$a->y} competencies linked to courses';
 $string['xcompetenciesproficientoutofy'] = '{$a->x} out of {$a->y} competencies are proficient';
 $string['xcompetenciesproficientoutofyincourse'] = 'You are proficient in {$a->x} out of {$a->y} competencies in this course.';
-$string['xplanscompletedoutofy'] = '{$a->x} out of {$a->y} plans completed for this template';
+$string['xplanscompletedoutofy'] = '{$a->x} out of {$a->y} learning plans completed for this template';
index 1539e32..b2e894d 100644 (file)
@@ -36,8 +36,8 @@ function tool_lp_extend_navigation_course($navigation, $course, $coursecontext)
         return;
     }
 
-    // Just a link to course report.
-    $title = get_string('coursecompetencies', 'tool_lp');
+    // Just a link to course competency.
+    $title = get_string('competencies', 'core_competency');
     $path = new moodle_url("/admin/tool/lp/coursecompetencies.php", array('courseid' => $course->id));
     $settingsnode = navigation_node::create($title,
                                             $path,
index c725a95..75dd6b9 100644 (file)
@@ -10,9 +10,8 @@
     vertical-align: top;
 }
 .path-admin-tool-lp .progress {
-    width: 10em;
+    width: 100%;
     display: inline-block;
-    margin-left: 2em;
     margin-right: 2em;
 }
 .dir-rtl.path-admin-tool-lp .progress .bar {
index 8cb1a55..01e21c6 100644 (file)
@@ -49,7 +49,6 @@
 </nav>
 {{#js}}
 require(['tool_lp/competencydialogue'], function(Compdialogue) {
-    var competencydialogue = new Compdialogue({includerelated : true});
-    competencydialogue.watch('[id="competency-path-{{uniqid}}"]');
+    Compdialogue.init();
 });
 {{/js}}
diff --git a/admin/tool/lp/templates/competency_plan_navigation.mustache b/admin/tool/lp/templates/competency_plan_navigation.mustache
new file mode 100644 (file)
index 0000000..bf70ee9
--- /dev/null
@@ -0,0 +1,22 @@
+<div class="pull-right well">
+{{#hascompetencies}}
+<span>
+<label for="competency-nav-{{uniqid}}" class="accesshide">{{jumptocompetency}}</label>
+<select id="competency-nav-{{uniqid}}">
+{{#competencies}}
+<option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{shortname}} {{idnumber}}</option>
+{{/competencies}}
+</select>
+</span>
+{{/hascompetencies}}
+</form>
+</div>
+{{#js}}
+require(['core/form-autocomplete', 'tool_lp/competency_plan_navigation'], function(autocomplete, nav) {
+    (new nav('#competency-nav-{{uniqid}}', '{{baseurl}}', {{userid}}, {{competencyid}}, {{planid}}));
+{{#hascompetencies}}
+    autocomplete.enhance('#competency-nav-{{uniqid}}', false, false, '{{jumptocompetency}}');
+{{/hascompetencies}}
+
+});
+{{/js}}
index 88a895e..918b6ff 100644 (file)
@@ -1,5 +1,5 @@
 <div class='competency-heading'>
-    <h4>{{competency.shortname}}
+    <h4 id="competency_link_{{competency.id}}">{{competency.shortname}}
         <small>{{competency.idnumber}}</small>
     </h4>
     {{#framework}}
index 3782d4d..0a65312 100644 (file)
 }}
 <div data-region="coursecompetenciespage">
     <div data-region="actions" class="clearfix">
-        <div class="pull-right">
+        <div class="pull-left">
             {{#canmanagecoursecompetencies}}
                 <button disabled>{{#str}}addcoursecompetencies, tool_lp{{/str}}</button>
             {{/canmanagecoursecompetencies}}
         </div>
     </div>
-<div data-region="configurecoursecompetencies">
-{{#settings.pushratingstouserplans}}
-    <p class="alert">
-    {{#str}}coursecompetencyratingsarepushedtouserplans, tool_lp{{/str}}
-{{/settings.pushratingstouserplans}}
-{{^settings.pushratingstouserplans}}
-    <p class="alert alert-info">
-    {{#str}}coursecompetencyratingsarenotpushedtouserplans, tool_lp{{/str}}
-{{/settings.pushratingstouserplans}}
-{{#canconfigurecoursecompetencies}}
-    <a href="#"
-        data-action="configure-course-competency-settings"
-        data-courseid="{{courseid}}"
-        data-pushratingstouserplans="{{settings.pushratingstouserplans}}"
-        >{{#pix}}t/edit, core, {{#str}}edit{{/str}}{{/pix}}</a>
-    </p>
-{{/canconfigurecoursecompetencies}}
-</div>
+    <div data-region="configurecoursecompetencies">
+    {{#cangradecompetencies}}
+        <p class="alert {{^settings.pushratingstouserplans}}alert-info{{/settings.pushratingstouserplans}}">
+        {{#settings.pushratingstouserplans}}
+            {{#str}}coursecompetencyratingsarepushedtouserplans, tool_lp{{/str}}
+        {{/settings.pushratingstouserplans}}
+        {{^settings.pushratingstouserplans}}
+            {{#str}}coursecompetencyratingsarenotpushedtouserplans, tool_lp{{/str}}
+        {{/settings.pushratingstouserplans}}
+        {{#canconfigurecoursecompetencies}}
+            <a href="#"
+               data-action="configure-course-competency-settings"
+               data-courseid="{{courseid}}"
+               data-pushratingstouserplans="{{settings.pushratingstouserplans}}">
+                {{#pix}}t/edit, core, {{#str}}edit{{/str}}{{/pix}}
+            </a>
+        {{/canconfigurecoursecompetencies}}
+        </p>
+    {{/cangradecompetencies}}
+    </div>
 {{#statistics}}
 {{> tool_lp/course_competency_statistics }}
 {{/statistics}}
index cfb9df3..9202052 100644 (file)
 {{#competencycount}}
 <div data-region="coursecompetencystatistics" class="well">
     {{#canbegradedincourse}}
-    <div>
-        <div class="progresstext">
-            {{#str}}xcompetenciesproficientoutofyincourse, tool_lp, { "x": "{{proficientcompetencycount}}", "y": "{{competencycount}}" } {{/str}}
+    <div class="clearfix">
+        <div class="span6">
+            <div class="progresstext">
+                {{#str}}xcompetenciesproficientoutofyincourse, tool_lp, { "x": "{{proficientcompetencycount}}", "y": "{{competencycount}}" } {{/str}}
+            </div>
         </div>
-        <div class="progress">
-            <div class="bar" style="width: {{proficientcompetencypercentage}}%;">
-                {{proficientcompetencypercentageformatted}} %
+        <div class="span6">
+            <span class="pull-right label label-info">{{proficientcompetencypercentageformatted}} %</span>
+            <div class="progress">
+                <div class="bar" style="width: {{proficientcompetencypercentage}}%;"></div>
             </div>
         </div>
     </div>
index facc7c3..ee99906 100644 (file)
     * navigation - array of strings containing buttons for navigation
 }}
 <div data-region="managecompetencies">
-<div class="btn-group pull-right">
+<div class="btn-group pull-left">
     {{#navigation}}
     {{{.}}}
     {{/navigation}}
 </div>
 <table class="generaltable fullwidth managecompetencies">
+    <caption>{{#str}}listcompetencyframeworkscaption, tool_lp{{/str}}</caption>
     <thead>
         <tr>
             <th scope="col">{{#str}}competencyframeworkname, tool_lp{{/str}}</th>
index eb678c0..82d2b76 100644 (file)
@@ -32,7 +32,7 @@
     * navigation - array of strings containing buttons for navigation
 }}
 <div data-region="managetemplates">
-<div class="btn-group pull-right">
+<div class="btn-group pull-left">
     {{#navigation}}
     {{{.}}}
     {{/navigation}}
index 1b66ee3..aec4a7d 100644 (file)
             <a href="{{pluginbaseurl}}/editplan.php?id={{plan.id}}&amp;userid={{plan.userid}}">{{#pix}}t/edit, core, {{#str}}editplan, tool_lp{{/str}}{{/pix}}</a>
         {{/plan.canbeedited}}
     </h2>
+    {{#plan.canbeedited}}
+    <div data-region="actions" class="clearfix">
+        <div class="pull-left">
+            <!-- Button to add competencies to the plan -->
+            <button class="btn" data-action="add">{{#pix}}t/add{{/pix}} {{#str}}addcompetency, tool_lp{{/str}}</button>
+        </div>
+    </div>
+    {{/plan.canbeedited}}
     <div data-region="plan-summary">
         <dl>
             <dt>{{#str}}status, tool_lp{{/str}}</dt>
                 <dd>{{{plan.description}}}</dd>
             {{/description}}
             <dt>{{#str}}progress, tool_lp{{/str}}</dt>
-            <dd><span class="progresstext">{{#str}}xcompetenciesproficientoutofy, tool_lp, { "x": "{{proficientcompetencycount}}", "y": "{{competencycount}}" }{{/str}}</span>
-                <div class="progress">
-                    <div class="bar" style="width: {{proficientcompetencypercentage}}%;">
-                        {{proficientcompetencypercentageformatted}} %
+            <dd>
+                <div class="span4">
+                    <div class="progresstext">
+                        {{#str}}xcompetenciesproficientoutofy, tool_lp, { "x": "{{proficientcompetencycount}}", "y": "{{competencycount}}" }{{/str}}
+                    </div>
+                </div>
+                <div class="span4">
+                    <span class="pull-right label label-info">{{proficientcompetencypercentageformatted}} %</span>
+                    <div class="progress">
+                        <div class="bar" style="width: {{proficientcompetencypercentage}}%;"></div>
                     </div>
                 </div>
             </dd>
         {{/canpostorhascomments}}
     {{/plan.commentarea}}
     <div data-region="plan-competencies">
-        <div data-region="actions">
-            <div class="pull-right">
-                <!-- Button to add competencies to the plan -->
-                {{#plan.canbeedited}}
-                <button class="btn" data-action="add">{{#pix}}t/add{{/pix}} {{#str}}addcompetency, tool_lp{{/str}}</button>
-                {{/plan.canbeedited}}
-            </div>
-        </div>
         <h3>{{#str}}learningplancompetencies, tool_lp{{/str}}</h3>
         <table class="generaltable fullwidth managecompetencies">
             <thead>
             </thead>
             <tbody class="drag-parentnode">
                 {{#competencies}}
-                <tr class="drag-samenode" data-node="user-competency" data-id="{{competency.id}}" data-competencyid="{{usercompetency.competencyid}}" data-userid="{{usercompetency.userid}}">
+                <tr class="drag-samenode" data-node="user-competency" data-id="{{competency.id}}"
+                        data-competencyid="{{competency.id}}"
+                        data-userid="{{plan.userid}}">
                     <td>
                         {{#plan.canbeedited}}
                         <span class="drag-handlecontainer pull-left"></span>
                         {{/plan.canbeedited}}
-                        <a href="{{pluginbaseurl}}/user_competency_in_plan.php?competencyid={{competency.id}}&amp;userid={{plan.userid}}&amp;planid={{plan.id}}">{{competency.shortname}}</a>
+                        <a data-usercompetency="true" href="#">{{competency.shortname}}</a>
                         <em>{{competency.idnumber}}</em>
                         {{#comppath}}
                             <br>
     </div>
 </div>
 {{#js}}
-require(['tool_lp/competencies', 'tool_lp/planactions', 'tool_lp/user_competency_workflow'], function(mod, actionsMod, UserCompWorkflow) {
+require(['tool_lp/competencies', 'tool_lp/planactions', 'tool_lp/user_competency_workflow', 'tool_lp/user_competency_plan_popup'], function(mod, actionsMod, UserCompWorkflow, Popup) {
     var planActions = new actionsMod('plan');
 
     (new mod({{plan.id}}, 'plan', {{contextid}}));
+    (new Popup('[data-region=plan-page]', '[data-usercompetency=true]', {{plan.id}}));
     planActions.registerEvents();
 
     var ucw = new UserCompWorkflow();
index 6aee2d6..78d1c1d 100644 (file)
@@ -34,7 +34,7 @@
 }}
 
 <div data-region="plans">
-<div class="btn-group pull-right">
+<div class="btn-group pull-left">
     {{#navigation}}
     {{{.}}}
     {{/navigation}}
index 897a26e..f9b2c51 100644 (file)
@@ -24,7 +24,6 @@
 </div>
 {{#js}}
 require(['tool_lp/competencydialogue'], function(Compdialogue) {
-    var competencydialogue = new Compdialogue({includerelated : true});
-    competencydialogue.watch('[data-region="relatedcompetencies"]');
+    Compdialogue.init();
 });
 {{/js}}
index 715bf60..6095c71 100644 (file)
             <a href="{{pluginbaseurl}}/edittemplate.php?id={{template.id}}&amp;pagecontextid={{pagecontextid}}">{{#pix}}t/edit, core, {{#str}}edittemplate, tool_lp{{/str}}{{/pix}}</a>
         {{/template.canmanage}}
     </h2>
-    <h3>{{#str}}templatecompetencies, tool_lp{{/str}}</h3>
-    {{#statistics}}
-        {{> tool_lp/template_statistics }}
-    {{/statistics}}
     {{#canmanagetemplatecompetencies}}
     <div data-region="actions" class="clearfix">
-        <div class="pull-right">
+        <div class="pull-left">
             <button disabled>{{#str}}addtemplatecompetencies, tool_lp{{/str}}</button>
         </div>
     </div>
     {{/canmanagetemplatecompetencies}}
+    <h3>{{#str}}templatecompetencies, tool_lp{{/str}}</h3>
+    {{#statistics}}
+        {{> tool_lp/template_statistics }}
+    {{/statistics}}
     <div data-region="templatecompetencies">
         <div class="managecompetencies">
             <div class="drag-parentnode">
@@ -48,7 +48,6 @@
                         </div>
                         {{/canmanagetemplatecompetencies}}
                         {{#competency}}
-                        <a id="competency-info-link-{{competency.idnumber}}"></a>
                             {{> tool_lp/competency_summary }}
                         {{/competency}}
                         <strong>{{#str}}linkedcourseslist, tool_lp{{/str}}</strong>
index c344dde..66caf50 100644 (file)
 }}
 {{#competencycount}}
 <div data-region="templatestatistics" class="well">
-    <div>
-        <div class="progresstext">
-            {{#str}}xcompetencieslinkedoutofy, tool_lp, { "x": "{{linkedcompetencycount}}", "y": "{{competencycount}}" } {{/str}}
+    <div class="clearfix">
+        <div class="span4">
+            <div class="progresstext">
+                {{#str}}xcompetencieslinkedoutofy, tool_lp, { "x": "{{linkedcompetencycount}}", "y": "{{competencycount}}" } {{/str}}
+            </div>
         </div>
-        <div class="progress">
-            <div class="bar" style="width: {{linkedcompetencypercentage}}%;">
-                {{linkedcompetencypercentageformatted}} %
+        <div class="span6">
+            <span class="pull-right label label-info">{{linkedcompetencypercentageformatted}} %</span>
+            <div class="progress">
+                <div class="bar" style="width: {{linkedcompetencypercentage}}%;"></div>
             </div>
         </div>
     </div>
     {{#plancount}}
-    <div>
-        <div class="progresstext">
-            {{#str}}xplanscompletedoutofy, tool_lp, { "x": "{{completedplancount}}", "y": "{{plancount}}" } {{/str}}
+    <div class="clearfix">
+        <div class="span4">
+            <div class="progresstext">
+                {{#str}}xplanscompletedoutofy, tool_lp, { "x": "{{completedplancount}}", "y": "{{plancount}}" } {{/str}}
+            </div>
         </div>
-        <div class="progress">
-            <div class="bar" style="width: {{completedplanpercentage}}%;">
-                {{completedplanpercentageformatted}} %
+        <div class="span6">
+            <span class="pull-right label label-info">{{completedplanpercentageformatted}} %</span>
+            <div class="progress">
+                <div class="bar" style="width: {{completedplanpercentage}}%;">
+
+                </div>
             </div>
         </div>
     </div>
     {{/plancount}}
     {{#usercompetencyplancount}}
-    <div>
-        <div class="progresstext">
-            {{#str}}averageproficiencyrate, tool_lp, {{proficientusercompetencyplanpercentageformatted}} {{/str}}
+    <div  class="clearfix">
+        <div class="span4">
+            <div class="progresstext">
+                {{#str}}averageproficiencyrate, tool_lp, {{proficientusercompetencyplanpercentageformatted}} {{/str}}
+            </div>
         </div>
-        <div class="progress">
-            <div class="bar" style="width: {{proficientusercompetencyplanpercentage}}%;">
-                {{proficientusercompetencyplanpercentageformatted}} %
+        <div class="span6">
+            <span class="pull-right label label-info">{{proficientusercompetencyplanpercentageformatted}} %</span>
+            <div class="progress">
+                <div class="bar" style="width: {{proficientusercompetencyplanpercentage}}%;"></div>
             </div>
         </div>
     </div>
@@ -88,7 +99,7 @@
         <div>
             {{#leastproficient}}
                 {{#showcompetencylinks}}
-                <a href="#competency-info-link-{{idnumber}}">
+                <a href="#competency_link_{{id}}">
                 {{/showcompetencylinks}}
                 <div><p>{{shortname}} <em>{{idnumber}}</em></p></div>
                 {{#showcompetencylinks}}
index 4567a47..6ebc4c1 100644 (file)
@@ -3,7 +3,7 @@
 <form class="user-competency-course-navigation">
 {{#hasusers}}
 <span>
-<label for="user-nav-{{uniqid}}" class="accesshide">{{#str}}jumptouser, tool_lp{{/str}}</label>
+<label for="user-nav-{{uniqid}}" class="accesshide">{{jumptouser}}</label>
 <select id="user-nav-{{uniqid}}">
 {{#users}}
 <option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{fullname}}</option>
@@ -14,7 +14,7 @@
 <br>
 {{#hascompetencies}}
 <span>
-<label for="competency-nav-{{uniqid}}" class="accesshide">{{#str}}jumptocompetency, tool_lp{{/str}}</label>
+<label for="competency-nav-{{uniqid}}" class="accesshide">{{jumptocompetency}}</label>
 <select id="competency-nav-{{uniqid}}">
 {{#competencies}}
 <option value="{{id}}" {{#selected}}selected="selected"{{/selected}}>{{shortname}} {{idnumber}}</option>
 require(['core/form-autocomplete', 'tool_lp/user_competency_course_navigation'], function(autocomplete, nav) {
     (new nav('#user-nav-{{uniqid}}', '#competency-nav-{{uniqid}}', '{{baseurl}}', {{userid}}, {{competencyid}}, {{courseid}}));
 {{#hasusers}}
-    autocomplete.enhance('#user-nav-{{uniqid}}', false, false, '{{#str}}jumptouser, tool_lp{{/str}}');
+    autocomplete.enhance('#user-nav-{{uniqid}}', false, false, '{{jumptouser}}');
 {{/hasusers}}
 {{#hascompetencies}}
-    autocomplete.enhance('#competency-nav-{{uniqid}}', false, false, '{{#str}}jumptocompetency, tool_lp{{/str}}');
+    autocomplete.enhance('#competency-nav-{{uniqid}}', false, false, '{{jumptocompetency}}');
 {{/hascompetencies}}
 
 });
index 6c095da..4b11153 100644 (file)
@@ -33,7 +33,7 @@
 }}
 
 <div data-region="user-evidence-list">
-<div class="btn-group pull-right">
+<div class="btn-group pull-left">
     {{#navigation}}
         {{{.}}}
     {{/navigation}}
                                 {{#pix}}t/edit{{/pix}} {{#str}}editthisuserevidence, tool_lp{{/str}}
                             </a>
                         </li>
+                        {{#userhasplan}}
                         <li>
                             <a href="#" data-action="link-competency">
                                 {{#pix}}t/add{{/pix}} {{#str}}linkcompetencies, tool_lp{{/str}}
                             </a>
                         </li>
+                        {{/userhasplan}}
                         <li>
                             <a data-action="send-competencies-review" href="#">
                                 {{#pix}}t/edit{{/pix}} {{#str}}sendcompetenciestoreview, tool_lp{{/str}}
index 7e873f3..40876be 100644 (file)
             <a href="{{pluginbaseurl}}/user_evidence_edit.php?id={{id}}&amp;userid={{userid}}">{{#pix}}t/edit, core, {{#str}}editthisuserevidence, tool_lp{{/str}}{{/pix}}</a>
         {{/canmanage}}
     </h2>
+    {{#canmanage}}
+    <div data-region="actions" class="clearfix">
+        <div class="pull-left">
+            {{#userhasplan}}
+                <button class="btn" data-action="link-competency">{{#pix}}t/add{{/pix}} {{#str}}linkcompetencies, tool_lp{{/str}}</button>
+            {{/userhasplan}}
+        </div>
+    </div>
+    {{/canmanage}}
 
     <div data-region="user-evidence-summary">
         {{#description}}
                 {{/usercompetencies}}
             </tbody>
         </table>
-
-        <div data-region="actions">
-            <div class="pull-right">
-                {{#canmanage}}
-                    <button class="btn" data-action="link-competency">{{#pix}}t/add{{/pix}} {{#str}}linkcompetencies, tool_lp{{/str}}</button>
-                {{/canmanage}}
-            </div>
-        </div>
     </div>
 </div>
 
@@ -109,8 +110,7 @@ require(['tool_lp/user_evidence_actions'], function(UserEvidenceActions) {
 
 {{#js}}
 require(['tool_lp/competencydialogue'], function(Compdialogue) {
-    var competencydialogue = new Compdialogue({includerelated : true});
-    competencydialogue.watch('[data-region="user-evidence-competencies"]');
+    Compdialogue.init();
 });
 {{/js}}
 
index 6b0bf40..0a5fea5 100644 (file)
@@ -25,8 +25,6 @@
 
 require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
 
-use Behat\Behat\Context\Step\Given as Given;
-
 /**
  * Step definition for learning plan system.
  *
@@ -44,17 +42,12 @@ class behat_tool_lp extends behat_base {
      *
      * @param string $nodetext
      * @param string $rowname
-     * @return Given[] array of steps
      */
     public function click_on_edit_menu_of_the_row($nodetext, $rowname) {
-        $steps = array();
-
-        $xpathtarget = "//ul//li//ul//li[@class='tool-lp-menu-item']//a[contains(.,'". $nodetext ."')]";
+        $xpathtarget = "//ul//li//ul//li[@class='tool-lp-menu-item']//a[contains(.,'" . $nodetext . "')]";
 
-        $steps[] = new Given('I click on "Edit" "link" in the "' . $this->escape($rowname) . '" "table_row"');
-        $steps[] = new Given('I click on "'.$xpathtarget.'" "xpath_element" in the "' . $this->escape($rowname) . '" "table_row"');
-
-        return $steps;
+        $this->execute('behat_general::i_click_on_in_the', [get_string('edit'), 'link', $this->escape($rowname), 'table_row']);
+        $this->execute('behat_general::i_click_on_in_the', [$xpathtarget, 'xpath_element', $this->escape($rowname), 'table_row']);
     }
 
     /**
@@ -63,16 +56,11 @@ class behat_tool_lp extends behat_base {
      * @Given /^I select "([^"]*)" of the competency tree$/
      *
      * @param string $competencyname
-     * @return Given[] array of steps
      */
     public function select_of_the_competency_tree($competencyname) {
-        $steps = array();
-
-        $xpathtarget = "//li[@role='tree-item']//span[contains(.,'". $competencyname ."')]";
+        $xpathtarget = "//li[@role='tree-item']//span[contains(.,'" . $competencyname . "')]";
 
-        $steps[] = new Given('I click on "' . $xpathtarget . '" "xpath_element"');
-
-        return $steps;
+        $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']);
     }
 
     /**
@@ -81,15 +69,10 @@ class behat_tool_lp extends behat_base {
      * @Given /^I click on "([^"]*)" item in the autocomplete list$/
      *
      * @param string $item
-     * @return Given[] array of steps
      */
     public function i_click_on_item_in_the_autocomplete_list($item) {
-        $steps = array();
-
-        $xpathtarget = "//ul[@class='form-autocomplete-suggestions']//li//span//span[contains(.,'". $item ."')]";
-
-        $steps[] = new Given('I click on "' . $xpathtarget . '" "xpath_element"');
+        $xpathtarget = "//ul[@class='form-autocomplete-suggestions']//li//span//span[contains(.,'" . $item . "')]";
 
-        return $steps;
+        $this->execute('behat_general::i_click_on', [$xpathtarget, 'xpath_element']);
     }
 }
index a77dcd0..27b6f95 100644 (file)
@@ -9,18 +9,18 @@ Feature: Manage competency frameworks
     And I am on site homepage
     And I expand "Site administration" node
     When I expand "Competencies" node
-    Then I should see "Competency Frameworks"
+    Then I should see "Competency frameworks"
 
   Scenario: Create a new framework
-    Given I follow "Competency Frameworks"
+    Given I follow "Competency frameworks"
     And I should see "List of competency frameworks"
     And I click on "Add new competency framework" "button"
     And I should see "General"
     And I should see "Taxonomies"
     And I set the field "Name" to "Science Year-1"
-    And I set the field "Id number" to "Comp-frm-1"
+    And I set the field "ID number" to "Comp-frm-1"
     And I press "Save changes"
-    And I should see "You must configure the scale by selecting default and proficient values"
+    And I should see "The scale needs to be configured by selecting default and proficient items."
     And "Configure scales" "button" should be visible
     And I press "Configure scales"
     And I click on "//input[@data-field='tool_lp_scale_default_1']" "xpath_element"
@@ -34,7 +34,7 @@ Feature: Manage competency frameworks
     Given the following lp "frameworks" exist:
       | shortname | idnumber |
       | Science Year-2 | sc-y-2 |
-    And I follow "Competency Frameworks"
+    And I follow "Competency frameworks"
     And I should see "Science Year-2"
     When I click on "Science Year-2" "link"
     Then I should see "Science Year-2"
@@ -43,7 +43,7 @@ Feature: Manage competency frameworks
     Given the following lp "frameworks" exist:
       | shortname | idnumber |
       | Science Year-3 | sc-y-3 |
-    And I follow "Competency Frameworks"
+    And I follow "Competency frameworks"
     And I should see "Science Year-3"
     And I click on "Edit" of edit menu in the "Science Year-3" row
     And the field "Name" matches value "Science Year-3 "
@@ -57,7 +57,7 @@ Feature: Manage competency frameworks
     Given the following lp "frameworks" exist:
       | shortname | idnumber |
       | Science Year-4 | sc-y-4 |
-    And I follow "Competency Frameworks"
+    And I follow "Competency frameworks"
     And I should see "Science Year-4"
     And I should see "sc-y-4"
     And I click on "Delete" of edit menu in the "Science Year-4" row
@@ -90,7 +90,7 @@ Scenario: Edit a framework with competencies in user competency
       | user | competency |
       | admin | Comp1 |
       | admin | Comp2 |
-    And I follow "Competency Frameworks"
+    And I follow "Competency frameworks"
     And I should see "Science Year-5"
     And I click on "Edit" of edit menu in the "Science Year-5" row
     And the field "Name" matches value "Science Year-5 "
@@ -120,7 +120,7 @@ Scenario: Edit a framework with competencies in user competency plan
       | user | competency | plan |
       | admin | Comp1 | Plan Science-6 |
       | admin | Comp2 | Plan Science-6 |
-    And I follow "Competency Frameworks"
+    And I follow "Competency frameworks"
     And I should see "Science Year-6"
     And I click on "Edit" of edit menu in the "Science Year-6" row
     And the field "Name" matches value "Science Year-6 "
index 82c6bd6..a03113d 100644 (file)
@@ -32,8 +32,8 @@ Feature: Manage plearning plan
     And I click on ".template-userplans" "css_element" in the "Science template" "table_row"
     And I click on ".form-autocomplete-downarrow" "css_element"
     And I click on "Admin" item in the autocomplete list
-    When I click on "Create plans" "button"
-    Then I should see "A plan was created"
+    When I click on "Create learning plans" "button"
+    Then I should see "A learning plan was created"
     And I should see "Admin User" in the "Science template" "table_row"
 
   Scenario: Create a learning plan from template cohort
@@ -59,7 +59,7 @@ Feature: Manage plearning plan
     And I click on ".form-autocomplete-downarrow" "css_element"
     And I click on "cohort plan" item in the autocomplete list
     When I click on "Add cohorts" "button"
-    Then I should see "2 plans were created."
+    Then I should see "2 learning plans were created."
     And I follow "Learning plan templates"
     And I click on ".template-userplans" "css_element" in the "Science template cohort" "table_row"
     And I should see "Student 1"
index a6a3f5d..2686517 100644 (file)
@@ -30,7 +30,7 @@ Feature: Manage plearning plan templates
     And I should see "Science template Year-2"
     When I click on "Science template Year-2" "link"
     Then I should see "Science template Year-2"
-    And I should see "Template competencies"
+    And I should see "Learning plan template competencies"
 
   Scenario: Edit a learning plan template
     Given the following lp "templates" exist:
index 1a1cd28..3a48a47 100644 (file)
@@ -46,7 +46,11 @@ list($title, $subtitle) = \tool_lp\page_helper::setup_for_plan($userid, $url, $p
 $output = $PAGE->get_renderer('tool_lp');
 echo $output->header();
 echo $output->heading($title);
+// User competency plan navigation.
+$baseurl = new moodle_url('/admin/tool/lp/user_competency_in_plan.php');
+$nav = new \tool_lp\output\competency_plan_navigation($userid, $competencyid, $planid, $baseurl);
 
+echo $output->render($nav);
 $page = new \tool_lp\output\user_competency_summary_in_plan($competencyid, $planid);
 echo $output->render($page);
 // Trigger the viewed event.
index f81d224..d9658aa 100644 (file)
@@ -31,6 +31,5 @@ $capabilities = array(
         'archetypes' => array(
             'manager' => CAP_ALLOW
         ),
-        'clonepermissionsfrom' => 'moodle/site:config'
     ),
 );
index 26c3b5d..347a749 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 $string['allowedcourses'] = 'Courses allowed';
-$string['allowedcourses_help'] = 'When at least one course is specified, the migration will only occur on the courses listed here.';
+$string['allowedcourses_help'] = 'Select courses to be migrated to the new framework. If no course is specified, then all courses will be migrated.';
 $string['continuetoframeworks'] = 'Continue to frameworks';
 $string['coursecompetencymigrations'] = 'Course competency migrations';
-$string['coursemodulecompetencymigrations'] = 'Course module competency migrations';
+$string['coursemodulecompetencymigrations'] = 'Course activity and resource competency migrations';
 $string['coursesfound'] = 'Courses found';
-$string['coursemodulesfound'] = 'Course modules found';
+$string['coursemodulesfound'] = 'Course activities or resources found';
 $string['coursestartdate'] = 'Courses start date';
-$string['coursestartdate_help'] = 'When enabled, courses with a start date prior to the one specified here will not be migrated.';
+$string['coursestartdate_help'] = 'If enabled, courses with a start date prior to the date specified will not be migrated.';
 $string['disallowedcourses'] = 'Disallowed courses';
-$string['disallowedcourses_help'] = 'Any course specified here will not be migrated.';
+$string['disallowedcourses_help'] = 'Select any courses which should NOT be migrated to the new framework.';
 $string['errorcannotmigratetosameframework'] = 'Cannot migrate from and to the same framework.';
 $string['errorcouldnotmapcompetenciesinframework'] = 'Could not map to any competency in this framework.';
 $string['errors'] = 'Errors';
 $string['errorwhilemigratingcoursecompetencywithexception'] = 'Error while migrating the course competency: {$a}';
-$string['errorwhilemigratingmodulecompetencywithexception'] = 'Error while migrating the course module competency: {$a}';
+$string['errorwhilemigratingmodulecompetencywithexception'] = 'Error while migrating the activity or resource competency: {$a}';
 $string['excludethese'] = 'Exclude these';
-$string['explanation'] = 'When updating a competency framework to a newer version, this tool can be used to search for competencies in courses and course modules using the older framework, and update the links to point to the new framework. This works by finding competencies with matching ID numbers in the two frameworks, and then mapping all of the competency links in the specified list of courses to point to the new version of the competency in the new framework. It is not recommended to edit the old set of competencies directly as this would change all of the competencies that have already been awarded in users learning plans. Typically you would import the new version of a framework, hide the old framework, and use this tool to migrate new courses to the new framework.';
+$string['explanation'] = 'This tool can be used to update a competency framework to a newer version. It searches for competencies in courses and activities using the older framework, and updates the links to point to the new framework.
+
+It is not recommended to edit the old set of competencies directly, as this would change all of the competencies that have already been awarded in users\' learning plans.
+
+Typically you would import the new version of a framework, hide the old framework, then use this tool to migrate new courses to the new framework.';
 $string['findingcoursecompetencies'] = 'Finding course competencies';
-$string['findingmodulecompetencies'] = 'Finding module competencies';
+$string['findingmodulecompetencies'] = 'Finding activity and resource competencies';
 $string['frameworks'] = 'Frameworks';
 $string['limittothese'] = 'Limit to these';
 $string['lpmigrate:frameworksmigrate'] = 'Migrate frameworks';
 $string['migrateframeworks'] = 'Migrate frameworks';
 $string['migratefrom'] = 'Migrate from';
-$string['migratefrom_help'] = 'Select the framework to migrate from.';
+$string['migratefrom_help'] = 'Select the older framework currently in use.';
 $string['migratemore'] = 'Migrate more';
 $string['migrateto'] = 'Migrate to';
-$string['migrateto_help'] = 'Select the framework to migrate to. Note that it must be not be hidden.';
+$string['migrateto_help'] = 'Select the newer version of the framework. It is only possible to select a framework which is not hidden.';
 $string['migratingcourses'] = 'Migrating courses';
 $string['missingmappings'] = 'Missing mappings';
 $string['performmigration'] = 'Perform migration';
@@ -61,7 +65,7 @@ $string['results'] = 'Results';
 $string['startdatefrom'] = 'Start date from';
 $string['unmappedin'] = 'Unmapped in {$a}';
 $string['warningcouldnotremovecoursecompetency'] = 'The course competency could not be removed.';
-$string['warningcouldnotremovemodulecompetency'] = 'The course module competency could not be removed.';
+$string['warningcouldnotremovemodulecompetency'] = 'The activity or resource competency could not be removed.';
 $string['warningdestinationcoursecompetencyalreadyexists'] = 'The destination course competency already exists.';
-$string['warningdestinationmodulecompetencyalreadyexists'] = 'The destination course module competency already exists.';
+$string['warningdestinationmodulecompetencyalreadyexists'] = 'The destination activity or resource competency already exists.';
 $string['warnings'] = 'Warnings';
index fc6e074..a5527c2 100644 (file)
     <h4>{{#str}}unmappedin, tool_lpmigrate, {{frameworkfrom.shortname}}{{/str}}</h4>
     <ul>
         {{#unmappedfrom}}
-            <li><a href="#" data-id="{{id}}" data-action="competency-dialogue">{{shortname}}</a> <em>{{idnumber}}</em></li>
+            <li><a href="#" data-id="{{id}}" data-action="competency-dialogue" data-includecourses="true">{{shortname}}</a> <em>{{idnumber}}</em></li>
         {{/unmappedfrom}}
     </ul>
     {{/hasunmappedfrom}}
     <h4>{{#str}}unmappedin, tool_lpmigrate, {{frameworkto.shortname}}{{/str}}</h4>
     <ul>
         {{#unmappedto}}
-            <li><a href="#" data-id="{{id}}" data-action="competency-dialogue">{{shortname}}</a> <em>{{idnumber}}</em></li>
+            <li><a href="#" data-id="{{id}}" data-action="competency-dialogue" data-includecourses="true">{{shortname}}</a> <em>{{idnumber}}</em></li>
         {{/unmappedto}}
     </ul>
     {{/hasunmappedto}}
             <li>
                 <p>
                     {{message}}<br>
-                    <small>{{course}} {{#cm}} - {{cm}} {{/cm}} - <a href="#" data-id="{{competencyid}}" data-action="competency-dialogue">{{competency}}</a></small>
+                    <small>{{course}} {{#cm}} - {{cm}} {{/cm}} - <a href="#" data-id="{{competencyid}}" data-action="competency-dialogue" data-includecourses="true">{{competency}}</a></small>
                 </p>
             </li>
         {{/warnings}}
             <li>
                 <p>
                     {{message}}<br>
-                    <small>{{course}} {{#cm}} - {{cm}} {{/cm}} - <a href="#" data-id="{{competencyid}}" data-action="competency-dialogue">{{competency}}</a></small>
+                    <small>{{course}} {{#cm}} - {{cm}} {{/cm}} - <a href="#" data-id="{{competencyid}}" data-action="competency-dialogue" data-includecourses="true">{{competency}}</a></small>
                 </p>
             </li>
         {{/errors}}
     </p>
 </div>
 {{#js}}
-require(['tool_lp/competencydialogue'], function(CompDialogue) {
-    var cd = new CompDialogue({includecourses: true, includerelated: true});
-    cd.watch('[data-region="migration-results"]');
-})
+require(['tool_lp/competencydialogue'], function(Compdialogue) {
+    Compdialogue.init();
+});
 {{/js}}
index aaab461..ed5e303 100644 (file)
@@ -1119,8 +1119,10 @@ if ($formdata = $mform2->is_cancelled()) {
     }
     $upt->close(); // close table
     if (!empty($validation)) {
-        foreach ($validation as $username => $error) {
-            \core\notification::warning(get_string('invaliduserdata', 'tool_uploaduser', s($username)));
+        foreach ($validation as $username => $result) {
+            if ($result !== true) {
+                \core\notification::warning(get_string('invaliduserdata', 'tool_uploaduser', s($username)));
+            }
         }
     }
     $cir->close();
index cc958c2..b01b78b 100644 (file)
@@ -28,768 +28,6 @@ class webservice_test_client_form extends moodleform {
 
 // === Test client forms ===
 
-class moodle_user_create_users_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-
-        /// specific to the create users function
-        $mform->addElement('text', 'username', 'username');
-        $mform->setType('username', core_user::get_property_type('username'));
-        $mform->addElement('text', 'password', 'password');
-        $mform->setType('password', core_user::get_property_type('password'));
-        $mform->addElement('text', 'firstname', 'firstname');
-        $mform->setType('firstname', core_user::get_property_type('firstname'));
-        $mform->addElement('text', 'lastname', 'lastname');
-        $mform->setType('lastname', core_user::get_property_type('lastname'));
-        $mform->addElement('text', 'email', 'email');
-        $mform->setType('email', core_user::get_property_type('email'));
-
-        $mform->addElement('text', 'customfieldtype', 'customfieldtype');
-        $mform->setType('customfieldtype', PARAM_RAW);
-        $mform->addElement('text', 'customfieldvalue', 'customfieldvalue');
-        $mform->setType('customfieldvalue', PARAM_RAW);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-
-        //set customfields
-        if (!empty($data->customfieldtype)) {
-            $data->customfields = array(array('type' => $data->customfieldtype, 'value' => $data->customfieldvalue));
-        }
-
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-        unset($data->customfieldtype);
-        unset($data->customfieldvalue);
-
-        $params = array();
-        $params['users'] = array();
-        $params['users'][] = (array)$data;
-
-        return $params;
-    }
-}
-
-
-class moodle_user_update_users_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-
-        /// specific to the create users function
-        $mform->addElement('text', 'id', 'id');
-        $mform->addRule('id', get_string('required'), 'required', null, 'client');
-        $mform->setType('id', core_user::get_property_type('id'));
-        $mform->addElement('text', 'username', 'username');
-        $mform->setType('username', core_user::get_property_type('username'));
-        $mform->addElement('text', 'password', 'password');
-        $mform->setType('password', core_user::get_property_type('password'));
-        $mform->addElement('text', 'firstname', 'firstname');
-        $mform->setType('firstname', core_user::get_property_type('firstname'));
-        $mform->addElement('text', 'lastname', 'lastname');
-        $mform->setType('lastname', core_user::get_property_type('lastname'));
-        $mform->addElement('text', 'email', 'email');
-        $mform->setType('email', core_user::get_property_type('email'));
-
-
-        $mform->addElement('text', 'customfieldtype', 'customfieldtype');
-        $mform->setType('customfieldtype', PARAM_RAW);
-        $mform->addElement('text', 'customfieldvalue', 'customfieldvalue');
-        $mform->setType('customfieldvalue', PARAM_RAW);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-
-        //set customfields
-        if (!empty($data->customfieldtype)) {
-            $data->customfields = array(array('type' => $data->customfieldtype, 'value' => $data->customfieldvalue));
-        }
-
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-        unset($data->customfieldtype);
-        unset($data->customfieldvalue);
-
-        foreach($data as $key => $value) {
-            if (empty($value)) {
-                 unset($data->{$key});
-            }
-        }
-
-        $params = array();
-        $params['users'] = array();
-        $params['users'][] = (array)$data;
-
-        return $params;
-    }
-}
-
-
-class moodle_user_delete_users_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-
-        /// beginning of specific code to the create users function
-        $mform->addElement('text', 'userids[0]', 'userids[0]');
-        $mform->addElement('text', 'userids[1]', 'userids[1]');
-        $mform->addElement('text', 'userids[2]', 'userids[2]');
-        $mform->addElement('text', 'userids[3]', 'userids[3]');
-        $mform->setType('userids', core_user::get_property_type('id'));
-        /// end of specific code to the create users function
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        ///  beginning of specific code to the create users form
-        $params = array();
-        $params['userids'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->userids[$i])) {
-                continue;
-            }
-            $params['userids'][] = $data->userids[$i];
-        }
-        /// end of specific code to the create users function
-
-        return $params;
-    }
-}
-
-
-class moodle_user_get_users_by_id_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-
-        /// beginning of specific code to the create users function
-        $mform->addElement('text', 'userids[0]', 'userids[0]');
-        $mform->addElement('text', 'userids[1]', 'userids[1]');
-        $mform->addElement('text', 'userids[2]', 'userids[2]');
-        $mform->addElement('text', 'userids[3]', 'userids[3]');
-        $mform->setType('userids', core_user::get_property_type('id'));
-        /// end of specific code to the create users function
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        ///  beginning of specific code to the create users form
-        $params = array();
-        $params['userids'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->userids[$i])) {
-                continue;
-            }
-            $params['userids'][] = $data->userids[$i];
-        }
-        /// end of specific code to the create users function
-
-        return $params;
-    }
-}
-
-class moodle_group_create_groups_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-
-        $mform->addElement('text', 'courseid', 'courseid');
-        $mform->setType('courseid', PARAM_INT);
-        $mform->addElement('text', 'name', 'name');
-        $mform->setType('name', PARAM_TEXT);
-        $mform->addElement('text', 'description', 'description');
-        $mform->setType('description', PARAM_TEXT);
-        $mform->addElement('text', 'enrolmentkey', 'enrolmentkey');
-        $mform->setType('enrolmentkey', PARAM_RAW);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['groups'] = array();
-        $params['groups'][] = (array)$data;
-
-        return $params;
-    }
-}
-
-class moodle_group_get_groups_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'groupids[0]', 'groupids[0]');
-        $mform->addElement('text', 'groupids[1]', 'groupids[1]');
-        $mform->addElement('text', 'groupids[2]', 'groupids[2]');
-        $mform->addElement('text', 'groupids[3]', 'groupids[3]');
-        $mform->setType('groupids', PARAM_INT);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['groupids'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->groupids[$i])) {
-                continue;
-            }
-            $params['groupids'][] = $data->groupids[$i];
-        }
-
-        return $params;
-    }
-}
-
-class moodle_group_get_course_groups_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'courseid', 'courseid');
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['courseid'] = $data->courseid;
-
-        return $params;
-    }
-}
-
-class moodle_group_delete_groups_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'groupids[0]', 'groupids[0]');
-        $mform->addElement('text', 'groupids[1]', 'groupids[1]');
-        $mform->addElement('text', 'groupids[2]', 'groupids[2]');
-        $mform->addElement('text', 'groupids[3]', 'groupids[3]');
-        $mform->setType('groupids', PARAM_INT);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['groupids'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->groupids[$i])) {
-                continue;
-            }
-            $params['groupids'][] = $data->groupids[$i];
-        }
-
-        return $params;
-    }
-}
-
-class moodle_group_get_groupmembers_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'groupids[0]', 'groupids[0]');
-        $mform->addElement('text', 'groupids[1]', 'groupids[1]');
-        $mform->addElement('text', 'groupids[2]', 'groupids[2]');
-        $mform->addElement('text', 'groupids[3]', 'groupids[3]');
-        $mform->setType('groupids', PARAM_INT);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['groupids'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->groupids[$i])) {
-                continue;
-            }
-            $params['groupids'][] = $data->groupids[$i];
-        }
-
-        return $params;
-    }
-}
-
-class moodle_group_add_groupmembers_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'userid[0]', 'userid[0]');
-        $mform->addElement('text', 'groupid[0]', 'groupid[0]');
-        $mform->addElement('text', 'userid[1]', 'userid[1]');
-        $mform->addElement('text', 'groupid[1]', 'groupid[1]');
-        $mform->setType('userid', core_user::get_property_type('id'));
-        $mform->setType('groupids', PARAM_INT);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['members'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->groupid[$i]) or empty($data->userid[$i])) {
-                continue;
-            }
-            $params['members'][] = array('userid'=>$data->userid[$i], 'groupid'=>$data->groupid[$i]);
-        }
-
-        return $params;
-    }
-}
-
-class moodle_group_delete_groupmembers_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->setType('wsusername', core_user::get_property_type('username'));
-            $mform->addElement('text', 'wspassword', 'wspassword');
-            $mform->setType('wspassword', core_user::get_property_type('password'));
-        } else if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-            $mform->setType('token', PARAM_RAW_TRIMMED);
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', core_user::get_property_type('auth'));
-        $mform->addElement('text', 'userid[0]', 'userid[0]');
-        $mform->addElement('text', 'groupid[0]', 'groupid[0]');
-        $mform->addElement('text', 'userid[1]', 'userid[1]');
-        $mform->addElement('text', 'groupid[1]', 'groupid[1]');
-        $mform->setType('userid', PARAM_INT);
-        $mform->setType('groupids', PARAM_INT);
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_PLUGIN);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_ALPHA);
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['members'] = array();
-        for ($i=0; $i<10; $i++) {
-            if (empty($data->groupid[$i]) or empty($data->userid[$i])) {
-                continue;
-            }
-            $params['members'][] = array('userid'=>$data->userid[$i], 'groupid'=>$data->groupid[$i]);
-        }
-
-        return $params;
-    }
-}
-
 /**
  * Form class for create_categories() web service function test.
  *
index ac3e645..7a9ae73 100644 (file)
@@ -45,7 +45,7 @@ $string['auth_dbname'] = 'Name of the database itself. Leave empty if using an O
 $string['auth_dbname_key'] = 'DB name';
 $string['auth_dbpass'] = 'Password matching the above username';
 $string['auth_dbpass_key'] = 'Password';
-$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using. MD5 hashing is useful for connecting to other common web applications like PostNuke.</p> <p>Use \'internal\' if you want to the external DB to manage usernames &amp; email addresses, but Moodle to manage passwords. If you use \'internal\', you <i>must</i> provide a populated email address field in the external DB, and you must execute both admin/cron.php and auth/db/cli/sync_users.php regularly. Moodle will send an email to new users with a temporary password.</p>';
+$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using. MD5 hashing is useful for connecting to other common web applications like PostNuke.</p> <p>Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you <i>must</i> provide a populated email address field in the external database, and you must execute both admin/cron.php and auth/db/cli/sync_users.php regularly. Moodle will send an email to new users with a temporary password.</p>';
 $string['auth_dbpasstype_key'] = 'Password format';
 $string['auth_dbreviveduser'] = 'Revived user {$a->name} id {$a->id}';
 $string['auth_dbrevivedusererror'] = 'Error reviving user {$a}';
index 349dd13..7ca3fb2 100644 (file)
@@ -56,7 +56,7 @@ $string['auth_ldap_gracelogins_desc'] = 'Enable LDAP gracelogin support. After p
 $string['auth_ldap_gracelogins_key'] = 'Grace logins';
 $string['auth_ldap_groupecreators'] = 'List of groups or contexts whose members are allowed to create groups. Separate multiple groups with \';\'. Usually something like \'cn=teachers,ou=staff,o=myorg\'';
 $string['auth_ldap_groupecreators_key'] = 'Group creators';
-$string['auth_ldap_host_url'] = 'Specify LDAP host in URL-form like \'ldap://ldap.myorg.com/\' or \'ldaps://ldap.myorg.com/\' Separate multipleservers with \';\' to get failover support.';
+$string['auth_ldap_host_url'] = 'Specify LDAP host in URL-form like \'ldap://ldap.myorg.com/\' or \'ldaps://ldap.myorg.com/\'. Separate multiple servers with \';\' to get failover support.';
 $string['auth_ldap_host_url_key'] = 'Host URL';
 $string['auth_ldap_changepasswordurl_key'] = 'Password-change URL';
 $string['auth_ldap_ldap_encoding'] = 'Specify encoding used by LDAP server. Most probably utf-8, MS AD v2 uses default platform encoding such as cp1252, cp1250, etc.';
diff --git a/auth/lti/auth.php b/auth/lti/auth.php
new file mode 100644 (file)
index 0000000..b6eee11
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * LTI Authentication plugin.
+ *
+ * @package auth_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir.'/authlib.php');
+
+/**
+ * LTI Authentication plugin.
+ *
+ * @package auth_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class auth_plugin_lti extends auth_plugin_base {
+
+    /**
+     * Constructor.
+     */
+    public function __construct() {
+        $this->authtype = 'lti';
+    }
+
+    /**
+     * Users can not log in via the traditional login form.
+     *
+     * @param string $username The username
+     * @param string $password The password
+     * @return bool Authentication success or failure
+     */
+    public function user_login($username, $password) {
+        return false;
+    }
+}
diff --git a/auth/lti/lang/en/auth_lti.php b/auth/lti/lang/en/auth_lti.php
new file mode 100644 (file)
index 0000000..9b1d030
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'auth_lti', language 'en'.
+ *
+ * @package auth_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['auth_ltidescription'] = 'The LTI authentication plugin enables your site to behave as an LTI provider - this plugin works in conjunction with the LTI enrolment plugin by allowing external users to access a course or individual activities.';
+$string['pluginname'] = 'LTI';
diff --git a/auth/lti/version.php b/auth/lti/version.php
new file mode 100644 (file)
index 0000000..bee4bf0
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * LTI authentication plugin version information
+ *
+ * @package auth_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version = 2016040800; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->requires = 2016040700; // Requires this Moodle version (3.1).
+$plugin->component = 'auth_lti'; // Full name of the plugin (used for diagnostics).
index 9367cf3..2ace0f3 100644 (file)
@@ -1541,6 +1541,8 @@ class backup_activity_logstores_structure_step extends backup_course_logstores_s
 class backup_course_competencies_structure_step extends backup_structure_step {
 
     protected function define_structure() {
+        $userinfo = $this->get_setting_value('users');
+
         $wrapper = new backup_nested_element('course_competencies');
 
         $settings = new backup_nested_element('settings', array('id'), array('pushratingstouserplans'));
@@ -1554,11 +1556,11 @@ class backup_course_competencies_structure_step extends backup_structure_step {
         $competencies = new backup_nested_element('competencies');
         $wrapper->add_child($competencies);
 
-        $competency = new backup_nested_element('competency', null, array('idnumber', 'ruleoutcome',
-            'sortorder', 'frameworkidnumber'));
+        $competency = new backup_nested_element('competency', null, array('id', 'idnumber', 'ruleoutcome',
+            'sortorder', 'frameworkid', 'frameworkidnumber'));
         $competencies->add_child($competency);
 
-        $sql = 'SELECT c.idnumber, cc.ruleoutcome, cc.sortorder, f.idnumber AS frameworkidnumber
+        $sql = 'SELECT c.id, c.idnumber, cc.ruleoutcome, cc.sortorder, f.id AS frameworkid, f.idnumber AS frameworkidnumber
                   FROM {' . \core_competency\course_competency::TABLE . '} cc
                   JOIN {' . \core_competency\competency::TABLE . '} c ON c.id = cc.competencyid
                   JOIN {' . \core_competency\competency_framework::TABLE . '} f ON f.id = c.competencyframeworkid
@@ -1566,6 +1568,21 @@ class backup_course_competencies_structure_step extends backup_structure_step {
               ORDER BY cc.sortorder';
         $competency->set_source_sql($sql, array('courseid' => backup::VAR_COURSEID));
 
+        $usercomps = new backup_nested_element('user_competencies');
+        $wrapper->add_child($usercomps);
+        if ($userinfo) {
+            $usercomp = new backup_nested_element('user_competency', null, array('userid', 'competencyid',
+                'proficiency', 'grade'));
+            $usercomps->add_child($usercomp);
+
+            $sql = 'SELECT ucc.userid, ucc.competencyid, ucc.proficiency, ucc.grade
+                      FROM {' . \core_competency\user_competency_course::TABLE . '} ucc
+                     WHERE ucc.courseid = :courseid
+                       AND ucc.grade IS NOT NULL';
+            $usercomp->set_source_sql($sql, array('courseid' => backup::VAR_COURSEID));
+            $usercomp->annotate_ids('user', 'userid');
+        }
+
         return $wrapper;
     }
 }
index 2de6165..861dcae 100644 (file)
@@ -3066,10 +3066,15 @@ class restore_course_competencies_structure_step extends restore_structure_step
      * @return array
      */
     protected function define_structure() {
+        $userinfo = $this->get_setting_value('users');
         $paths = array(
             new restore_path_element('course_competency', '/course_competencies/competencies/competency'),
-            new restore_path_element('course_competency_settings', '/course_competencies/settings')
+            new restore_path_element('course_competency_settings', '/course_competencies/settings'),
         );
+        if ($userinfo) {
+            $paths[] = new restore_path_element('user_competency_course',
+                '/course_competencies/user_competencies/user_competency');
+        }
         return $paths;
     }
 
@@ -3080,22 +3085,27 @@ class restore_course_competencies_structure_step extends restore_structure_step
      */
     public function process_course_competency_settings($data) {
         global $DB;
-
         $data = (object) $data;
+
+        // We do not restore the course settings during merge.
+        $target = $this->get_task()->get_target();
+        if ($target == backup::TARGET_CURRENT_ADDING || $target == backup::TARGET_EXISTING_ADDING) {
+            return;
+        }
+
         $courseid = $this->task->get_courseid();
-        $exists = \core_competency\course_competency_settings::get_record(array('courseid' => $courseid));
+        $exists = \core_competency\course_competency_settings::record_exists_select('courseid = :courseid',
+            array('courseid' => $courseid));
 
-        // Now update or insert.
+        // Strangely the course settings already exist, let's just leave them as is then.
         if ($exists) {
-            $settings = $exists;
-            $settings->set_pushratingstouserplans($data->pushratingstouserplans);
-            return $settings->update();
-        } else {
-            $data = (object) array('courseid' => $courseid, 'pushratingstouserplans' => $data->pushratingstouserplans);
-            $settings = new \core_competency\course_competency_settings(0, $data);
-            $result = $settings->create();
-            return !empty($result);
+            $this->log('Course competency settings not restored, existing settings have been found.', backup::LOG_WARNING);
+            return;
         }
+
+        $data = (object) array('courseid' => $courseid, 'pushratingstouserplans' => $data->pushratingstouserplans);
+        $settings = new \core_competency\course_competency_settings(0, $data);
+        $settings->create();
     }
 
     /**
@@ -3116,6 +3126,7 @@ class restore_course_competencies_structure_step extends restore_structure_step
         if (!$competency) {
             return;
         }
+        $this->set_mapping(\core_competency\competency::TABLE, $data->id, $competency->get_id());
 
         $params = array(
             'competencyid' => $competency->get_id(),
@@ -3133,6 +3144,41 @@ class restore_course_competencies_structure_step extends restore_structure_step
         }
     }
 
+    /**
+     * Process the user competency course.
+     *
+     * @param array $data The data.
+     */
+    public function process_user_competency_course($data) {
+        global $USER, $DB;
+        $data = (object) $data;
+
+        $data->competencyid = $this->get_mappingid(\core_competency\competency::TABLE, $data->competencyid);
+        if (!$data->competencyid) {
+            // This is strange, the competency does not belong to the course.
+            return;
+        } else if ($data->grade === null) {
+            // We do not need to do anything when there is no grade.
+            return;
+        }
+
+        $data->userid = $this->get_mappingid('user', $data->userid);
+        $shortname = $DB->get_field('course', 'shortname', array('id' => $this->task->get_courseid()), MUST_EXIST);
+
+        // The method add_evidence also sets the course rating.
+        \core_competency\api::add_evidence($data->userid,
+                                           $data->competencyid,
+                                           $this->task->get_contextid(),
+                                           \core_competency\evidence::ACTION_OVERRIDE,
+                                           'evidence_courserestored',
+                                           'core_competency',
+                                           $shortname,
+                                           false,
+                                           null,
+                                           $data->grade,
+                                           $USER->id);
+    }
+
     /**
      * Execute conditions.
      *
@@ -3140,6 +3186,11 @@ class restore_course_competencies_structure_step extends restore_structure_step
      */
     protected function execute_condition() {
 
+        // Do not restore when competencies are disabled.
+        if (!\core_competency\api::is_enabled()) {
+            return false;
+        }
+
         // Do not execute if the competencies XML file is not found.
         $fullpath = $this->task->get_taskbasepath();
         $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
@@ -3210,6 +3261,11 @@ class restore_activity_competencies_structure_step extends restore_structure_ste
      */
     protected function execute_condition() {
 
+        // Do not restore when competencies are disabled.
+        if (!\core_competency\api::is_enabled()) {
+            return false;
+        }
+
         // Do not execute if the competencies XML file is not found.
         $fullpath = $this->task->get_taskbasepath();
         $fullpath = rtrim($fullpath, '/') . '/' . $this->filename;
index a2aedb6..b57d54d 100644 (file)
@@ -4,11 +4,6 @@ Feature: Block activity modules
   As a manager
   I can add activities block in a course or on the frontpage
 
-  Background:
-    Given I log in as "admin"
-    And I navigate to "Manage activities" node in "Site administration > Plugins > Activity modules"
-    And I click on "//a[@title=\"Show\"]" "xpath_element" in the "Feedback" "table_row"
-
   Scenario: Add activities block on the frontpage
     Given the following "activities" exist:
       | activity   | name                        | intro                              | course               | idnumber    |
@@ -34,8 +29,9 @@ Feature: Block activity modules
       | wiki       | Frontpage wiki name         | Frontpage wiki description         | Acceptance test site | wiki0       |
       | workshop   | Frontpage workshop name     | Frontpage workshop description     | Acceptance test site | workshop0   |
 
+    When I log in as "admin"
     And I am on site homepage
-    When I follow "Turn editing on"
+    And I follow "Turn editing on"
     And I add the "Activities" block
     And I click on "Assignments" "link" in the "Activities" "block"
     Then I should see "Frontpage assignment name"
@@ -112,7 +108,8 @@ Feature: Block activity modules
       | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
       | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
 
-    When I follow "Courses"
+    When I log in as "admin"
+    And I follow "Courses"
     And I follow "Course 1"
     And I turn editing mode on
     And I add the "Activities" block
index fd06623..add5034 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-if (is_file($CFG->dirroot.'/mod/feedback/lib.php')) {
-    require_once($CFG->dirroot.'/mod/feedback/lib.php');
-    define('FEEDBACK_BLOCK_LIB_IS_OK', true);
-}
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/mod/feedback/lib.php');
 
 class block_feedback extends block_list {
 
@@ -49,11 +48,6 @@ class block_feedback extends block_list {
         $this->content->icons = array();
         $this->content->footer = '';
 
-        if (!defined('FEEDBACK_BLOCK_LIB_IS_OK')) {
-            $this->content->items = array(get_string('missing_feedback_module', 'block_feedback'));
-            return $this->content;
-        }
-
         $courseid = $this->page->course->id;
         if ($courseid <= 0) {
             $courseid = SITEID;
index 559248b..737c9e3 100644 (file)
@@ -25,8 +25,5 @@
 function xmldb_block_feedback_install() {
     global $DB;
 
-/// Disable this block by default (because Feedback is not technically part of 2.0)
-    $DB->set_field('block', 'visible', 0, array('name'=>'feedback'));
-
 }
 
index 982755e..4999f02 100644 (file)
@@ -24,5 +24,4 @@
 
 $string['feedback'] = 'Feedback';
 $string['feedback:addinstance'] = 'Add a new feedback block';
-$string['missing_feedback_module'] = 'This blocks relies on the Feedback activity module, but that module is not present!';
 $string['pluginname'] = 'Feedback';
index 60a9aea..53fede6 100644 (file)
@@ -66,14 +66,17 @@ class block_lp extends block_base {
             return $this->content;
         }
 
-        $summary = new \block_lp\output\summary();
-        if (!$summary->has_content()) {
-            return $this->content;
-        }
+        // Block needs a valid, non-guest user to be logged-in in order to display the user's learning plans.
+        if (isloggedin() && !isguestuser()) {
+            $summary = new \block_lp\output\summary();
+            if (!$summary->has_content()) {
+                return $this->content;
+            }
 
-        $renderer = $this->page->get_renderer('block_lp');
-        $this->content->text = $renderer->render($summary);
-        $this->content->footer = '';
+            $renderer = $this->page->get_renderer('block_lp');
+            $this->content->text = $renderer->render($summary);
+            $this->content->footer = '';
+        }
 
         return $this->content;
     }
index 80b88ca..8470df8 100644 (file)
@@ -25,9 +25,9 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['competenciestoreview'] = 'Competencies to review';
-$string['lp:addinstance'] = 'Adding a new block';
-$string['lp:myaddinstance'] = 'Adding the block on the dashboard';
-$string['lp:view'] = 'Viewing the block';
+$string['lp:addinstance'] = 'Add a new learning plans block';
+$string['lp:myaddinstance'] = 'Add a new learning plans block to Dashboard';
+$string['lp:view'] = 'View learning plans block';
 $string['planstoreview'] = 'Plans to review';
 $string['pluginname'] = 'Learning plans';
 $string['viewmore'] = 'View more...';
index 9ecb706..84ec315 100644 (file)
@@ -508,9 +508,14 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
      * @return array
      */
     protected static function get_prefixed_keys(Memcached $connection, $prefix) {
+        $connkeys = $connection->getAllKeys();
+        if (empty($connkeys)) {
+            return array();
+        }
+
         $keys = array();
         $start = strlen($prefix);
-        foreach ($connection->getAllKeys() as $key) {
+        foreach ($connkeys as $key) {
             if (strpos($key, $prefix) === 0) {
                 $keys[] = substr($key, $start);
             }
index de015a4..467c592 100644 (file)
@@ -113,6 +113,9 @@ function cohort_delete_cohort($cohort) {
     $DB->delete_records('cohort_members', array('cohortid'=>$cohort->id));
     $DB->delete_records('cohort', array('id'=>$cohort->id));
 
+    // Notify the competency subsystem.
+    \core_competency\api::hook_cohort_deleted($cohort);
+
     $event = \core\event\cohort_deleted::create(array(
         'context' => context::instance_by_id($cohort->contextid),
         'objectid' => $cohort->id,
index 2a76ecd..a70ac33 100644 (file)
@@ -4706,6 +4706,19 @@ class api {
         $DB->delete_records(user_competency_course::TABLE, array('courseid' => $courseid));
     }
 
+    /**
+     * Action to perform when a cohort is deleted.
+     *
+     * Do not call this directly, this is reserved for core use.
+     *
+     * @param \stdClass $cohort The cohort object.
+     * @return void
+     */
+    public static function hook_cohort_deleted(\stdClass $cohort) {
+        global $DB;
+        $DB->delete_records(template_cohort::TABLE, array('cohortid' => $cohort->id));
+    }
+
     /**
      * Manually grade a user competency.
      *
index 101d29a..05f193d 100644 (file)
@@ -506,10 +506,6 @@ class competency extends persistent {
         } else if (!preg_match('@/([0-9]+/)+@', $value)) {
             // The format of the path is not correct.
             return new lang_string('invaliddata', 'error');
-
-        } else if ((substr_count($value, '/') - 1) > competency_framework::get_taxonomies_max_level()) {
-            // Validate the depth of the path.
-            return new lang_string('invaliddata', 'error');
         }
 
         return true;
@@ -697,6 +693,29 @@ class competency extends persistent {
         return $rules;
     }
 
+    /**
+     * Return the current depth of a competency framework.
+     *
+     * @param int $frameworkid The framework ID.
+     * @return int
+     */
+    public static function get_framework_depth($frameworkid) {
+        global $DB;
+        $totallength = $DB->sql_length('path');
+        $trimmedlength = $DB->sql_length("REPLACE(path, '/', '')");
+        $sql = "SELECT ($totallength - $trimmedlength - 1) AS depth
+                  FROM {" . self::TABLE . "}
+                 WHERE competencyframeworkid = :id
+              ORDER BY depth DESC";
+        $record = $DB->get_record_sql($sql, array('id' => $frameworkid), IGNORE_MULTIPLE);
+        if (!$record) {
+            $depth = 0;
+        } else {
+            $depth = $record->depth;
+        }
+        return $depth;
+    }
+
     /**
      * Build a framework tree with competency nodes.
      *
index 79d8979..5e51dcc 100644 (file)
@@ -133,6 +133,16 @@ class competency_framework extends persistent {
 
     }
 
+    /**
+     * Return the current depth of a competency framework.
+     *
+     * @see competency::get_framework_depth()
+     * @return int
+     */
+    public function get_depth() {
+        return competency::get_framework_depth($this->get_id());
+    }
+
     /**
      * Return the scale.
      *
@@ -176,8 +186,8 @@ class competency_framework extends persistent {
         unset($taxonomies[0]);
 
         // Ensure that we do not return empty levels.
-        for ($i = 1; $i <= self::get_taxonomies_max_level(); $i++) {
-            if (empty($taxonomies[$i])) {
+        foreach ($taxonomies as $i => $taxonomy) {
+            if (empty($taxonomy)) {
                 $taxonomies[$i] = self::TAXONOMY_COMPETENCY;
             }
         }
@@ -335,10 +345,6 @@ class competency_framework extends persistent {
     protected function validate_taxonomies($value) {
         $terms = explode(',', $value);
 
-        if (count($terms) > self::get_taxonomies_max_level()) {
-            return new lang_string('invaliddata', 'error');
-        }
-
         foreach ($terms as $term) {
             if (!empty($term) && !array_key_exists($term, self::get_taxonomies_list())) {
                 return new lang_string('invalidtaxonomy', 'core_competency', $term);
@@ -410,18 +416,6 @@ class competency_framework extends persistent {
         return self::get_taxonomies_list()[$constant];
     }
 
-    /**
-     * Return the maximum number of taxonomy levels.
-     *
-     * This is a method and not a constant because we want to make it easy to adapt
-     * to the number of levels desired in the future.
-     *
-     * @return int
-     */
-    public static function get_taxonomies_max_level() {
-        return 6;
-    }
-
     /**
      * Get the list of all taxonomies.
      *
index 3470643..50747d3 100644 (file)
@@ -110,6 +110,15 @@ class user_evidence extends persistent {
         return user_evidence_competency::get_user_competencies_by_userevidenceid($this->get_id());
     }
 
+    /**
+     * Return true if the user of the evidence has plan.
+     *
+     * @return bool
+     */
+    public function user_has_plan() {
+        return plan::record_exists_select('userid = ?', array($this->get_userid()));
+    }
+
     /**
      * Return the files associated with this evidence.
      *
index 9004d1f..d0f86eb 100644 (file)
@@ -3866,7 +3866,7 @@ class core_competency_api_testcase extends advanced_testcase {
         // Give permission to view competencies.
         $dg->role_assign($canviewucrole, $teacher1->id, $c1ctx->id);
         accesslib_clear_all_caches_for_unit_testing();
-        $this->assertExceptionWithGradeCompetencyInCourse('required_capability_exception', 'Set competency grade',
+        $this->assertExceptionWithGradeCompetencyInCourse('required_capability_exception', 'Set competency rating',
             $c1->id, $student1->id, $comp1->get_id());
 
         // Give permission to rate.
diff --git a/competency/tests/competency_test.php b/competency/tests/competency_test.php
new file mode 100644 (file)
index 0000000..caff731
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Competency tests.
+ *
+ * @package    core_competency
+ * @copyright  2016 Frédéric Massart - FMCorz.net
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
+use core_competency\competency;
+
+/**
+ * Competency testcase.
+ *
+ * @package    core_competency
+ * @copyright  2016 Frédéric Massart - FMCorz.net
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_competency_competency_testcase extends advanced_testcase {
+
+    public function test_get_framework_depth() {
+        $this->resetAfterTest();
+
+        $ccg = $this->getDataGenerator()->get_plugin_generator('core_competency');
+        $f1 = $ccg->create_framework();
+        $f2 = $ccg->create_framework();
+        $f3 = $ccg->create_framework();
+        $f4 = $ccg->create_framework();
+
+        $f1c1 = $ccg->create_competency(['competencyframeworkid' => $f1->get_id()]);
+        $f1c11 = $ccg->create_competency(['competencyframeworkid' => $f1->get_id(), 'parentid' => $f1c1->get_id()]);
+        $f1c111 = $ccg->create_competency(['competencyframeworkid' => $f1->get_id(), 'parentid' => $f1c11->get_id()]);
+        $f1c1111 = $ccg->create_competency(['competencyframeworkid' => $f1->get_id(), 'parentid' => $f1c111->get_id()]);
+
+        $f2c1 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id()]);
+        $f2c2 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id()]);
+        $f2c21 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c2->get_id()]);
+        $f2c22 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c2->get_id()]);
+        $f2c211 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c21->get_id()]);
+        $f2c221 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c22->get_id()]);
+        $f2c222 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c22->get_id()]);
+        $f2c223 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id(), 'parentid' => $f2c22->get_id()]);
+        $f2c3 = $ccg->create_competency(['competencyframeworkid' => $f2->get_id()]);
+
+        $f3c1 = $ccg->create_competency(['competencyframeworkid' => $f3->get_id()]);
+
+        $this->assertEquals(4, competency::get_framework_depth($f1->get_id()));
+        $this->assertEquals(3, competency::get_framework_depth($f2->get_id()));
+        $this->assertEquals(1, competency::get_framework_depth($f3->get_id()));
+        $this->assertEquals(0, competency::get_framework_depth($f4->get_id()));
+    }
+
+}
index 287ae50..f280e5f 100644 (file)
@@ -2670,7 +2670,7 @@ class core_competency_external_testcase extends externallib_advanced_testcase {
 
         $evidence = external::grade_competency_in_plan($plan->get_id(), $c1->get_id(), 1, 'Evil note');
 
-        $this->assertEquals('The competency grade was manually set in the plan \'Evil\'.', $evidence->description);
+        $this->assertEquals('The competency rating was manually set in the learning plan \'Evil\'.', $evidence->description);
         $this->assertEquals('A', $evidence->gradename);
         $this->assertEquals('Evil note', $evidence->note);
 
index 568b189..c6657c7 100644 (file)
@@ -179,4 +179,32 @@ class core_competency_hooks_testcase extends advanced_testcase {
         $this->assertEquals(2, user_competency_course::count_records(['courseid' => $c2->id, 'userid' => $u1->id]));
     }
 
+    public function test_hook_cohort_deleted() {
+        $this->resetAfterTest();
+        $this->setAdminUser();
+
+        $datagen = $this->getDataGenerator();
+        $corecompgen = $datagen->get_plugin_generator('core_competency');
+
+        $c1 = $datagen->create_cohort();
+        $c2 = $datagen->create_cohort();
+        $t1 = $corecompgen->create_template();
+        $t2 = $corecompgen->create_template();
+
+        // Create the template cohorts.
+        core_competency\api::create_template_cohort($t1->get_id(), $c1->id);
+        core_competency\api::create_template_cohort($t1->get_id(), $c2->id);
+        core_competency\api::create_template_cohort($t2->get_id(), $c1->id);
+
+        // Check that the association was made.
+        $this->assertEquals(2, \core_competency\template_cohort::count_records(array('templateid' => $t1->get_id())));
+        $this->assertEquals(1, \core_competency\template_cohort::count_records(array('templateid' => $t2->get_id())));
+
+        // Delete the first cohort.
+        cohort_delete_cohort($c1);
+
+        // Check that the association was removed.
+        $this->assertEquals(1, \core_competency\template_cohort::count_records(array('templateid' => $t1->get_id())));
+        $this->assertEquals(0, \core_competency\template_cohort::count_records(array('templateid' => $t2->get_id())));
+    }
 }
index 7391ada..865a5e2 100644 (file)
@@ -429,16 +429,17 @@ class core_competency_plan_testcase extends advanced_testcase {
         // Reopening plan: with due date in the future => duedate unchanged.
         $record = $plan->to_record();
         $record->status = plan::STATUS_COMPLETE;
-        $record->duedate = time() + plan::DUEDATE_THRESHOLD + 10;
+        $duedate = time() + plan::DUEDATE_THRESHOLD + 10;
+        $record->duedate = $duedate;
         $DB->update_record(plan::TABLE, $record);
 
         $success = core_competency\api::reopen_plan($plan->get_id());
         $this->assertTrue($success);
         $plan->read();
 
-        // Check that the due date has not changed, but allow for PHP Unit latency.
-        $this->assertTrue($plan->get_duedate() >= time() + plan::DUEDATE_THRESHOLD + 10);
-        $this->assertTrue($plan->get_duedate() <= time() + plan::DUEDATE_THRESHOLD + 15);
+        // Check that the due date has not changed.
+        $this->assertNotEquals(0, $plan->get_duedate());
+        $this->assertEquals($duedate, $plan->get_duedate());
     }
 
     public function test_get_by_user_and_competency() {
index 7c5d975..da654a2 100644 (file)
@@ -2495,108 +2495,3 @@ class core_course_external extends external_api {
     }
 
 }
-
-/**
- * Deprecated course external functions
- *
- * @package    core_course
- * @copyright  2009 Petr Skodak
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 2.0
- * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
- * @see core_course_external
- */
-class moodle_course_external extends external_api {
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::get_courses_parameters()
-     */
-    public static function get_courses_parameters() {
-        return core_course_external::get_courses_parameters();
-    }
-
-    /**
-     * Get courses
-     *
-     * @param array $options
-     * @return array
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::get_courses()
-     */
-    public static function get_courses($options) {
-        return core_course_external::get_courses($options);
-    }
-
-    /**
-     * Returns description of method result value
-     *
-     * @return external_description
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::get_courses_returns()
-     */
-    public static function get_courses_returns() {
-        return core_course_external::get_courses_returns();
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function get_courses_is_deprecated() {
-        return true;
-    }
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::create_courses_parameters()
-     */
-    public static function create_courses_parameters() {
-        return core_course_external::create_courses_parameters();
-    }
-
-    /**
-     * Create  courses
-     *
-     * @param array $courses
-     * @return array courses (id and shortname only)
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::create_courses()
-     */
-    public static function create_courses($courses) {
-        return core_course_external::create_courses($courses);
-    }
-
-    /**
-     * Returns description of method result value
-     *
-     * @return external_description
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_course_external::create_courses_returns()
-     */
-    public static function create_courses_returns() {
-        return core_course_external::create_courses_returns();
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function create_courses_is_deprecated() {
-        return true;
-    }
-}
index 38d3ae7..12cea2e 100644 (file)
@@ -28,7 +28,7 @@ require_once('editinstance_form.php');
 $courseid   = required_param('courseid', PARAM_INT);
 $type   = required_param('type', PARAM_COMPONENT);
 $instanceid = optional_param('id', 0, PARAM_INT);
-
+$return = optional_param('returnurl', 0, PARAM_LOCALURL);
 $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
 $context = context_course::instance($course->id, MUST_EXIST);
 
@@ -43,7 +43,10 @@ require_capability('enrol/' . $type . ':config', $context);
 $PAGE->set_url('/enrol/editinstance.php', array('courseid' => $course->id, 'id' => $instanceid, 'type' => $type));
 $PAGE->set_pagelayout('admin');
 
-$return = new moodle_url('/enrol/instances.php', array('id' => $course->id));
+if (empty($return)) {
+    $return = new moodle_url('/enrol/instances.php', array('id' => $course->id));
+}
+
 if (!enrol_is_enabled($type)) {
     redirect($return);
 }
@@ -62,7 +65,7 @@ if ($instanceid) {
     $instance->status   = ENROL_INSTANCE_ENABLED; // Do not use default for automatically created instances here.
 }
 
-$mform = new enrol_instance_edit_form(null, array($instance, $plugin, $context, $type));
+$mform = new enrol_instance_edit_form(null, array($instance, $plugin, $context, $type, $return));
 
 if ($mform->is_cancelled()) {
     redirect($return);
index 728a7e8..53ab92f 100644 (file)
@@ -45,7 +45,7 @@ class enrol_instance_edit_form extends moodleform {
 
         $mform = $this->_form;
 
-        list($instance, $plugin, $context, $type) = $this->_customdata;
+        list($instance, $plugin, $context, $type, $returnurl) = $this->_customdata;
 
         $mform->addElement('header', 'header', get_string('pluginname', 'enrol_' . $type));
 
@@ -60,6 +60,10 @@ class enrol_instance_edit_form extends moodleform {
         $mform->setType('type', PARAM_COMPONENT);
         $instance->type = $type;
 
+        $mform->addElement('hidden', 'returnurl');
+        $mform->setType('returnurl', PARAM_LOCALURL);
+        $mform->setConstant('returnurl', $returnurl);
+
         $this->add_action_buttons(true, ($instance->id ? null : get_string('addinstance', 'enrol')));
 
         $this->set_data($instance);
index 959df21..8f8347b 100644 (file)
@@ -839,303 +839,3 @@ class core_role_external extends external_api {
         return null;
     }
 }
-
-
-/**
- * Deprecated enrol and role external functions
- *
- * @package    core_enrol
- * @copyright  2010 Jerome Mouneyrac
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @since Moodle 2.0
- * @deprecated Moodle 2.2 MDL-29106 - Please do not use this class any more.
- * @see core_enrol_external
- * @see core_role_external
- */
-class moodle_enrol_external extends external_api {
-
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_enrol_external::get_enrolled_users_parameters()
-     */
-    public static function get_enrolled_users_parameters() {
-        return new external_function_parameters(
-            array(
-                'courseid'       => new external_value(PARAM_INT, 'Course id'),
-                'withcapability' => new external_value(PARAM_CAPABILITY, 'User should have this capability', VALUE_DEFAULT, null),
-                'groupid'        => new external_value(PARAM_INT, 'Group id, null means all groups', VALUE_DEFAULT, null),
-                'onlyactive'     => new external_value(PARAM_INT, 'True means only active, false means all participants', VALUE_DEFAULT, 0),
-            )
-        );
-    }
-
-    /**
-     * Get list of course participants.
-     *
-     * @param int $courseid
-     * @param text $withcapability
-     * @param int $groupid
-     * @param bool $onlyactive
-     * @return array of course participants
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_enrol_external::get_enrolled_users()
-     */
-    public static function get_enrolled_users($courseid, $withcapability = null, $groupid = null, $onlyactive = false) {
-        global $DB, $CFG, $USER;
-
-        // Do basic automatic PARAM checks on incoming data, using params description
-        // If any problems are found then exceptions are thrown with helpful error messages
-        $params = self::validate_parameters(self::get_enrolled_users_parameters(), array(
-            'courseid'=>$courseid,
-            'withcapability'=>$withcapability,
-            'groupid'=>$groupid,
-            'onlyactive'=>$onlyactive)
-        );
-
-        $coursecontext = context_course::instance($params['courseid'], IGNORE_MISSING);
-        if ($courseid == SITEID) {
-            $context = context_system::instance();
-        } else {
-            $context = $coursecontext;
-        }
-
-        try {
-            self::validate_context($context);
-        } catch (Exception $e) {
-            $exceptionparam = new stdClass();
-            $exceptionparam->message = $e->getMessage();
-            $exceptionparam->courseid = $params['courseid'];
-            throw new moodle_exception('errorcoursecontextnotvalid' , 'webservice', '', $exceptionparam);
-        }
-
-        if ($courseid == SITEID) {
-            require_capability('moodle/site:viewparticipants', $context);
-        } else {
-            require_capability('moodle/course:viewparticipants', $context);
-        }
-
-        if ($withcapability) {
-            require_capability('moodle/role:review', $coursecontext);
-        }
-        if ($groupid && groups_is_member($groupid)) {
-            require_capability('moodle/site:accessallgroups', $coursecontext);
-        }
-        if ($onlyactive) {
-            require_capability('moodle/course:enrolreview', $coursecontext);
-        }
-
-        list($sqlparams, $params) =  get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
-        $sql = "SELECT ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id as usercontextid
-                  FROM {user_enrolments} ue
-                  JOIN {enrol} e ON (e.id = ue.enrolid)
-                  JOIN {user} u ON (ue.userid = u.id)
-                  JOIN {context} c ON (u.id = c.instanceid AND contextlevel = " . CONTEXT_USER . ")
-                  WHERE e.courseid = :courseid AND ue.userid IN ($sqlparams)
-                  GROUP BY ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id";
-        $params['courseid'] = $courseid;
-        $enrolledusers = $DB->get_records_sql($sql, $params);
-        $result = array();
-        $isadmin = is_siteadmin($USER);
-        $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
-        foreach ($enrolledusers as $enrolleduser) {
-            $profilimgurl = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f1');
-            $profilimgurlsmall = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f2');
-            $resultuser = array(
-                'courseid' => $enrolleduser->courseid,
-                'userid' => $enrolleduser->userid,
-                'fullname' => fullname($enrolleduser),
-                'profileimgurl' => $profilimgurl->out(false),
-                'profileimgurlsmall' => $profilimgurlsmall->out(false)
-            );
-            // check if we can return username
-            if ($isadmin) {
-                $resultuser['username'] = $enrolleduser->username;
-            }
-            // check if we can return first and last name
-            if ($isadmin or $canviewfullnames) {
-                $resultuser['firstname'] = $enrolleduser->firstname;
-                $resultuser['lastname'] = $enrolleduser->lastname;
-            }
-            $result[] = $resultuser;
-        }
-
-        return $result;
-    }
-
-    /**
-     * Returns description of method result value
-     *
-     * @return external_description
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_enrol_external::get_enrolled_users_returns()
-     */
-    public static function get_enrolled_users_returns() {
-        return new external_multiple_structure(
-            new external_single_structure(
-                array(
-                    'courseid' => new external_value(PARAM_INT, 'id of course'),
-                    'userid' => new external_value(PARAM_INT, 'id of user'),
-                    'firstname' => new external_value(PARAM_RAW, 'first name of user', VALUE_OPTIONAL),
-                    'lastname' => new external_value(PARAM_RAW, 'last name of user', VALUE_OPTIONAL),
-                    'fullname' => new external_value(PARAM_RAW, 'fullname of user'),
-                    'username' => new external_value(PARAM_RAW, 'username of user', VALUE_OPTIONAL),
-                    'profileimgurl' => new external_value(PARAM_URL, 'url of the profile image'),
-                    'profileimgurlsmall' => new external_value(PARAM_URL, 'url of the profile image (small version)')
-                )
-            )
-        );
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function get_enrolled_users_is_deprecated() {
-        return true;
-    }
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.1
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_enrol_external::get_users_courses_parameters()
-     */
-    public static function get_users_courses_parameters() {
-        return core_enrol_external::get_users_courses_parameters();
-    }
-
-    /**
-     * Get list of courses user is enrolled in (only active enrolments are returned).
-     * Please note the current user must be able to access the course, otherwise the course is not included.
-     *
-     * @param int $userid
-     * @return array of courses
-     * @since Moodle 2.1
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see use core_enrol_external::get_users_courses()
-     */
-    public static function get_users_courses($userid) {
-        return core_enrol_external::get_users_courses($userid);
-    }
-
-    /**
-     * Returns description of method result value
-     *
-     * @return external_description
-     * @since Moodle 2.1
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_enrol_external::get_users_courses_returns()
-     */
-    public static function get_users_courses_returns() {
-        return core_enrol_external::get_users_courses_returns();
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function get_users_courses_is_deprecated() {
-        return true;
-    }
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::assign_roles_parameters()
-     */
-    public static function role_assign_parameters() {
-        return core_role_external::assign_roles_parameters();
-    }
-
-    /**
-     * Manual role assignments to users
-     *
-     * @param array $assignments An array of manual role assignment
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::assign_roles()
-     */
-    public static function role_assign($assignments) {
-        return core_role_external::assign_roles($assignments);
-    }
-
-    /**
-     * Returns description of method result value
-     *
-     * @return null
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::assign_roles_returns()
-     */
-    public static function role_assign_returns() {
-        return core_role_external::assign_roles_returns();
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function role_assign_is_deprecated() {
-        return true;
-    }
-
-    /**
-     * Returns description of method parameters
-     *
-     * @return external_function_parameters
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::unassign_roles_parameters()
-     */
-    public static function role_unassign_parameters() {
-        return core_role_external::unassign_roles_parameters();
-    }
-
-     /**
-     * Unassign roles from users
-     *
-     * @param array $unassignments An array of unassignment
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::unassign_roles()
-     */
-    public static function role_unassign($unassignments) {
-         return core_role_external::unassign_roles($unassignments);
-    }
-
-   /**
-     * Returns description of method result value
-     *
-     * @return null
-     * @since Moodle 2.0
-     * @deprecated Moodle 2.2 MDL-29106 - Please do not call this function any more.
-     * @see core_role_external::unassign_roles_returns()
-     */
-    public static function role_unassign_returns() {
-        return core_role_external::unassign_roles_returns();
-    }
-
-    /**
-     * Marking the method as deprecated.
-     *
-     * @return bool
-     */
-    public static function role_unassign_is_deprecated() {
-        return true;
-    }
-}
diff --git a/enrol/lti/backup/moodle2/backup_enrol_lti_plugin.class.php b/enrol/lti/backup/moodle2/backup_enrol_lti_plugin.class.php
new file mode 100644 (file)
index 0000000..6cd5497
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines the backup_enrol_lti_plugin class.
+ *
+ * @package   enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Define all the backup steps.
+ *
+ * @package   enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class backup_enrol_lti_plugin extends backup_enrol_plugin {
+
+    /**
+     * Defines the other LTI enrolment structures to append.
+     *
+     * @return backup_plugin_element
+     */
+    public function define_enrol_plugin_structure() {
+        // Get the parent we will be adding these elements to.
+        $plugin = $this->get_plugin_element();
+
+        // Define our elements.
+        $tool = new backup_nested_element('tool', array('id'), array(
+            'enrolid', 'contextid', 'institution', 'lang', 'timezone', 'maxenrolled', 'maildisplay', 'city',
+            'country', 'gradesync', 'gradesynccompletion', 'membersync', 'membersyncmode',  'roleinstructor',
+            'rolelearner', 'secret', 'timecreated', 'timemodified'));
+
+        $users = new backup_nested_element('users');
+
+        $user = new backup_nested_element('user', array('id'), array(
+            'userid', 'toolid', 'serviceurl', 'sourceid', 'consumerkey', 'consumersecret', 'membershipurl',
+            'membershipsid'));
+
+        // Build elements hierarchy.
+        $plugin->add_child($tool);
+        $tool->add_child($users);
+        $users->add_child($user);
+
+        // Set sources to populate the data.
+        $tool->set_source_table('enrol_lti_tools',
+            array('enrolid' => backup::VAR_PARENTID));
+
+        // Users are only added only if users included.
+        if ($this->task->get_setting_value('users')) {
+            $user->set_source_table('enrol_lti_users', array('toolid' => backup::VAR_PARENTID));
+        }
+    }
+}
diff --git a/enrol/lti/backup/moodle2/restore_enrol_lti_plugin.class.php b/enrol/lti/backup/moodle2/restore_enrol_lti_plugin.class.php
new file mode 100644 (file)
index 0000000..1225223
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines the restore_enrol_lti_plugin class.
+ *
+ * @package   enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Define all the restore steps.
+ *
+ * @package   enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class restore_enrol_lti_plugin extends restore_enrol_plugin {
+
+    /**
+     * @var array $tools Stores the IDs of the newly created tools.
+     */
+    protected $tools = array();
+
+    /**
+     * Declares the enrol LTI XML paths attached to the enrol element
+     *
+     * @return array of {@link restore_path_element}
+     */
+    protected function define_enrol_plugin_structure() {
+
+        $paths = array();
+        $paths[] = new restore_path_element('enrol_lti_tool', $this->connectionpoint->get_path() . '/tool');
+        $paths[] = new restore_path_element('enrol_lti_users', $this->connectionpoint->get_path() . '/tool/users/user');
+
+        return $paths;
+    }
+
+    /**
+     * Processes LTI tools element data
+     *
+     * @param array|stdClass $data
+     */
+    public function process_enrol_lti_tool($data) {
+        global $DB;
+
+        $data = (object) $data;
+
+        // Store the old id.
+        $oldid = $data->id;
+
+        // Change the values before we insert it.
+        $data->timecreated = time();
+        $data->timemodified = $data->timecreated;
+
+        // Now we can insert the new record.
+        $data->id = $DB->insert_record('enrol_lti_tools', $data);
+
+        // Add the array of tools we need to process later.
+        $this->tools[$data->id] = $data;
+
+        // Set up the mapping.
+        $this->set_mapping('enrol_lti_tool', $oldid, $data->id);
+    }
+
+    /**
+     * Processes LTI users element data
+     *
+     * @param array|stdClass $data The data to insert as a comment
+     */
+    public function process_enrol_lti_users($data) {
+        global $DB;
+
+        $data = (object) $data;
+
+        $data->userid = $this->get_mappingid('user', $data->userid);
+        $data->toolid = $this->get_mappingid('enrol_lti_tool', $data->toolid);
+        $data->timecreated = time();
+
+        $DB->insert_record('enrol_lti_users', $data);
+    }
+
+    /**
+     * This function is executed after all the tasks in the plan have been finished.
+     * This must be done here because the activities have not been restored yet.
+     */
+    public function after_restore_enrol() {
+        global $DB;
+
+        // Need to go through and change the values.
+        foreach ($this->tools as $tool) {
+            $updatetool = new stdClass();
+            $updatetool->id = $tool->id;
+            $updatetool->enrolid = $this->get_mappingid('enrol', $tool->enrolid);
+            $updatetool->contextid = $this->get_mappingid('context', $tool->contextid);
+            $DB->update_record('enrol_lti_tools', $updatetool);
+        }
+    }
+}
diff --git a/enrol/lti/classes/helper.php b/enrol/lti/classes/helper.php
new file mode 100644 (file)
index 0000000..f6a006a
--- /dev/null
@@ -0,0 +1,378 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * LTI enrolment plugin helper.
+ *
+ * @package enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace enrol_lti;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * LTI enrolment plugin helper class.
+ *
+ * @package enrol_lti
+ * @copyright 2016 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class helper {
+    /*
+     * The value used when we want to enrol new members and unenrol old ones.
+     */
+    const MEMBER_SYNC_ENROL_AND_UNENROL = 1;
+
+    /*
+     * The value used when we want to enrol new members only.
+     */
+    const MEMBER_SYNC_ENROL_NEW = 2;
+
+    /*
+     * The value used when we want to unenrol missing users.
+     */
+    const MEMBER_SYNC_UNENROL_MISSING = 3;
+
+    /**
+     * Code for when an enrolment was successful.
+     */
+    const ENROLMENT_SUCCESSFUL = true;
+
+    /**
+     * Error code for enrolment when max enrolled reached.
+     */
+    const ENROLMENT_MAX_ENROLLED = 'maxenrolledreached';
+
+    /**
+     * Error code for enrolment has not started.
+     */
+    const ENROLMENT_NOT_STARTED = 'enrolmentnotstarted';
+
+    /**
+     * Error code for enrolment when enrolment has finished.
+     */
+    const ENROLMENT_FINISHED = 'enrolmentfinished';
+
+    /**
+     * Error code for when an image file fails to upload.
+     */
+    const PROFILE_IMAGE_UPDATE_SUCCESSFUL = true;
+
+    /**
+     * Error code for when an image file fails to upload.
+     */
+    const PROFILE_IMAGE_UPDATE_FAILED = 'profileimagefailed';
+
+    /**
+     * Creates a unique username.
+     *
+     * @param string $consumerkey Consumer key
+     * @param string $ltiuserid External tool user id
+     * @return string The new username
+     */
+    public static function create_username($consumerkey, $ltiuserid) {
+        if (!empty($ltiuserid) && !empty($consumerkey)) {
+            $userkey = $consumerkey . ':' . $ltiuserid;
+        } else {
+            $userkey = false;
+        }
+
+        return 'enrol_lti' . sha1($consumerkey . '::' . $userkey);
+    }
+
+    /**
+     * Adds default values for the user object based on the tool provided.
+     *
+     * @param \stdClass $tool
+     * @param \stdClass $user
+     * @return \stdClass The $user class with added default values
+     */
+    public static function assign_user_tool_data($tool, $user) {
+        global $CFG;
+
+        $user->city = (!empty($tool->city)) ? $tool->city : "";
+        $user->country = (!empty($tool->country)) ? $tool->country : "";
+        $user->institution = (!empty($tool->institution)) ? $tool->institution : "";
+        $user->timezone = (!empty($tool->timezone)) ? $tool->timezone : "";
+        if (isset($tool->maildisplay)) {
+            $user->maildisplay = $tool->maildisplay;
+        } else if (isset($CFG->defaultpreference_maildisplay)) {
+            $user->maildisplay = $CFG->defaultpreference_maildisplay;
+        } else {
+            $user->maildisplay = 2;
+        }
+        $user->mnethostid = $CFG->mnet_localhost_id;
+        $user->confirmed = 1;
+        $user->lang = $tool->lang;
+
+        return $user;
+    }
+
+    /**
+     * Compares two users.
+     *
+     * @param \stdClass $newuser The new user
+     * @param \stdClass $olduser The old user
+     * @return bool True if both users are the same
+     */
+    public static function user_match($newuser, $olduser) {
+        if ($newuser->firstname != $olduser->firstname) {
+            return false;
+        }
+        if ($newuser->lastname != $olduser->lastname) {
+            return false;
+        }
+        if ($newuser->email != $olduser->email) {
+            return false;
+        }
+        if ($newuser->city != $olduser->city) {
+            return false;
+        }
+        if ($newuser->country != $olduser->country) {
+            return false;
+        }
+        if ($newuser->institution != $olduser->institution) {
+            return false;
+        }
+        if ($newuser->timezone != $olduser->timezone) {
+            return false;
+        }
+        if ($newuser->maildisplay != $olduser->maildisplay) {
+            return false;
+        }
+        if ($newuser->mnethostid != $olduser->mnethostid) {
+            return false;
+        }
+        if ($newuser->confirmed != $olduser->confirmed) {
+            return false;
+        }
+        if ($newuser->lang != $olduser->lang) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Updates the users profile image.
+     *
+     * @param int $userid the id of the user
+     * @param string $url the url of the image
+     * @return bool|string true if successful, else a string explaining why it failed
+     */
+    public static function update_user_profile_image($userid, $url) {
+        global $CFG, $DB;
+
+        require_once($CFG->libdir . '/filelib.php');
+        require_once($CFG->libdir . '/gdlib.php');
+
+        $fs = get_file_storage();
+
+        $context = \context_user::instance($userid, MUST_EXIST);
+        $fs->delete_area_files($context->id, 'user', 'newicon');
+
+        $filerecord = array(
+            'contextid' => $context->id,
+            'component' => 'user',
+            'filearea' => 'newicon',
+            'itemid' => 0,
+            'filepath' => '/'
+        );
+
+        $urlparams = array(
+            'calctimeout' => false,
+            'timeout' => 5,
+            'skipcertverify' => true,
+            'connecttimeout' => 5
+        );
+
+        if (!$iconfiles = $fs->create_file_from_url($filerecord, $url, $urlparams)) {
+            return self::PROFILE_IMAGE_UPDATE_FAILED;
+        }
+
+        $iconfile = $fs->get_area_files($context->id, 'user', 'newicon', false, 'itemid', false);
+
+        // There should only be one.
+        $iconfile = reset($iconfile);
+
+        // Something went wrong while creating temp file - remove the uploaded file.
+        if (!$iconfile = $iconfile->copy_content_to_temp()) {
+            $fs->delete_area_files($context->id, 'user', 'newicon');
+            return self::PROFILE_IMAGE_UPDATE_FAILED;
+        }
+
+        // Copy file to temporary location and the send it for processing icon.
+        $newpicture = (int) process_new_icon($context, 'user', 'icon', 0, $iconfile);
+        // Delete temporary file.
+        @unlink($iconfile);
+        // Remove uploaded file.
+        $fs->delete_area_files($context->id, 'user', 'newicon');
+        // Set the user's picture.
+        $DB->set_field('user', 'picture', $newpicture, array('id' => $userid));
+        return self::PROFILE_IMAGE_UPDATE_SUCCESSFUL;
+    }
+
+    /**
+     * Enrol a user in a course.
+     *
+     * @param \stdclass $tool The tool object (retrieved using self::get_lti_tool() or self::get_lti_tools())
+     * @param int $userid The user id
+     * @return bool|string returns true if successful, else an error code
+     */
+    public static function enrol_user($tool, $userid) {
+        global $DB;
+
+        // Check if the user enrolment exists.
+        if (!$DB->record_exists('user_enrolments', array('enrolid' => $tool->enrolid, 'userid' => $userid))) {
+            // Check if the maximum enrolled limit has been met.
+            if ($tool->maxenrolled) {
+                if ($DB->count_records('user_enrolments', array('enrolid' => $tool->enrolid)) >= $tool->maxenrolled) {
+                    return self::ENROLMENT_MAX_ENROLLED;
+                }
+            }
+            // Check if the enrolment has not started.
+            if ($tool->enrolstartdate && time() < $tool->enrolstartdate) {
+                return self::ENROLMENT_NOT_STARTED;
+            }
+            // Check if the enrolment has finished.
+            if ($tool->enrolenddate && time() > $tool->enrolenddate) {
+                return self::ENROLMENT_FINISHED;
+            }
+
+            $timeend = 0;
+            if ($tool->enrolperiod) {
+                $timeend = time() + $tool->enrolperiod;
+            }
+
+            // Finally, enrol the user.
+            $instance = new \stdClass();
+            $instance->id = $tool->enrolid;
+            $instance->courseid = $tool->courseid;
+            $instance->enrol = 'lti';
+            $instance->status = $tool->status;
+            $ltienrol = enrol_get_plugin('lti');
+            $ltienrol->enrol_user($instance, $userid, null, time(), $timeend);
+        }
+
+        return self::ENROLMENT_SUCCESSFUL;
+    }
+
+    /**
+     * Returns the LTI tool.
+     *
+     * @param int $toolid
+     * @return \stdClass the tool
+     */
+    public static function get_lti_tool($toolid) {
+        global $DB;
+
+        $sql = "SELECT elt.*, e.name, e.courseid, e.status, e.enrolstartdate, e.enrolenddate, e.enrolperiod
+                  FROM {enrol_lti_tools} elt
+                  JOIN {enrol} e
+                    ON elt.enrolid = e.id
+                 WHERE elt.id = :tid";
+
+        return $DB->get_record_sql($sql, array('tid' => $toolid), MUST_EXIST);
+    }
+
+    /**
+     * Returns the LTI tools requested.
+     *
+     * @param array $params The list of SQL params (eg. array('columnname' => value, 'columnname2' => value)).
+     * @param int $limitfrom return a subset of records, starting at this point (optional).
+     * @param int $limitnum return a subset comprising this many records in total
+     * @return array of tools
+     */
+    public static function get_lti_tools($params = array(), $limitfrom = 0, $limitnum = 0) {
+        global $DB;
+
+        $sql = "SELECT elt.*, e.name, e.courseid, e.status, e.enrolstartdate, e.enrolenddate, e.enrolperiod
+                  FROM {enrol_lti_tools} elt
+                  JOIN {enrol} e
+                    ON elt.enrolid = e.id";
+        if ($params) {
+            $where = "WHERE";
+            foreach ($params as $colname => $value) {
+                $sql .= " $where $colname = :$colname";
+                $where = "AND";
+            }
+        }
+        $sql .= " ORDER BY elt.timecreated";
+
+        return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum);
+    }
+
+    /**
+     * Returns the number of LTI tools.
+     *
+     * @param array $params The list of SQL params (eg. array('columnname' => value, 'columnname2' => value)).
+     * @return int The number of tools
+     */
+    public static function count_lti_tools($params = array()) {
+        global $DB;
+
+        $sql = "SELECT COUNT(*)
+                  FROM {enrol_lti_tools} elt
+                  JOIN {enrol} e
+                    ON elt.enrolid = e.id";
+        if ($params) {
+            $where = "WHERE";
+            foreach ($params as $colname => $value) {
+                $sql .= " $where $colname = :$colname";
+                $where = "AND";
+            }
+        }
+
+        return $DB->count_records_sql($sql, $params);
+    }
+
+    /**
+     * Create a IMS POX body request for sync grades.
+     *
+     * @param string $source Sourceid required for the request
+     * @param float $grade User final grade
+     * @return string
+     */
+    public static function create_service_body($source, $grade) {
+        return '<?xml version="1.0" encoding="UTF-8"?>
+            <imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">
+              <imsx_POXHeader>
+                <imsx_POXRequestHeaderInfo>
+                  <imsx_version>V1.0</imsx_version>
+                  <imsx_messageIdentifier>' . (time()) . '</imsx_messageIdentifier>
+                </imsx_POXRequestHeaderInfo>
+              </imsx_POXHeader>
+              <imsx_POXBody>
+                <replaceResultRequest>
+                  <resultRecord>
+                    <sourcedGUID>
+                      <sourcedId>' . $source . '</sourcedId>
+                    </sourcedGUID>
+                    <result>
+                      <resultScore>
+                        <language>en-us</language>
+                        <textString>' . $grade . '</textString>
+                      </resultScore>
+                    </result>
+                  </resultRecord>
+                </replaceResultRequest>
+              </imsx_POXBody>
+            </imsx_POXEnvelopeRequest>';
+    }
+}
diff --git a/enrol/lti/classes/manage_table.php b/enrol/lti/classes/manage_table.php
new file mode 100644 (file)
index 0000000..d334483
--- /dev/null
@@ -0,0 +1,218 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Displays enrolment LTI instances.
+ *
+ * @package    enrol_lti
+ * @copyright  2016 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace enrol_lti;
+
+defined('MOODLE_INTERNAL') || die;
+
+global $CFG;
+
+require_once($CFG->libdir . '/tablelib.php');
+
+/**
+ * Handles displaying enrolment LTI instances.
+ *
+ * @package    enrol_lti
+ * @copyright  2016 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class manage_table extends \table_sql {
+
+    /**
+     * @var \enrol_plugin $ltiplugin
+     */
+    protected $ltiplugin;
+
+    /**
+     * @var bool $ltienabled
+     */
+    protected $ltienabled;
+
+    /**
+     * @var bool $canconfig
+     */
+    protected $canconfig;
+
+    /**
+     * @var int $courseid The course id.
+     */
+    protected $courseid;
+
+    /**
+     * Sets up the table.
+     *
+     * @param string $courseid The id of the course.
+     */
+    public function __construct($courseid) {
+        parent::__construct('enrol_lti_manage_table');
+
+        $this->define_columns(array(
+            'name',
+            'url',
+            'secret',
+            'edit'
+        ));
+        $this->define_headers(array(
+            get_string('name'),
+            get_string('url'),
+            get_string('secret', 'enrol_lti'),
+            get_string('edit')
+        ));
+        $this->collapsible(false);
+        $this->sortable(false);
+
+        // Set the variables we need access to.
+        $this->ltiplugin = enrol_get_plugin('lti');
+        $this->ltienabled = enrol_is_enabled('lti');
+        $this->canconfig = has_capability('moodle/course:enrolconfig', \context_course::instance($courseid));
+        $this->courseid = $courseid;
+    }
+
+    /**
+     * Generate the name column.
+     *
+     * @param \stdClass $tool event data.
+     * @return string
+     */
+    public function col_name($tool) {
+        if (empty($tool->name)) {
+            $toolcontext = \context::instance_by_id($tool->contextid);
+            $name = $toolcontext->get_context_name();
+        } else {
+            $name = $tool->name;
+        };
+
+        return $this->get_display_text($tool, $name);
+    }
+
+    /**
+     * Generate the URL column.
+     *
+     * @param \stdClass $tool event data.
+     * @return string
+     */
+    public function col_url($tool) {
+        $url = new \moodle_url('/enrol/lti/tool.php', array('id' => $tool->id));
+        return $this->get_display_text($tool, $url);
+    }
+
+    /**
+     * Generate the secret column.
+     *
+     * @param \stdClass $tool event data.
+     * @return string
+     */
+    public function col_secret($tool) {
+        return $this->get_display_text($tool, $tool->secret);
+    }
+
+
+    /**
+     * Generate the edit column.
+     *
+     * @param \stdClass $tool event data.
+     * @return string
+     */
+    public function col_edit($tool) {
+        global $OUTPUT;
+
+        $buttons = array();
+
+        $instance = new \stdClass();
+        $instance->id = $tool->enrolid;
+        $instance->courseid = $tool->courseid;
+        $instance->enrol = 'lti';
+        $instance->status = $tool->status;
+
+        $strdelete = get_string('delete');
+        $strenable = get_string('enable');
+        $strdisable = get_string('disable');
+
+        $url = new \moodle_url('/enrol/lti/index.php', array('sesskey' => sesskey(), 'courseid' => $this->courseid));
+
+        if ($this->ltiplugin->can_delete_instance($instance)) {
+            $aurl = new \moodle_url($url, array('action' => 'delete', 'instanceid' => $instance->id));
+            $buttons[] = $OUTPUT->action_icon($aurl, new \pix_icon('t/delete', $strdelete, 'core',
+                array('class' => 'iconsmall')));
+        }
+
+        if ($this->ltienabled && $this->ltiplugin->can_hide_show_instance($instance)) {
+            if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                $aurl = new \moodle_url($url, array('action' => 'disable', 'instanceid' => $instance->id));
+                $buttons[] = $OUTPUT->action_icon($aurl, new \pix_icon('t/hide', $strdisable, 'core',
+                    array('class' => 'iconsmall')));
+            } else if ($instance->status == ENROL_INSTANCE_DISABLED) {
+                $aurl = new \moodle_url($url, array('action' => 'enable', 'instanceid' => $instance->id));
+                $buttons[] = $OUTPUT->action_icon($aurl, new \pix_icon('t/show', $strenable, 'core',
+                    array('class' => 'iconsmall')));
+            }
+        }
+
+        if ($this->ltienabled && $this->canconfig) {
+            $linkparams = array(
+                'courseid' => $instance->courseid,
+                'id' => $instance->id, 'type' => $instance->enrol,
+                'returnurl' => new \moodle_url('/enrol/lti/index.php', array('courseid' => $this->courseid))
+            );
+            $editlink = new \moodle_url("/enrol/editinstance.php", $linkparams);
+            $buttons[] = $OUTPUT->action_icon($editlink, new \pix_icon('t/edit', get_string('edit'), 'core',
+                array('class' => 'iconsmall')));
+        }
+
+        return implode(' ', $buttons);
+    }
+
+    /**
+     * Query the reader. Store results in the object for use by build_table.
+     *
+     * @param int $pagesize size of page for paginated displayed table.
+     * @param bool $useinitialsbar do you want to use the initials bar.
+     */
+    public function query_db($pagesize, $useinitialsbar = true) {
+        $total = \enrol_lti\helper::count_lti_tools(array('courseid' => $this->courseid));
+        $this->pagesize($pagesize, $total);
+        $tools = \enrol_lti\helper::get_lti_tools(array('courseid' => $this->courseid), $this->get_page_start(),
+            $this->get_page_size());
+        $this->rawdata = $tools;
+        // Set initial bars.
+        if ($useinitialsbar) {
+            $this->initialbars($total > $pagesize);
+        }
+    }
+
+    /**
+     * Returns text to display in the columns.
+     *
+     * @param \stdClass $tool the tool
+     * @param string $text the text to alter
+     * @return string
+     */
+    protected function get_display_text($tool, $text) {
+        if ($tool->status != ENROL_INSTANCE_ENABLED) {
+            return \html_writer::tag('span', $text, array('class' => 'dimmed_text'));
+        }
+
+        return $text;
+    }
+}
diff --git a/enrol/lti/classes/task/sync_grades.php b/enrol/lti/classes/task/sync_grades.php
new file mode 100644 (file)
index 0000000..944de37
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Handles synchronising grades for the enrolment LTI.
+ *
+ * @package    enrol_lti
+ * @copyright  2016 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace enrol_lti\task;
+
+/**
+ * Task for synchronising grades for the enrolment LTI.
+ *
+ * @package    enrol_lti
+ * @copyright  2016 Mark Nelson <markn@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class sync_grades extends \core\task\scheduled_task {
+
+    /**
+     * Get a descriptive name for this task.
+     *
+     * @return string
+     */
+    public function get_name() {
+        return get_string('tasksyncgrades', 'enrol_lti');
+    }
+
+    /**
+     * Performs the synchronisation of grades.
+     *
+     * @return bool|void
+     */
+    public function execute() {
+        global $DB, $CFG;
+
+        require_once($CFG->dirroot . '/enrol/lti/ims-blti/OAuth.php');
+        require_once($CFG->dirroot . '/enrol/lti/ims-blti/OAuthBody.php');
+        require_once($CFG->dirroot . '/lib/completionlib.php');
+        require_once($CFG->libdir . '/gradelib.php');
+        require_once($CFG->dirroot . '/grade/querylib.php');
+
+        // Check if the authentication plugin is disabled.
+        if (!is_enabled_auth('lti')) {
+            mtrace('Skipping task - ' . get_string('pluginnotenabled', 'auth', get_string('pluginname', 'auth_lti')));
+            return true;
+        }
+
+        // Check if the enrolment plugin is disabled - isn't really necessary as the task should not run if
+        // the plugin is disabled, but there is no harm in making sure core hasn't done something wrong.
+        if (!enrol_is_enabled('lti')) {
+            mtrace('Skipping task - ' . get_string('enrolisdisabled', 'enrol_lti'));
+            return true;
+        }
+
+        // Get all the enabled tools.
+        if ($tools = \enrol_lti\helper::get_lti_tools(array('status' => ENROL_INSTANCE_ENABLED, 'gradesync' => 1))) {
+            foreach ($tools as $tool) {
+                mtrace("Starting - Grade sync for shared tool '$tool->id' for the course '$tool->courseid'.");
+
+                // Variables to keep track of information to display later.
+                $usercount = 0;
+                $sendcount = 0;
+
+                // We check for all the users - users can access the same tool from different consumers.
+                if ($ltiusers = $DB->get_records('enrol_lti_users', array('toolid' => $tool->id), 'lastaccess DESC')) {
+                    $completion = new \completion_info(get_course($tool->courseid));
+                    foreach ($ltiusers as $ltiuser) {
+                        $mtracecontent = "for the user '$ltiuser->userid' in the tool '$tool->id' for the course " .
+                            "'$tool->courseid'";
+
+                        $usercount = $usercount + 1;
+
+                        // Check if we do not have a serviceurl - this can happen if the sync process has an unexpected error.
+                        if (empty($ltiuser->serviceurl)) {
+                            mtrace("Skipping - Empty serviceurl $mtracecontent.");
+                            continue;
+                        }
+
+                        // Check if we do not have a sourceid - this can happen if the sync process has an unexpected error.
+                        if (empty($ltiuser->sourceid)) {
+                            mtrace("Skipping - Empty sourceid $mtracecontent.");
+                            continue;
+                        }
+
+                        // Need a valid context to continue.
+                        if (!$context = \context::instance_by_id($tool->contextid)) {
+                            mtrace("Failed - Invalid contextid '$tool->contextid' for the tool '$tool->id'.");
+                            continue;
+                        }
+
+                        // Ok, let's get the grade.
+                        $grade = false;
+                        if ($context->contextlevel == CONTEXT_COURSE) {
+                            // Check if the user did not completed the course when it was required.
+                            if ($tool->gradesynccompletion && !$completion->is_course_complete($ltiuser->userid)) {
+                                mtrace("Skipping - Course not completed $mtracecontent.");
+                                continue;
+                            }
+
+                            // Get the grade.
+                            if ($grade = grade_get_course_grade($ltiuser->userid, $tool->courseid)) {
+                                $grademax = floatval($grade->item->grademax);
+                                $grade = $grade->grade;
+                            }
+                        } else if ($context->contextlevel == CONTEXT_MODULE) {
+                            $cm = get_coursemodule_from_id(false, $context->instanceid, 0, false, MUST_EXIST);
+
+                            if ($tool->gradesynccompletion) {
+                                $data = $completion->get_data($cm, false, $ltiuser->userid);
+                                if ($data->completionstate != COMPLETION_COMPLETE_PASS &&
+                                    $data->completionstate != COMPLETION_COMPLETE) {