Merge branch 'MDL-54891-master' of git://github.com/ryanwyllie/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 12 Jul 2016 09:38:56 +0000 (10:38 +0100)
committerDan Poltawski <dan@moodle.com>
Tue, 12 Jul 2016 09:38:56 +0000 (10:38 +0100)
382 files changed:
.eslintrc
.jshintrc
.travis.yml
Gruntfile.js
admin/cli/install.php
admin/cli/install_database.php
admin/cli/reset_password.php
admin/environment.xml
admin/index.php
admin/roles/ajax.php
admin/searchareas.php [new file with mode: 0644]
admin/settings/plugins.php
admin/tool/lp/amd/build/actionselector.min.js
admin/tool/lp/amd/build/competencies.min.js
admin/tool/lp/amd/build/competencyactions.min.js
admin/tool/lp/amd/build/competencypicker.min.js
admin/tool/lp/amd/build/competencypicker_user_plans.min.js
admin/tool/lp/amd/build/competencyruleconfig.min.js
admin/tool/lp/amd/build/grade_user_competency_inline.min.js
admin/tool/lp/amd/build/menubar.min.js
admin/tool/lp/amd/build/parentcompetency_form.min.js
admin/tool/lp/amd/build/planactions.min.js
admin/tool/lp/amd/build/scaleconfig.min.js
admin/tool/lp/amd/build/user_evidence_actions.min.js
admin/tool/lp/amd/src/actionselector.js
admin/tool/lp/amd/src/competencies.js
admin/tool/lp/amd/src/competency_outcomes.js
admin/tool/lp/amd/src/competency_rule_points.js
admin/tool/lp/amd/src/competencyactions.js
admin/tool/lp/amd/src/competencydialogue.js
admin/tool/lp/amd/src/competencypicker.js
admin/tool/lp/amd/src/competencypicker_user_plans.js
admin/tool/lp/amd/src/competencyruleconfig.js
admin/tool/lp/amd/src/course_competency_settings.js
admin/tool/lp/amd/src/dragdrop-reorder.js
admin/tool/lp/amd/src/evidence_delete.js
admin/tool/lp/amd/src/form-cohort-selector.js
admin/tool/lp/amd/src/frameworkactions.js
admin/tool/lp/amd/src/grade_user_competency_inline.js
admin/tool/lp/amd/src/menubar.js
admin/tool/lp/amd/src/parentcompetency_form.js
admin/tool/lp/amd/src/planactions.js
admin/tool/lp/amd/src/scaleconfig.js
admin/tool/lp/amd/src/templateactions.js
admin/tool/lp/amd/src/tree.js
admin/tool/lp/amd/src/user_competency_info.js
admin/tool/lp/amd/src/user_competency_plan_popup.js
admin/tool/lp/amd/src/user_evidence_actions.js
admin/tool/lp/coursecompetencies.php
admin/tool/lp/lib.php
admin/tool/mobile/classes/api.php
admin/tool/mobile/classes/external.php
admin/tool/mobile/db/services.php
admin/tool/mobile/tests/externallib_test.php
admin/tool/mobile/version.php
admin/tool/monitor/classes/eventobservers.php
admin/tool/monitor/classes/subscription.php
admin/tool/monitor/classes/subscription_manager.php
admin/tool/monitor/classes/task/check_subscriptions.php [new file with mode: 0644]
admin/tool/monitor/db/install.xml
admin/tool/monitor/db/tasks.php
admin/tool/monitor/db/upgrade.php
admin/tool/monitor/lang/en/tool_monitor.php
admin/tool/monitor/tests/subscription_test.php [new file with mode: 0644]
admin/tool/monitor/tests/task_check_subscriptions_test.php [new file with mode: 0644]
admin/tool/monitor/version.php
admin/tool/templatelibrary/amd/src/search.js
admin/tool/xmldb/actions/edit_field/edit_field.class.php
admin/tool/xmldb/actions/edit_index/edit_index.class.php
admin/tool/xmldb/actions/edit_key/edit_key.class.php
admin/tool/xmldb/actions/edit_table/edit_table.class.php
auth/db/auth.php
competency/classes/api.php
competency/classes/course_competency_settings.php
competency/classes/evidence.php
competency/tests/api_test.php
composer.json
course/classes/search/mycourse.php
course/externallib.php
course/tests/externallib_test.php
course/view.php
course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js
course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js
course/yui/build/moodle-course-management/moodle-course-management-debug.js
course/yui/build/moodle-course-management/moodle-course-management.js
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js
course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js
course/yui/src/dragdrop/js/dragdrop.js
course/yui/src/management/js/category.js
course/yui/src/management/js/console.js
course/yui/src/management/js/course.js
course/yui/src/management/js/dd.js
course/yui/src/toolboxes/js/resource.js
course/yui/src/toolboxes/js/section.js
course/yui/src/toolboxes/js/toolbox.js
enrol/externallib.php
enrol/tests/externallib_test.php
enrol/upgrade.txt
filter/mathjaxloader/filter.php
grade/amd/build/edittree_index.min.js [new file with mode: 0644]
grade/amd/src/edittree_index.js [new file with mode: 0644]
grade/edit/tree/functions.js [deleted file]
grade/edit/tree/index.php
grade/edit/tree/lib.php
grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable-debug.js
grade/report/grader/yui/build/moodle-gradereport_grader-gradereporttable/moodle-gradereport_grader-gradereporttable.js
grade/report/grader/yui/src/gradereporttable/js/floatingheaders.js
grade/report/grader/yui/src/gradereporttable/js/gradereporttable.js
install.php
install/lang/da/error.php
install/lang/da/moodle.php
lang/en/admin.php
lang/en/search.php
lib/adminlib.php
lib/amd/build/fragment.min.js
lib/amd/build/notification.min.js
lib/amd/build/permissionmanager.min.js
lib/amd/build/tag.min.js
lib/amd/src/event.js
lib/amd/src/form-autocomplete.js
lib/amd/src/form-course-selector.js
lib/amd/src/fragment.js
lib/amd/src/localstorage.js
lib/amd/src/notification.js
lib/amd/src/permissionmanager.js
lib/amd/src/tag.js
lib/amd/src/templates.js
lib/amd/src/tree.js
lib/badgeslib.php
lib/classes/grades_external.php
lib/classes/plugin_manager.php
lib/classes/update/code_manager.php
lib/classes/user.php
lib/db/services.php
lib/db/upgrade.php
lib/ddl/tests/ddl_test.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/pgsql_native_moodle_recordset.php
lib/dml/tests/dml_test.php
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin.js
lib/editor/atto/yui/src/editor/js/autosave.js
lib/editor/atto/yui/src/editor/js/clean.js
lib/editor/atto/yui/src/editor/js/editor-plugin-buttons.js
lib/editor/atto/yui/src/editor/js/editor.js
lib/editor/atto/yui/src/editor/js/lintconfig.js [deleted file]
lib/editor/atto/yui/src/editor/js/notify.js
lib/editor/atto/yui/src/editor/js/selection.js
lib/editor/atto/yui/src/editor/js/styling.js
lib/editor/atto/yui/src/editor/js/toolbar-keyboardnav.js
lib/externallib.php
lib/filelib.php
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js
lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js
lib/form/yui/src/dateselector/js/dateselector.js
lib/navigationlib.php
lib/phpmailer/moodle_phpmailer.php
lib/setup.php
lib/tests/behat/behat_hooks.php
lib/tests/externallib_test.php
lib/tests/filelib_test.php
lib/tests/fixtures/update_validator/zips/multidir.zip [new file with mode: 0644]
lib/tests/text_test.php
lib/tests/update_code_manager_test.php
lib/tests/user_test.php
lib/tests/weblib_test.php
lib/upgrade.txt
lib/weblib.php
lib/xmldb/xmldb_table.php
lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
lib/yui/build/moodle-core-dock/moodle-core-dock-debug.js
lib/yui/build/moodle-core-dock/moodle-core-dock.js
lib/yui/build/moodle-core-languninstallconfirm/moodle-core-languninstallconfirm-debug.js
lib/yui/build/moodle-core-languninstallconfirm/moodle-core-languninstallconfirm.js
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception-debug.js
lib/yui/build/moodle-core-notification-ajaxexception/moodle-core-notification-ajaxexception.js
lib/yui/build/moodle-core-notification-alert/moodle-core-notification-alert-debug.js
lib/yui/build/moodle-core-notification-alert/moodle-core-notification-alert.js
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm-debug.js
lib/yui/build/moodle-core-notification-confirm/moodle-core-notification-confirm.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue-debug.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue.js
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception-debug.js
lib/yui/build/moodle-core-notification-exception/moodle-core-notification-exception.js
lib/yui/src/blocks/js/blockregion.js
lib/yui/src/blocks/js/blocks.js
lib/yui/src/blocks/js/manager.js
lib/yui/src/dock/js/block.js
lib/yui/src/dock/js/dock.js
lib/yui/src/dock/js/dockeditem.js
lib/yui/src/dock/js/panel.js
lib/yui/src/dock/js/tabheightmanager.js
lib/yui/src/languninstallconfirm/js/languninstallconfirm.js
lib/yui/src/notification/js/ajaxexception.js
lib/yui/src/notification/js/alert.js
lib/yui/src/notification/js/confirm.js
lib/yui/src/notification/js/dialogue.js
lib/yui/src/notification/js/exception.js
lib/yui/src/notification/js/info.js
lib/yui/src/notification/js/shared.js
message/output/airnotifier/message_output_airnotifier.php
message/yui/build/moodle-core_message-messenger/moodle-core_message-messenger-debug.js
message/yui/build/moodle-core_message-messenger/moodle-core_message-messenger.js
message/yui/src/messenger/js/constants.js
message/yui/src/messenger/js/manager.js
message/yui/src/messenger/js/sendmessage.js
mod/assign/amd/build/grading_actions.min.js
mod/assign/amd/build/grading_events.min.js [new file with mode: 0644]
mod/assign/amd/build/grading_navigation.min.js
mod/assign/amd/build/grading_panel.min.js
mod/assign/amd/build/grading_review_panel.min.js
mod/assign/amd/src/grading_actions.js
mod/assign/amd/src/grading_events.js [moved from report/search/settings.php with 62% similarity]
mod/assign/amd/src/grading_navigation.js
mod/assign/amd/src/grading_panel.js
mod/assign/amd/src/grading_review_panel.js
mod/assign/amd/src/participant_selector.js
mod/assign/db/services.php
mod/assign/externallib.php
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotation.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationhighlight.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js
mod/assign/feedback/editpdf/yui/src/editor/js/comment.js
mod/assign/feedback/editpdf/yui/src/editor/js/commentsearch.js
mod/assign/feedback/editpdf/yui/src/editor/js/drawable.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/globals.js
mod/assign/feedback/editpdf/yui/src/editor/js/quickcommentlist.js
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/pix/layout-default.png [new file with mode: 0644]
mod/assign/pix/layout-default.svg [new file with mode: 0644]
mod/assign/pix/layout-expand-left.png [new file with mode: 0644]
mod/assign/pix/layout-expand-left.svg [new file with mode: 0644]
mod/assign/pix/layout-expand-right.png [new file with mode: 0644]
mod/assign/pix/layout-expand-right.svg [new file with mode: 0644]
mod/assign/styles.css
mod/assign/submission/file/locallib.php
mod/assign/submission/file/tests/locallib_test.php [new file with mode: 0644]
mod/assign/submission/onlinetext/locallib.php
mod/assign/submission/onlinetext/tests/locallib_test.php [new file with mode: 0644]
mod/assign/submission_form.php
mod/assign/submissionplugin.php
mod/assign/templates/grading_actions.mustache
mod/assign/templates/grading_app.mustache
mod/assign/tests/externallib_test.php
mod/assign/tests/locallib_test.php
mod/assign/upgrade.txt
mod/assign/version.php
mod/book/classes/external.php
mod/book/locallib.php
mod/book/tests/externallib_test.php
mod/book/tests/search_test.php
mod/chat/classes/external.php
mod/chat/gui_header_js/jsupdate.php
mod/chat/gui_header_js/jsupdated.php
mod/chat/tests/externallib_test.php
mod/choice/classes/external.php
mod/choice/db/access.php
mod/choice/lang/en/choice.php
mod/choice/renderer.php
mod/choice/version.php
mod/data/classes/external.php
mod/data/lib.php
mod/data/tests/externallib_test.php
mod/forum/externallib.php
mod/forum/lib.php
mod/forum/tests/externallib_test.php
mod/forum/tests/lib_test.php
mod/forum/tests/search_test.php
mod/glossary/classes/external.php
mod/glossary/classes/search/entry.php
mod/glossary/lib.php
mod/glossary/tests/search_test.php
mod/imscp/classes/external.php
mod/lesson/pagetypes/numerical.php
mod/lti/amd/build/cartridge_registration_form.min.js
mod/lti/amd/src/cartridge_registration_form.js
mod/lti/amd/src/tool_proxy.js
mod/lti/amd/src/tool_type.js
mod/lti/classes/external.php
mod/lti/locallib.php
mod/lti/tests/externallib_test.php
mod/quiz/classes/external.php
mod/quiz/tests/external_test.php
mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop-debug.js
mod/quiz/yui/build/moodle-mod_quiz-dragdrop/moodle-mod_quiz-dragdrop.js
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-debug.js
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes-min.js
mod/quiz/yui/build/moodle-mod_quiz-toolboxes/moodle-mod_quiz-toolboxes.js
mod/quiz/yui/build/moodle-mod_quiz-util-page/moodle-mod_quiz-util-page-debug.js
mod/quiz/yui/build/moodle-mod_quiz-util-page/moodle-mod_quiz-util-page.js
mod/quiz/yui/src/dragdrop/js/dragdrop.js
mod/quiz/yui/src/dragdrop/js/resource.js
mod/quiz/yui/src/toolboxes/js/resource.js
mod/quiz/yui/src/toolboxes/js/section.js
mod/quiz/yui/src/toolboxes/js/toolbox.js
mod/quiz/yui/src/util/js/page.js
mod/scorm/classes/external.php
mod/scorm/datamodels/scormlib.php
mod/scorm/lang/en/scorm.php
mod/scorm/mod_form.php
mod/scorm/tests/externallib_test.php
mod/survey/backup/moodle2/backup_survey_stepslib.php
mod/survey/classes/external.php
mod/survey/classes/search/activity.php
mod/survey/db/install.xml
mod/survey/db/upgrade.php
mod/survey/lang/en/survey.php
mod/survey/lib.php
mod/survey/mod_form.php
mod/survey/tests/behat/survey_completion.feature [new file with mode: 0644]
mod/survey/tests/externallib_test.php
mod/survey/version.php
mod/wiki/classes/external.php
mod/wiki/tests/externallib_test.php
mod/wiki/tests/search_test.php
mod/workshop/lang/en/workshop.php
mod/workshop/renderer.php
mod/workshop/submission.php
mod/workshop/view.php
my/lib.php
npm-shrinkwrap.json
package.json
question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form-debug.js
question/type/ddimageortext/yui/build/moodle-qtype_ddimageortext-form/moodle-qtype_ddimageortext-form.js
question/type/ddimageortext/yui/src/form/js/form.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form-debug.js
question/type/ddmarker/yui/build/moodle-qtype_ddmarker-form/moodle-qtype_ddmarker-form.js
question/type/ddmarker/yui/src/form/js/form.js
question/type/multichoice/lang/en/qtype_multichoice.php
question/type/multichoice/renderer.php
question/type/multichoice/tests/behat/preview.feature
report/competency/amd/src/grading_popup.js
report/search/classes/output/form.php [deleted file]
report/search/classes/output/renderer.php [deleted file]
report/search/index.php [deleted file]
report/search/lang/en/report_search.php [deleted file]
search/classes/area/base.php
search/classes/area/base_activity.php
search/classes/document.php
search/classes/manager.php
search/classes/output/form/search.php
search/templates/result.mustache
search/tests/document_test.php [new file with mode: 0644]
search/tests/fixtures/mock_search_area.php
search/tests/fixtures/mock_search_engine.php
search/tests/generator/lib.php
search/tests/manager_test.php
theme/bootstrapbase/less/moodle/bootstrapoverride.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/less/moodle/expendable.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/forms.less
theme/bootstrapbase/less/moodle/grade.less
theme/bootstrapbase/less/moodle/message.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/less/moodle/responsive.less
theme/bootstrapbase/less/moodle/search.less
theme/bootstrapbase/less/moodle/templates.less
theme/bootstrapbase/less/moodle/undo.less
theme/bootstrapbase/style/moodle.css
theme/clean/classes/core_renderer.php
user/classes/search/user.php [new file with mode: 0644]
user/externallib.php
user/messageselect.php
user/tests/search_test.php [new file with mode: 0644]
version.php
webservice/upgrade.txt
webservice/upload.php

index 03dc85a..c701962 100644 (file)
--- a/.eslintrc
+++ b/.eslintrc
@@ -9,10 +9,10 @@
   },
   'rules': {
     // See http://eslint.org/docs/rules/ for all rules and explanations of all
-    // rules. Commented out rules with 'DEFINE POLICY' are rules Dan P has flagged
-    // for discussion and possible enable soon.
+    // rules.
+
     // === Possible Errors ===
-    // DEFINE POLICY: 'comma-dangle': ['off', 'always'],
+    'comma-dangle': 'off',
     'no-cond-assign': 'error',
     'no-console': 'error',
     'no-constant-condition': 'error',
@@ -21,7 +21,7 @@
     'no-dupe-args': 'error',
     'no-dupe-keys': 'error',
     'no-duplicate-case': 'error',
-    // Disabled for YUI rollups, enabled by grunt for AMD: 'no-empty': 'error',
+    'no-empty': 'warn',
     'no-empty-character-class': 'error',
     'no-ex-assign': 'error',
     'no-extra-boolean-cast': 'error',
 
     // === Best Practices ===
     // (these mostly match our jshint config)
+    'array-callback-return': 'warn',
+    'block-scoped-var': 'warn',
+    'complexity': 'warn',
+    'consistent-return': 'warn',
     'curly': 'error',
     'dot-notation': 'warn',
     'no-alert': 'warn',
     'no-caller': 'error',
     'no-case-declarations': 'error',
+    'no-div-regex': 'error',
     'no-empty-pattern': 'error',
     'no-empty-function': 'warn',
-    //DEFINE POLICY: 'no-eq-null': 'warn',
+    'no-eq-null': 'error',
     'no-eval': 'error',
-    //DEFINE POLICY: 'no-extra-bind': 'warn',
+    'no-extend-native': 'error',
+    'no-extra-bind': 'warn',
     'no-fallthrough': 'error',
-    //DEFINE POLICY: 'no-implicit-globals': 'warn',
+    'no-floating-decimal': 'warn',
+    // Enabled by grunt for AMD modules: 'no-implicit-globals': 'error',
     'no-implied-eval': 'error',
     'no-invalid-this': 'error',
     'no-iterator': 'error',
     'no-native-reassign': 'warn',
     'no-new-func': 'error',
     'no-new-wrappers': 'error',
-    // DEFINE POLICY: no-octal: "error"
-    // DEFINE POLICY: no-octal-escape: "error"
+    'no-octal': 'error',
+    'no-octal-escape': 'error',
     'no-proto': 'error',
     'no-redeclare': 'warn',
     'no-return-assign': 'error',
     'no-script-url': 'error',
     'no-self-assign': 'error',
     'no-self-compare': 'error',
+    'no-sequences': 'warn',
+    'no-throw-literal': 'warn',
     'no-unmodified-loop-condition': 'error',
-    // Disabled for YUI rollups, enabled by grunt for AMD: 'no-unused-expressions': 'error',
+    'no-unused-expressions': 'error',
     'no-unused-labels': 'error',
-    //DEFINE POLICY: 'no-useless-call': 'error',
+    'no-useless-call': 'warn',
     'no-useless-escape': 'warn',
-    //DEFINE POLICY: 'no-with': 'error',
+    'no-with': 'error',
     'wrap-iife': ['error', 'any'],
 
     // === Variables ===
     'no-delete-var': 'error',
-    // Disabled for YUI rollups, enabled by grunt for AMD: 'no-undef': 'off',
-    //DEFINE POLICY: 'no-undef-init': 'error',
-    // Disabled for YUI rollups, enabled by grunt for AMD: 'no-unused-vars': 'error',
+    'no-undef': 'error',
+    'no-undef-init': 'error',
+    'no-unused-vars': ['error', { 'caughtErrors': 'none', 'argsIgnorePattern': "(e|event)" }],
 
     // === Stylistic Issues ===
     'array-bracket-spacing': 'warn',
     'lines-around-comment': 'off',
     'max-len': ['error', 132],
     'max-lines': 'off',
-    // DEFINE POLICY: turn on some of these max values?
-    'max-depth': 'off',
-    'max-nested-callbacks': 'off',
+    'max-depth': 'warn',
+    'max-nested-callbacks': ['warn', 5],
     'max-params': 'off',
     'max-statements': 'off',
-    'max-statements-per-line': 'off',
+    'max-statements-per-line': ['warn', { max: 2 }],
     'new-cap': ['warn', { 'properties': false }],
     'new-parens': 'warn',
     'newline-after-var': 'off',
     'newline-before-return': 'off',
-    // REVIST POLICY: 'newline-per-chained-call': 'warn',
+    'newline-per-chained-call': 'off',
     'no-array-constructor': 'off',
     'no-bitwise': 'error',
     'no-continue': 'off',
     'no-ternary': 'off',
     'no-trailing-spaces': 'error',
     'no-underscore-dangle': 'off',
-    // DEFINE POLICY: 'no-unneeded-ternary': 'off',
+    'no-unneeded-ternary': 'off',
     'no-whitespace-before-property': 'warn',
-    // DEFINE POLICY: 'object-curly-newline': 'off,
-    // DEFINE POLICY: 'object-curly-spacing': 'off',
-    // DEFINE POLICY: 'object-property-newline': 'off',
+    'object-curly-newline': 'off',
+    'object-curly-spacing': 'warn',
+    'object-property-newline': 'off',
     'one-var': 'off',
-    // DEFINE POLICY: 'one-var-declaration-per-line': 'off',
+    'one-var-declaration-per-line': ['warn', 'initializations'],
     'operator-assignment': 'off',
     'operator-linebreak': 'off',
     'padded-blocks': 'off',
-    // DEFINE POLICY: 'quote-props': 'off',
+    'quote-props': ['warn', 'as-needed', {'unnecessary': false, 'keywords': true, 'numbers': true}],
     'quotes': 'off',
     'require-jsdoc': 'warn',
     'semi': 'error',
index ee94a05..b93ac60 100644 (file)
--- a/.jshintrc
+++ b/.jshintrc
@@ -1,3 +1,6 @@
+// NOTE: We use eslint now. This file is used only by shifter. We keep the configuration
+// here because shifter uses jshint after modules have been concating. Eslint can't
+// currently do this.
 {
     "asi":          false,
     "bitwise":      true,
index d1438f3..5a32a60 100644 (file)
@@ -14,15 +14,13 @@ language: php
 php:
     # We only run the highest and lowest supported versions to reduce the load on travis-ci.org.
     - 7.0
-    # - 5.6
-    # - 5.5
-    - 5.4
+    - 5.6
 
 env:
     # Although we want to run these jobs and see failures as quickly as possible, we also want to get the slowest job to
     # start first so that the total run time is not too high.
     #
-    # We only run MySQL on PHP 5.6, so run that first.
+    # We only run MySQL on PHP 7.0, so run that first.
     # CI Tests should be second-highest in priority as these only take <= 60 seconds to run under normal circumstances.
     # Postgres is significantly is pretty reasonable in its run-time.
 
@@ -50,17 +48,13 @@ matrix:
     exclude:
         # MySQL - it's just too slow.
         # Exclude it on all versions except for 7.0
-        # - env: DB=mysqli   TASK=PHPUNIT
-        #   php: 5.6
-        #
-        # - env: DB=mysqli   TASK=PHPUNIT
-        #   php: 5.5
 
         - env: DB=mysqli   TASK=PHPUNIT
-          php: 5.4
+          php: 5.6
 
+       # One grunt execution is enough.
         - env: DB=none     TASK=GRUNT
-          php: 5.4
+          php: 5.6
 
         # Moodle 2.7 is not compatible with PHP 7 for the upgrade test.
         - env: DB=pgsql    TASK=UPGRADE
index b728341..4e22a6e 100644 (file)
@@ -101,25 +101,29 @@ module.exports = function(grunt) {
 
     // Project configuration.
     grunt.initConfig({
-        jshint: {
-            options: {jshintrc: '.jshintrc'},
-            amd: { src: amdSrc }
-        },
         eslint: {
             // Even though warnings dont stop the build we don't display warnings by default because
             // at this moment we've got too many core warnings.
-            options: { quiet: !grunt.option('show-lint-warnings') },
-            // Check AMD files. We add some stricter rules which we can't apply to the default configuration due
-            // to YUI rollups.
+            options: {quiet: !grunt.option('show-lint-warnings')},
             amd: {
               src: amdSrc,
-              options: {
-                  rules: {'no-undef': 'error', 'no-unused-vars': 'error', 'no-empty': 'error', 'no-unused-expressions': 'error'}
+              // Check AMD with some slightly stricter rules.
+              rules: {
+                'no-unused-vars': 'error',
+                'no-implicit-globals': 'error'
               }
             },
             // Check YUI module source files.
             yui: {
                src: ['**/yui/src/**/*.js', '!*/**/yui/src/*/meta/*.js'],
+               options: {
+                   // Disable some rules which we can't safely define for YUI rollups.
+                   rules: {
+                     'no-undef': 'off',
+                     'no-unused-vars': 'off',
+                     'no-unused-expressions': 'off'
+                   }
+               }
             }
         },
         uglify: {
@@ -286,7 +290,8 @@ module.exports = function(grunt) {
     var changedFiles = Object.create(null);
     var onChange = grunt.util._.debounce(function() {
           var files = Object.keys(changedFiles);
-          grunt.config('jshint.amd.src', files);
+          grunt.config('eslint.amd.src', files);
+          grunt.config('eslint.yui.src', files);
           grunt.config('uglify.amd.files', [{ expand: true, src: files, rename: uglifyRename }]);
           grunt.config('shifter.options.paths', files);
           changedFiles = Object.create(null);
@@ -299,7 +304,6 @@ module.exports = function(grunt) {
 
     // Register NPM tasks.
     grunt.loadNpmTasks('grunt-contrib-uglify');
-    grunt.loadNpmTasks('grunt-contrib-jshint');
     grunt.loadNpmTasks('grunt-contrib-less');
     grunt.loadNpmTasks('grunt-contrib-watch');
     grunt.loadNpmTasks('grunt-eslint');
@@ -308,7 +312,7 @@ module.exports = function(grunt) {
     grunt.registerTask('shifter', 'Run Shifter against the current directory', tasks.shifter);
     grunt.registerTask('ignorefiles', 'Generate ignore files for linters', tasks.ignorefiles);
     grunt.registerTask('yui', ['eslint:yui', 'shifter']);
-    grunt.registerTask('amd', ['eslint:amd', 'jshint', 'uglify']);
+    grunt.registerTask('amd', ['eslint:amd', 'uglify']);
     grunt.registerTask('js', ['amd', 'yui']);
 
     // Register CSS taks.
index 9f3c0df..0b6c5ca 100644 (file)
@@ -147,10 +147,10 @@ define('PHPUNIT_TEST', false);
 define('IGNORE_COMPONENT_CACHE', true);
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.4.4") < 0) {
+if (version_compare(phpversion(), "5.6.5") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).\n");
     fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
     exit(1);
 }
index b66805e..f41263e 100644 (file)
@@ -63,10 +63,10 @@ Example:
 ";
 
 // Check that PHP is of a sufficient version
-if (version_compare(phpversion(), "5.4.4") < 0) {
+if (version_compare(phpversion(), "5.6.5") < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it after installib
-    fwrite(STDERR, "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).\n");
+    fwrite(STDERR, "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).\n");
     fwrite(STDERR, "Please upgrade your server software or install older Moodle version.\n");
     exit(1);
 }
index 27f4ca9..a1535d1 100644 (file)
@@ -29,10 +29,23 @@ define('CLI_SCRIPT', true);
 require(__DIR__.'/../../config.php');
 require_once($CFG->libdir.'/clilib.php');      // cli only functions
 
+// Define the input options.
+$longparams = array(
+        'help' => false,
+        'username' => '',
+        'password' => '',
+        'ignore-password-policy' => false
+);
+
+$shortparams = array(
+        'h' => 'help',
+        'u' => 'username',
+        'p' => 'password',
+        'i' => 'ignore-password-policy'
+);
 
 // now get cli options
-list($options, $unrecognized) = cli_get_params(array('help'=>false),
-                                               array('h'=>'help'));
+list($options, $unrecognized) = cli_get_params($longparams, $shortparams);
 
 if ($unrecognized) {
     $unrecognized = implode("\n  ", $unrecognized);
@@ -47,29 +60,43 @@ There are no security checks here because anybody who is able to
 execute this file may execute any PHP too.
 
 Options:
--h, --help            Print out this help
+-h, --help                    Print out this help
+-u, --username=username       Specify username to change
+-p, --password=newpassword    Specify new password
+--ignore-password-policy      Ignore password policy when setting password
 
 Example:
 \$sudo -u www-data /usr/bin/php admin/cli/reset_password.php
+\$sudo -u www-data /usr/bin/php admin/cli/reset_password.php --username=rosaura --password=jiu3jiu --ignore-password-policy
 "; //TODO: localize - to be translated later when everything is finished
 
     echo $help;
     die;
 }
-cli_heading('Password reset'); // TODO: localize
-$prompt = "enter username (manual authentication only)"; // TODO: localize
-$username = cli_input($prompt);
+if ($options['username'] == '' ) {
+    cli_heading('Password reset'); // TODO: localize.
+    $prompt = "enter username (manual authentication only)"; // TODO: localize.
+    $username = cli_input($prompt);
+} else {
+    $username = $options['username'];
+}
 
 if (!$user = $DB->get_record('user', array('auth'=>'manual', 'username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
     cli_error("Can not find user '$username'");
 }
 
-$prompt = "Enter new password"; // TODO: localize
-$password = cli_input($prompt);
+if ($options['password'] == '' ) {
+    $prompt = "Enter new password"; // TODO: localize.
+    $password = cli_input($prompt);
+} else {
+    $password = $options['password'];
+}
 
 $errmsg = '';//prevent eclipse warning
-if (!check_password_policy($password, $errmsg)) {
-    cli_error($errmsg);
+if (!$options['ignore-password-policy'] ) {
+    if (!check_password_policy($password, $errmsg)) {
+        cli_error($errmsg);
+    }
 }
 
 $hashedpassword = hash_internal_user_password($password);
@@ -78,4 +105,4 @@ $DB->set_field('user', 'password', $hashedpassword, array('id'=>$user->id));
 
 echo "Password changed\n";
 
-exit(0); // 0 means success
\ No newline at end of file
+exit(0); // 0 means success.
index f281b45..7bef8fc 100644 (file)
       </CUSTOM_CHECK>
     </CUSTOM_CHECKS>
   </MOODLE>
+  <MOODLE version="3.2" requires="2.7">
+    <UNICODE level="required">
+      <FEEDBACK>
+        <ON_ERROR message="unicoderequired" />
+      </FEEDBACK>
+    </UNICODE>
+    <DATABASE level="required">
+      <VENDOR name="mariadb" version="5.5.31" />
+      <VENDOR name="mysql" version="5.5.31" />
+      <VENDOR name="postgres" version="9.1" />
+      <VENDOR name="mssql" version="10.0" />
+      <VENDOR name="oracle" version="10.2" />
+    </DATABASE>
+    <PHP version="5.6.5" level="required">
+    </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
+    <PHP_EXTENSIONS>
+      <PHP_EXTENSION name="iconv" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="iconvrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="mbstring" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="mbstringrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="curl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="curlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="openssl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opensslrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="tokenizer" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="tokenizerrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlrpc" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="xmlrpcrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="soap" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="soaprecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="ctype" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ctyperequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zip" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ziprequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zlib" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="gd" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="gdrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="simplexml" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="simplexmlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="spl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="splrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="pcre" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="dom" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xml" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlreader" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="intl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="intlrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="json" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="hash" level="required"/>
+    </PHP_EXTENSIONS>
+    <PHP_SETTINGS>
+      <PHP_SETTING name="memory_limit" value="96M" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="settingmemorylimit" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="file_uploads" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingfileuploads" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="opcache.enable" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opcacherecommended" />
+        </FEEDBACK>
+      </PHP_SETTING>
+    </PHP_SETTINGS>
+    <CUSTOM_CHECKS>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="unsupporteddbstorageengine" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="quizattemptsupgradedmessage" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="slashargumentswarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unsupporteddbtablerowformat" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+      <CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="unoconvwarning" />
+        </FEEDBACK>
+      </CUSTOM_CHECK>
+    </CUSTOM_CHECKS>
+  </MOODLE>
 </COMPATIBILITY_MATRIX>
index 1000f93..382c252 100644 (file)
@@ -30,10 +30,10 @@ if (!file_exists('../config.php')) {
 }
 
 // Check that PHP is of a sufficient version as soon as possible
-if (version_compare(phpversion(), '5.4.4') < 0) {
+if (version_compare(phpversion(), '5.6.5') < 0) {
     $phpversion = phpversion();
     // do NOT localise - lang strings would not work here and we CAN NOT move it to later place
-    echo "Moodle 2.7 or later requires at least PHP 5.4.4 (currently using version $phpversion).<br />";
+    echo "Moodle 3.2 or later requires at least PHP 5.6.5 (currently using version $phpversion).<br />";
     echo "Please upgrade your server software or install older Moodle version.";
     die();
 }
index 717b8f2..2b66c9f 100644 (file)
@@ -36,6 +36,8 @@ require_login($course, false, $cm);
 require_capability('moodle/role:review', $context);
 require_sesskey();
 
+$OUTPUT->header();
+
 list($overridableroles, $overridecounts, $nameswithcounts) = get_overridable_roles($context,
         ROLENAME_BOTH, true);
 
diff --git a/admin/searchareas.php b/admin/searchareas.php
new file mode 100644 (file)
index 0000000..884d5b8
--- /dev/null
@@ -0,0 +1,180 @@
+<?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/>.
+
+/**
+ * Manage global search areas.
+ *
+ * @package   core_search
+ * @copyright 2016 Dan Poltawski <dan@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+require_once(__DIR__ . '/../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+admin_externalpage_setup('searchareas');
+
+$areaid = optional_param('areaid', null, PARAM_ALPHAEXT);
+$action = optional_param('action', null, PARAM_ALPHA);
+
+try {
+    $searchmanager = \core_search\manager::instance();
+} catch (core_search\engine_exception $searchmanagererror) {
+    // Continue, we return an error later depending on the requested action.
+}
+
+echo $OUTPUT->header();
+
+if ($action) {
+    require_sesskey();
+
+    if ($areaid) {
+        // We need to check that the area exists.
+        $area = \core_search\manager::get_search_area($areaid);
+        if ($area === false) {
+            throw new moodle_exception('invalidrequest');
+        }
+    }
+
+    // All actions but enable/disable need the search engine to be ready.
+    if ($action !== 'enable' && $action !== 'disable') {
+        if (!empty($searchmanagererror)) {
+            throw $searchmanagererror;
+        }
+    }
+
+    switch ($action) {
+        case 'enable':
+            $area->set_enabled(true);
+            echo $OUTPUT->notification(get_string('searchareaenabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        case 'disable':
+            $area->set_enabled(false);
+            echo $OUTPUT->notification(get_string('searchareadisabled', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        case 'delete':
+            $search = \core_search\manager::instance();
+            $search->delete_index($areaid);
+            echo $OUTPUT->notification(get_string('searchindexdeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        case 'indexall':
+            $searchmanager->index();
+            echo $OUTPUT->notification(get_string('searchindexupdated', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        case 'reindexall':
+            $searchmanager->index(true);
+            echo $OUTPUT->notification(get_string('searchreindexed', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        case 'deleteall':
+            $searchmanager->delete_index();
+            echo $OUTPUT->notification(get_string('searchalldeleted', 'admin'), \core\output\notification::NOTIFY_SUCCESS);
+            break;
+        default:
+            throw new moodle_exception('invalidaction');
+            break;
+    }
+}
+
+$searchareas = \core_search\manager::get_search_areas_list();
+if (empty($searchmanagererror)) {
+    $areasconfig = $searchmanager->get_areas_config($searchareas);
+} else {
+    $areasconfig = false;
+}
+
+if (!empty($searchmanagererror)) {
+    $errorstr = get_string($searchmanagererror->errorcode, $searchmanagererror->module);
+    echo $OUTPUT->notification($errorstr, \core\output\notification::NOTIFY_ERROR);
+} else {
+    echo $OUTPUT->notification(get_string('indexinginfo', 'admin'), \core\output\notification::NOTIFY_INFO);
+}
+
+$table = new html_table();
+$table->id = 'core-search-areas';
+
+$table->head = array(get_string('searcharea', 'search'), get_string('enable'), get_string('newestdocindexed', 'admin'),
+    get_string('searchlastrun', 'admin'), get_string('searchindexactions', 'admin'));
+
+foreach ($searchareas as $area) {
+    $areaid = $area->get_area_id();
+    $columns = array(new html_table_cell($area->get_visible_name()));
+
+    if ($area->is_enabled()) {
+        $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('disable', $areaid),
+            new pix_icon('t/hide', get_string('disable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
+            null, array('title' => get_string('disable')));
+
+        if ($areasconfig) {
+            $columns[] = $areasconfig[$areaid]->lastindexrun;
+
+            if ($areasconfig[$areaid]->indexingstart) {
+                $timediff = $areasconfig[$areaid]->indexingend - $areasconfig[$areaid]->indexingstart;
+                $laststatus = $timediff . ' , ' .
+                    $areasconfig[$areaid]->docsprocessed . ' , ' .
+                    $areasconfig[$areaid]->recordsprocessed . ' , ' .
+                    $areasconfig[$areaid]->docsignored;
+            } else {
+                $laststatus = '';
+            }
+            $columns[] = $laststatus;
+            $columns[] = html_writer::link(admin_searcharea_action_url('delete', $areaid), 'Delete index');
+
+        } else {
+            $blankrow = new html_table_cell(get_string('searchnotavailable', 'admin'));
+            $blankrow->colspan = 3;
+            $columns[] = $blankrow;
+        }
+
+    } else {
+        $columns[] = $OUTPUT->action_icon(admin_searcharea_action_url('enable', $areaid),
+            new pix_icon('t/show', get_string('enable'), 'moodle', array('title' => '', 'class' => 'iconsmall')),
+                null, array('title' => get_string('enable')));
+
+        $blankrow = new html_table_cell(get_string('searchareadisabled', 'admin'));
+        $blankrow->colspan = 3;
+        $columns[] = $blankrow;
+    }
+    $row = new html_table_row($columns);
+    $table->data[] = $row;
+}
+
+// Cross-search area tasks.
+$options = array();
+if (!empty($searchmanagererror)) {
+    $options['disabled'] = true;
+}
+echo $OUTPUT->box_start('search-areas-actions');
+echo $OUTPUT->single_button(admin_searcharea_action_url('indexall'), get_string('searchupdateindex', 'admin'), 'get', $options);
+echo $OUTPUT->single_button(admin_searcharea_action_url('reindexall'), get_string('searchreindexindex', 'admin'), 'get', $options);
+echo $OUTPUT->single_button(admin_searcharea_action_url('deleteall'), get_string('searchdeleteindex', 'admin'), 'get', $options);
+echo $OUTPUT->box_end();
+
+echo html_writer::table($table);
+echo $OUTPUT->footer();
+
+/**
+ * Helper for generating url for management actions.
+ *
+ * @param string $action
+ * @param string $areaid
+ * @return moodle_url
+ */
+function admin_searcharea_action_url($action, $areaid = false) {
+    $params = array('action' => $action, 'sesskey' => sesskey());
+    if ($areaid) {
+        $params['areaid'] = $areaid;
+    }
+    return new moodle_url('/admin/searchareas.php', $params);
+}
index 680bd3d..0711b23 100644 (file)
@@ -528,15 +528,9 @@ if ($hassiteconfig) {
     $temp->add(new admin_setting_configselect('searchengine',
                                 new lang_string('selectsearchengine', 'admin'), '', 'solr', $engines));
 
-    // Enable search areas.
-    $temp->add(new admin_setting_heading('searchareasheading', new lang_string('availablesearchareas', 'admin'), ''));
-    $searchareas = \core_search\manager::get_search_areas_list();
-    foreach ($searchareas as $areaid => $searcharea) {
-        list($componentname, $varname) = $searcharea->get_config_var_name();
-        $temp->add(new admin_setting_configcheckbox($componentname . '/' . $varname . '_enabled', $searcharea->get_visible_name(true),
-            '', 1, 1, 0));
-    }
     $ADMIN->add('searchplugins', $temp);
+    $ADMIN->add('searchplugins', new admin_externalpage('searchareas', new lang_string('searchareas', 'admin'),
+        new moodle_url('/admin/searchareas.php')));
 
     core_collator::asort_objects_by_property($pages, 'visiblename');
     foreach ($pages as $page) {
index 90062ca..0d03dea 100644 (file)
Binary files a/admin/tool/lp/amd/build/actionselector.min.js and b/admin/tool/lp/amd/build/actionselector.min.js differ
index c14f771..96285e7 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencies.min.js and b/admin/tool/lp/amd/build/competencies.min.js differ
index 2ca219d..415efaa 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 61eec30..ec656c2 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencypicker.min.js and b/admin/tool/lp/amd/build/competencypicker.min.js differ
index faf4bd4..2117b30 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencypicker_user_plans.min.js and b/admin/tool/lp/amd/build/competencypicker_user_plans.min.js differ
index 8a0f0a2..9f5801d 100644 (file)
Binary files a/admin/tool/lp/amd/build/competencyruleconfig.min.js and b/admin/tool/lp/amd/build/competencyruleconfig.min.js differ
index ac0c28f..6ea9624 100644 (file)
Binary files a/admin/tool/lp/amd/build/grade_user_competency_inline.min.js and b/admin/tool/lp/amd/build/grade_user_competency_inline.min.js differ
index 7ab1169..0259b87 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 d7732ee..6392ca6 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
index d590b7e..aeb8b9e 100644 (file)
Binary files a/admin/tool/lp/amd/build/planactions.min.js and b/admin/tool/lp/amd/build/planactions.min.js differ
index db0ffd8..4c5b700 100644 (file)
Binary files a/admin/tool/lp/amd/build/scaleconfig.min.js and b/admin/tool/lp/amd/build/scaleconfig.min.js differ
index e479a60..a69a9d8 100644 (file)
Binary files a/admin/tool/lp/amd/build/user_evidence_actions.min.js and b/admin/tool/lp/amd/build/user_evidence_actions.min.js differ
index 8b32a48..582478b 100644 (file)
@@ -87,13 +87,13 @@ define(['jquery',
             self._selectedValue = $("input[type='radio']:checked").val();
             self._find('[data-action="action-selector-confirm"]').removeAttr('disabled');
             self._refresh.bind(self);
-        }.bind(self));
+        });
 
         // Add listener for cancel.
         self._find('[data-action="action-selector-cancel"]').click(function(e) {
             e.preventDefault();
             self.close();
-        }.bind(self));
+        });
 
         // Add listener for confirm.
         self._find('[data-action="action-selector-confirm"]').click(function(e) {
@@ -101,9 +101,9 @@ define(['jquery',
             if (!self._selectedValue.length) {
                 return;
             }
-            self._trigger('save', { action: self._selectedValue });
+            self._trigger('save', {action: self._selectedValue});
             self.close();
-        }.bind(self));
+        });
     };
 
     /**
@@ -131,7 +131,7 @@ define(['jquery',
                 html,
                 self._afterRender.bind(self)
             );
-        }.bind(self)).fail(Notification.exception);
+        }).fail(Notification.exception);
     };
 
     /**
@@ -156,7 +156,7 @@ define(['jquery',
         return self._render().then(function(html) {
             self._find('[data-region="action-selector"]').replaceWith(html);
             self._afterRender();
-        }.bind(self));
+        });
     };
 
     /**
index 8b5ae2e..79e1aa9 100644 (file)
@@ -59,13 +59,13 @@ define(['jquery',
             function(movestring) {
                 dragdrop.dragdrop('movecompetency',
                                   movestring,
-                                  { identifier: 'movecompetency', component: 'tool_lp'},
-                                  { identifier: 'movecompetencyafter', component: 'tool_lp'},
+                                  {identifier: 'movecompetency', component: 'tool_lp'},
+                                  {identifier: 'movecompetencyafter', component: 'tool_lp'},
                                   'drag-samenode',
                                   'drag-parentnode',
                                   'drag-handlecontainer',
                                   function(drag, drop) {
-                                      localthis.handleDrop.call(localthis, drag, drop);
+                                      localthis.handleDrop(drag, drop);
                                   });
             }
         ).fail(notification.exception);
@@ -89,21 +89,21 @@ define(['jquery',
             requests = ajax.call([
                 {
                     methodname: 'core_competency_reorder_course_competency',
-                    args: { courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+                    args: {courseid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
                 }
             ]);
         } else if (localthis.itemtype == 'template') {
             requests = ajax.call([
                 {
                     methodname: 'core_competency_reorder_template_competency',
-                    args: { templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+                    args: {templateid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
                 }
             ]);
         } else if (localthis.itemtype == 'plan') {
             requests = ajax.call([
                 {
                     methodname: 'core_competency_reorder_plan_competency',
-                    args: { planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid }
+                    args: {planid: localthis.itemid, competencyidfrom: fromid, competencyidto: toid}
                 }
             ]);
         } else {
@@ -139,12 +139,12 @@ define(['jquery',
                     $.each(compIds, function(index, compId) {
                         requests.push({
                             methodname: 'core_competency_add_competency_to_course',
-                            args: { courseid: self.itemid, competencyid: compId }
+                            args: {courseid: self.itemid, competencyid: compId}
                         });
                     });
                     requests.push({
                         methodname: 'tool_lp_data_for_course_competencies_page',
-                        args: { courseid: self.itemid }
+                        args: {courseid: self.itemid}
                     });
 
                     pagerender = 'tool_lp/course_competencies_page';
@@ -156,12 +156,12 @@ define(['jquery',
                     $.each(compIds, function(index, compId) {
                         requests.push({
                             methodname: 'core_competency_add_competency_to_template',
-                            args: { templateid: self.itemid, competencyid: compId }
+                            args: {templateid: self.itemid, competencyid: compId}
                         });
                     });
                     requests.push({
                         methodname: 'tool_lp_data_for_template_competencies_page',
-                        args: { templateid: self.itemid, pagecontext: { contextid: self.pageContextId }}
+                        args: {templateid: self.itemid, pagecontext: {contextid: self.pageContextId}}
                     });
                     pagerender = 'tool_lp/template_competencies_page';
                     pageregion = 'templatecompetenciespage';
@@ -171,12 +171,12 @@ define(['jquery',
                     $.each(compIds, function(index, compId) {
                         requests.push({
                             methodname: 'core_competency_add_competency_to_plan',
-                            args: { planid: self.itemid, competencyid: compId }
+                            args: {planid: self.itemid, competencyid: compId}
                         });
                     });
                     requests.push({
                          methodname: 'tool_lp_data_for_plan_page',
-                         args: { planid: self.itemid}
+                         args: {planid: self.itemid}
                     });
                     pagerender = 'tool_lp/plan_page';
                     pageregion = 'plan-page';
@@ -209,28 +209,28 @@ define(['jquery',
         // Delete the link and reload the page template.
         if (localthis.itemtype == 'course') {
             requests = ajax.call([
-                { methodname: 'core_competency_remove_competency_from_course',
-                    args: { courseid: localthis.itemid, competencyid: deleteid } },
-                { methodname: 'tool_lp_data_for_course_competencies_page',
-                    args: { courseid: localthis.itemid } }
+                {methodname: 'core_competency_remove_competency_from_course',
+                    args: {courseid: localthis.itemid, competencyid: deleteid}},
+                {methodname: 'tool_lp_data_for_course_competencies_page',
+                    args: {courseid: localthis.itemid}}
             ]);
             pagerender = 'tool_lp/course_competencies_page';
             pageregion = 'coursecompetenciespage';
         } else if (localthis.itemtype == 'template') {
             requests = ajax.call([
-                { methodname: 'core_competency_remove_competency_from_template',
-                    args: { templateid: localthis.itemid, competencyid: deleteid } },
-                { methodname: 'tool_lp_data_for_template_competencies_page',
-                    args: { templateid: localthis.itemid, pagecontext: { contextid: localthis.pageContextId } } }
+                {methodname: 'core_competency_remove_competency_from_template',
+                    args: {templateid: localthis.itemid, competencyid: deleteid}},
+                {methodname: 'tool_lp_data_for_template_competencies_page',
+                    args: {templateid: localthis.itemid, pagecontext: {contextid: localthis.pageContextId}}}
             ]);
             pagerender = 'tool_lp/template_competencies_page';
             pageregion = 'templatecompetenciespage';
         } else if (localthis.itemtype == 'plan') {
             requests = ajax.call([
-                { methodname: 'core_competency_remove_competency_from_plan',
-                    args: { planid: localthis.itemid, competencyid: deleteid } },
-                { methodname: 'tool_lp_data_for_plan_page',
-                    args: { planid: localthis.itemid } }
+                {methodname: 'core_competency_remove_competency_from_plan',
+                    args: {planid: localthis.itemid, competencyid: deleteid}},
+                {methodname: 'tool_lp_data_for_plan_page',
+                    args: {planid: localthis.itemid}}
             ]);
             pagerender = 'tool_lp/plan_page';
             pageregion = 'plan-page';
@@ -268,15 +268,15 @@ define(['jquery',
 
         requests = ajax.call([{
             methodname: 'core_competency_read_competency',
-            args: { id: deleteid }
+            args: {id: deleteid}
         }]);
 
         requests[0].done(function(competency) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: message, component: 'tool_lp', param: competency.shortname },
-                { key: 'confirm', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: message, component: 'tool_lp', param: competency.shortname},
+                {key: 'confirm', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -308,10 +308,10 @@ define(['jquery',
                 var coursecompetencyid = $(e.target).data('id');
                 var ruleoutcome = $(e.target).val();
                 requests = ajax.call([
-                    { methodname: 'core_competency_set_course_competency_ruleoutcome',
-                      args: { coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome } },
-                    { methodname: 'tool_lp_data_for_course_competencies_page',
-                      args: { courseid: localthis.itemid } }
+                    {methodname: 'core_competency_set_course_competency_ruleoutcome',
+                      args: {coursecompetencyid: coursecompetencyid, ruleoutcome: ruleoutcome}},
+                    {methodname: 'tool_lp_data_for_course_competencies_page',
+                      args: {courseid: localthis.itemid}}
                 ]);
 
                 requests[1].done(function(context) {
index 36b2a68..829b892 100644 (file)
@@ -46,16 +46,16 @@ define(['jquery',
         getAll: function() {
             var self = this;
             return Str.get_strings([
-                { key: 'competencyoutcome_none', component: 'tool_lp' },
-                { key: 'competencyoutcome_evidence', component: 'tool_lp' },
-                { key: 'competencyoutcome_recommend', component: 'tool_lp' },
-                { key: 'competencyoutcome_complete', component: 'tool_lp' },
+                {key: 'competencyoutcome_none', component: 'tool_lp'},
+                {key: 'competencyoutcome_evidence', component: 'tool_lp'},
+                {key: 'competencyoutcome_recommend', component: 'tool_lp'},
+                {key: 'competencyoutcome_complete', component: 'tool_lp'},
             ]).then(function(strings) {
                 var outcomes = {};
-                outcomes[self.NONE] = { code: self.NONE, name: strings[0] };
-                outcomes[self.EVIDENCE] = { code: self.EVIDENCE, name: strings[1] };
-                outcomes[self.RECOMMEND] = { code: self.RECOMMEND, name: strings[2] };
-                outcomes[self.COMPLETE] = { code: self.COMPLETE, name: strings[3] };
+                outcomes[self.NONE] = {code: self.NONE, name: strings[0]};
+                outcomes[self.EVIDENCE] = {code: self.EVIDENCE, name: strings[1]};
+                outcomes[self.RECOMMEND] = {code: self.RECOMMEND, name: strings[2]};
+                outcomes[self.COMPLETE] = {code: self.COMPLETE, name: strings[3]};
                 return outcomes;
             });
         },
index db4bfe4..5c4ec17 100644 (file)
@@ -115,7 +115,7 @@ define(['jquery',
             children = this._tree.getChildren(this._competency.id),
             context,
             config = {
-                base: { points: 2 },
+                base: {points: 2},
                 competencies: []
             };
 
index c9c37bc..522e827 100644 (file)
@@ -82,10 +82,10 @@ define(['jquery',
 
         if (parent !== null && treeModel.hasRule(parent.id)) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname },
-                { key: 'yes', component: 'core' },
-                { key: 'no', component: 'core' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'addingcompetencywillresetparentrule', component: 'tool_lp', param: parent.shortname},
+                {key: 'yes', component: 'core'},
+                {key: 'no', component: 'core'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0],
@@ -108,11 +108,11 @@ define(['jquery',
         var frameworkid = $('[data-region="filtercompetencies"]').data('frameworkid');
         var requests = ajax.call([{
             methodname: 'core_competency_set_parent_competency',
-            args: { competencyid: moveSource, parentid: moveTarget }
+            args: {competencyid: moveSource, parentid: moveTarget}
         }, {
             methodname: 'tool_lp_data_for_competencies_manage_page',
-            args: { competencyframeworkid: frameworkid,
-                    search: $('[data-region="filtercompetencies"] input').val() }
+            args: {competencyframeworkid: frameworkid,
+                    search: $('[data-region="filtercompetencies"] input').val()}
         }]);
         requests[1].done(reloadPage).fail(notification.exception);
     };
@@ -153,10 +153,10 @@ define(['jquery',
         // Show confirm, and/or do the things.
         if (showConfirm) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: confirmMessage, component: 'tool_lp' },
-                { key: 'yes', component: 'moodle' },
-                { key: 'no', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: confirmMessage, component: 'tool_lp'},
+                {key: 'yes', component: 'moodle'},
+                {key: 'no', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -248,7 +248,8 @@ define(['jquery',
         $.when.apply(null, requests).done(function(competencies, framework) {
 
             // Expand the list of competencies into a tree.
-            var i, competenciestree = [];
+            var i;
+            var competenciestree = [];
             for (i = 0; i < competencies.length; i++) {
                 var onecompetency = competencies[i];
                 if (onecompetency.parentid == "0") {
@@ -260,9 +261,9 @@ define(['jquery',
             }
 
             str.get_strings([
-                { key: 'movecompetency', component: 'tool_lp', param: competency.shortname },
-                { key: 'move', component: 'tool_lp' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'movecompetency', component: 'tool_lp', param: competency.shortname},
+                {key: 'move', component: 'tool_lp'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
 
                 var context = {
@@ -330,8 +331,8 @@ define(['jquery',
 
         var requests = ajax.call([{
             methodname: 'tool_lp_data_for_competencies_manage_page',
-            args: { competencyframeworkid: frameworkid,
-                    search: $('[data-region="filtercompetencies"] input').val() }
+            args: {competencyframeworkid: frameworkid,
+                    search: $('[data-region="filtercompetencies"] input').val()}
         }]);
         requests[0].done(reloadPage).fail(notification.exception);
     };
@@ -345,11 +346,11 @@ define(['jquery',
         var competency = $('[data-region="competencyactions"]').data('competency');
         var requests = ajax.call([{
             methodname: 'core_competency_move_up_competency',
-            args: { id: competency.id }
+            args: {id: competency.id}
         }, {
             methodname: 'tool_lp_data_for_competencies_manage_page',
-            args: { competencyframeworkid: competency.competencyframeworkid,
-                    search: $('[data-region="filtercompetencies"] input').val() }
+            args: {competencyframeworkid: competency.competencyframeworkid,
+                    search: $('[data-region="filtercompetencies"] input').val()}
         }]);
         requests[1].done(reloadPage).fail(notification.exception);
     };
@@ -363,11 +364,11 @@ define(['jquery',
         var competency = $('[data-region="competencyactions"]').data('competency');
         var requests = ajax.call([{
             methodname: 'core_competency_move_down_competency',
-            args: { id: competency.id }
+            args: {id: competency.id}
         }, {
             methodname: 'tool_lp_data_for_competencies_manage_page',
-            args: { competencyframeworkid: competency.competencyframeworkid,
-                    search: $('[data-region="filtercompetencies"] input').val() }
+            args: {competencyframeworkid: competency.competencyframeworkid,
+                    search: $('[data-region="filtercompetencies"] input').val()}
         }]);
         requests[1].done(reloadPage).fail(notification.exception);
     };
@@ -381,7 +382,7 @@ define(['jquery',
 
         var requests = ajax.call([{
             methodname: 'tool_lp_list_courses_using_competency',
-            args: { id: competency.id }
+            args: {id: competency.id}
         }]);
 
         requests[0].done(function(courses) {
@@ -417,13 +418,13 @@ define(['jquery',
                 $.each(compIds, function(index, value) {
                     calls.push({
                         methodname: 'core_competency_add_related_competency',
-                        args: { competencyid: value, relatedcompetencyid: relatedTarget.id }
+                        args: {competencyid: value, relatedcompetencyid: relatedTarget.id}
                     });
                 });
 
                 calls.push({
                     methodname: 'tool_lp_data_for_related_competencies_section',
-                    args: { competencyid: relatedTarget.id }
+                    args: {competencyid: relatedTarget.id}
                 });
 
                 var promises = ajax.call(calls);
@@ -462,7 +463,7 @@ define(['jquery',
         };
         var promise = ajax.call([{
             methodname: 'core_competency_update_competency',
-            args: { competency: update }
+            args: {competency: update}
         }]);
         promise[0].then(function(result) {
             if (result) {
@@ -483,17 +484,17 @@ define(['jquery',
         var competency = $('[data-region="competencyactions"]').data('competency');
         var requests = ajax.call([{
             methodname: 'core_competency_delete_competency',
-            args: { id: competency.id }
+            args: {id: competency.id}
         }, {
             methodname: 'tool_lp_data_for_competencies_manage_page',
-            args: { competencyframeworkid: competency.competencyframeworkid,
-                    search: $('[data-region="filtercompetencies"] input').val() }
+            args: {competencyframeworkid: competency.competencyframeworkid,
+                    search: $('[data-region="filtercompetencies"] input').val()}
         }]);
         requests[0].done(function(success) {
             if (success === false) {
                 str.get_strings([
-                { key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'competencycannotbedeleted', component: 'tool_lp', param: competency.shortname},
+                {key: 'cancel', component: 'moodle'}
                 ]).done(function(strings) {
                     notification.alert(
                         null,
@@ -518,10 +519,10 @@ define(['jquery',
         }
 
         str.get_strings([
-            { key: 'confirm', component: 'moodle' },
-            { key: confirmMessage, component: 'tool_lp', param: competency.shortname },
-            { key: 'delete', component: 'moodle' },
-            { key: 'cancel', component: 'moodle' }
+            {key: 'confirm', component: 'moodle'},
+            {key: confirmMessage, component: 'tool_lp', param: competency.shortname},
+            {key: 'delete', component: 'moodle'},
+            {key: 'cancel', component: 'moodle'}
         ]).done(function(strings) {
             notification.confirm(
                 strings[0], // Confirm.
@@ -598,18 +599,18 @@ define(['jquery',
         var relatedid = this.id.substr(11);
         var competency = $('[data-region="competencyactions"]').data('competency');
         var removeRelated = ajax.call([
-            { methodname: 'core_competency_remove_related_competency',
-              args: { relatedcompetencyid: relatedid, competencyid: competency.id } },
-            { methodname: 'tool_lp_data_for_related_competencies_section',
-              args: { competencyid: competency.id } }
+            {methodname: 'core_competency_remove_related_competency',
+              args: {relatedcompetencyid: relatedid, competencyid: competency.id}},
+            {methodname: 'tool_lp_data_for_related_competencies_section',
+              args: {competencyid: competency.id}}
         ]);
 
         removeRelated[1].done(function(context) {
             templates.render('tool_lp/related_competencies', context).done(function(html) {
                 $('[data-region="relatedcompetencies"]').replaceWith(html);
                 updatedRelatedCompetencies();
-            }.bind(this)).fail(notification.exception);
-        }.bind(this)).fail(notification.exception);
+            }).fail(notification.exception);
+        }).fail(notification.exception);
     };
 
     /**
@@ -636,7 +637,7 @@ define(['jquery',
             selectedCompetencyId = competency.id;
             ajax.call([{
                     methodname: 'core_competency_competency_viewed',
-                    args: { id: competency.id }
+                    args: {id: competency.id}
             }]);
         }
     };
@@ -703,7 +704,7 @@ define(['jquery',
         }).done(function() {
             ajax.call([{
                 methodname: 'tool_lp_data_for_related_competencies_section',
-                args: { competencyid: competency.id },
+                args: {competencyid: competency.id},
                 done: function(context) {
                     return templates.render('tool_lp/related_competencies', context).done(function(html, js) {
                         $('[data-region="relatedcompetencies"]').replaceWith(html);
index 9902eed..012d70b 100644 (file)
@@ -55,7 +55,7 @@ define(['jquery',
     Competencydialogue.prototype.triggerCompetencyViewedEvent = function(competencyId) {
         ajax.call([{
                 methodname: 'core_competency_competency_viewed',
-                args: { id: competencyId }
+                args: {id: competencyId}
         }]);
     };
 
@@ -143,8 +143,8 @@ define(['jquery',
     Competencydialogue.prototype.getCompetencyDataPromise = function(competencyid, options) {
 
         var requests = ajax.call([
-            { methodname: 'tool_lp_data_for_competency_summary',
-              args: { competencyid: competencyid,
+            {methodname: 'tool_lp_data_for_competency_summary',
+              args: {competencyid: competencyid,
                       includerelated: options.includerelated || false,
                       includecourses: options.includecourses || false
                     }
@@ -170,7 +170,7 @@ define(['jquery',
 
             // Instantiate the one instance and delegate event on the body.
             instance = new Competencydialogue();
-            $('body').delegate('[data-action="competency-dialogue"]', 'click', { compdialogue: instance },
+            $('body').delegate('[data-action="competency-dialogue"]', 'click', {compdialogue: instance},
                 instance.clickEventHandler.bind(instance));
         }
     };
index 272d3e0..1aebe05 100644 (file)
@@ -118,7 +118,7 @@ define(['jquery',
                 if (valid) {
                     validIds.push(compId);
                 }
-            }.bind(self));
+            });
 
             self._selectedCompetencies = validIds;
 
@@ -128,14 +128,14 @@ define(['jquery',
             } else {
                 self._find('[data-region="competencylinktree"] [data-action="add"]').removeAttr('disabled');
             }
-        }.bind(self));
+        });
 
         // Add listener for framework change.
         if (!self._singleFramework) {
             self._find('[data-action="chooseframework"]').change(function(e) {
                 self._frameworkId = $(e.target).val();
                 self._loadCompetencies().then(self._refresh.bind(self));
-            }.bind(self));
+            });
         }
 
         // Add listener for search.
@@ -146,13 +146,13 @@ define(['jquery',
             return self._refresh().always(function() {
                 $(e.target).removeAttr('disabled');
             });
-        }.bind(self));
+        });
 
         // Add listener for cancel.
         self._find('[data-region="competencylinktree"] [data-action="cancel"]').click(function(e) {
             e.preventDefault();
             self.close();
-        }.bind(self));
+        });
 
         // Add listener for add.
         self._find('[data-region="competencylinktree"] [data-action="add"]').click(function(e) {
@@ -162,14 +162,14 @@ define(['jquery',
             }
 
             if (self._multiSelect) {
-                self._trigger('save', { competencyIds: self._selectedCompetencies });
+                self._trigger('save', {competencyIds: self._selectedCompetencies});
             } else {
                 // We checked above that the array has at least one value.
-                self._trigger('save', { competencyId: self._selectedCompetencies[0] });
+                self._trigger('save', {competencyId: self._selectedCompetencies[0]});
             }
 
             self.close();
-        }.bind(self));
+        });
 
         // The list of selected competencies will be modified while looping (because of the listeners above).
         var currentItems = self._selectedCompetencies.slice(0);
@@ -180,7 +180,7 @@ define(['jquery',
                 tree.toggleItem(node);
                 tree.updateFocus(node);
             }
-        }.bind(self));
+        });
 
     };
 
@@ -210,8 +210,8 @@ define(['jquery',
                     html,
                     self._afterRender.bind(self)
                 );
-            }.bind(self));
-        }.bind(self)).fail(Notification.exception);
+            });
+        }).fail(Notification.exception);
     };
 
     /**
@@ -226,7 +226,7 @@ define(['jquery',
         var self = this;
 
         return Ajax.call([
-            { methodname: 'core_competency_search_competencies', args: {
+            {methodname: 'core_competency_search_competencies', args: {
                 searchtext: searchText,
                 competencyframeworkid: frameworkId
             }}
@@ -248,7 +248,8 @@ define(['jquery',
             }
 
             // Expand the list of competencies into a tree.
-            var i, tree = [], comp;
+            var i, comp;
+            var tree = [];
             for (i = 0; i < competencies.length; i++) {
                 comp = competencies[i];
                 if (comp.parentid == "0") { // Loose check for now, because WS returns a string.
@@ -261,7 +262,7 @@ define(['jquery',
 
             self._competencies = tree;
 
-        }.bind(self)).fail(Notification.exception);
+        }).fail(Notification.exception);
     };
 
     /**
@@ -287,7 +288,7 @@ define(['jquery',
         $.each(this._frameworks, function(i, f) {
             if (f.id == fid) {
                 frm = f;
-                return false;
+                return;
             }
         });
         return frm;
@@ -320,7 +321,7 @@ define(['jquery',
 
         if (self._singleFramework) {
             promise = Ajax.call([
-                { methodname: 'core_competency_read_competency_framework', args: {
+                {methodname: 'core_competency_read_competency_framework', args: {
                     id: this._frameworkId
                 }}
             ])[0].then(function(framework) {
@@ -328,9 +329,9 @@ define(['jquery',
             });
         } else {
             promise = Ajax.call([
-                { methodname: 'core_competency_list_competency_frameworks', args: {
+                {methodname: 'core_competency_list_competency_frameworks', args: {
                     sort: 'shortname',
-                    context: { contextid: self._pageContextId },
+                    context: {contextid: self._pageContextId},
                     includes: self._pageContextIncludes,
                     onlyvisible: self._onlyVisible
                 }}
@@ -373,7 +374,7 @@ define(['jquery',
             }
 
             return self._loadCompetencies();
-        }.bind(self));
+        });
     };
 
     /**
@@ -387,7 +388,7 @@ define(['jquery',
         return self._render().then(function(html) {
             self._find('[data-region="competencylinktree"]').replaceWith(html);
             self._afterRender();
-        }.bind(self));
+        });
     };
 
     /**
@@ -419,7 +420,7 @@ define(['jquery',
             };
 
             return Templates.render('tool_lp/competency_picker', context);
-        }.bind(self));
+        });
     };
 
     /**
index b417b67..8d5b536 100644 (file)
@@ -78,7 +78,7 @@ define(['jquery',
             self._find('[data-action="chooseplan"]').change(function(e) {
                 self._planId = $(e.target).val();
                 self._loadCompetencies().then(self._refresh.bind(self));
-            }.bind(self));
+            });
         }
     };
 
@@ -94,13 +94,14 @@ define(['jquery',
         var self = this;
 
         return Ajax.call([
-            { methodname: 'core_competency_list_plan_competencies', args: {
+            {methodname: 'core_competency_list_plan_competencies', args: {
                 id: planId
             }}
         ])[0].done(function(competencies) {
 
             // Expand the list of competencies into a fake tree.
-            var i, tree = [], comp;
+            var i, comp;
+            var tree = [];
             for (i = 0; i < competencies.length; i++) {
                 comp = competencies[i].competency;
                 if (comp.shortname.toLowerCase().indexOf(searchText.toLowerCase()) < 0) {
@@ -128,7 +129,7 @@ define(['jquery',
         $.each(this._plans, function(i, f) {
             if (f.id == id) {
                 plan = f;
-                return false;
+                return;
             }
         });
         return plan;
@@ -161,7 +162,7 @@ define(['jquery',
 
         if (self._singlePlan) {
             promise = Ajax.call([
-                { methodname: 'core_competency_read_plan', args: {
+                {methodname: 'core_competency_read_plan', args: {
                     id: this._planId
                 }}
             ])[0].then(function(plan) {
@@ -169,7 +170,7 @@ define(['jquery',
             });
         } else {
             promise = Ajax.call([
-                { methodname: 'core_competency_list_user_plans', args: {
+                {methodname: 'core_competency_list_user_plans', args: {
                     userid: self._userId
                 }}
             ])[0];
@@ -200,7 +201,7 @@ define(['jquery',
             }
 
             return self._loadCompetencies();
-        }.bind(self));
+        });
     };
 
     /**
@@ -232,7 +233,7 @@ define(['jquery',
             };
 
             return Templates.render('tool_lp/competency_picker_user_plans', context);
-        }.bind(self));
+        });
     };
 
     return /** @alias module:tool_lp/competencypicker_user_plans */ Picker;
index f207939..3a956cb 100644 (file)
@@ -137,7 +137,7 @@ define(['jquery',
         $.each(this._rules, function(index, rule) {
             if (rule.canConfig()) {
                 can = true;
-                return false;
+                return;
             }
         });
         return can;
@@ -163,7 +163,7 @@ define(['jquery',
     RuleConfig.prototype.display = function() {
         var self = this;
         if (!self._competency) {
-            return;
+            return false;
         }
         return self._render().then(function(html) {
             return Str.get_string('competencyrule', 'tool_lp').then(function(title) {
@@ -276,7 +276,7 @@ define(['jquery',
         $.each(this._rules, function(index, rule) {
             if (rule.getType() == type) {
                 result = rule;
-                return false;
+                return;
             }
         });
 
@@ -297,7 +297,7 @@ define(['jquery',
         $.each(self._rulesModules, function(index, modInfo) {
             if (modInfo.type == type) {
                 name = modInfo.name;
-                return false;
+                return;
             }
         });
         return name;
index 1cc3933..06af055 100644 (file)
@@ -52,7 +52,7 @@ define(['jquery',
         var currentValue = $(e.target).closest('a').data('pushratingstouserplans');
         var context = {
             courseid: courseid,
-            settings: { pushratingstouserplans: currentValue }
+            settings: {pushratingstouserplans: currentValue}
         };
         e.preventDefault();
 
@@ -112,11 +112,11 @@ define(['jquery',
 
         var newValue = this._find('input[name="pushratingstouserplans"]:checked').val();
         var courseId = this._find('input[name="courseid"]').val();
-        var settings = { pushratingstouserplans: newValue };
+        var settings = {pushratingstouserplans: newValue};
 
         ajax.call([
-            { methodname: 'core_competency_update_course_competency_settings',
-              args: { courseid: courseId, settings: settings } }
+            {methodname: 'core_competency_update_course_competency_settings',
+              args: {courseid: courseId, settings: settings}}
         ])[0].done(function() {
             this.refreshCourseCompetenciesPage();
         }.bind(this)).fail(notification.exception);
@@ -133,8 +133,8 @@ define(['jquery',
         var courseId = this._find('input[name="courseid"]').val();
 
         ajax.call([
-            { methodname: 'tool_lp_data_for_course_competencies_page',
-              args: { courseid: courseId } }
+            {methodname: 'tool_lp_data_for_course_competencies_page',
+              args: {courseid: courseId}}
         ])[0].done(function(context) {
             templates.render('tool_lp/course_competencies_page', context).done(function(html, js) {
                 $('[data-region="coursecompetenciespage"]').replaceWith(html);
index a94eb30..367cb00 100644 (file)
@@ -67,9 +67,9 @@ define(['core/str', 'core/yui'], function(str, Y) {
             // Here we are wrapping YUI. This allows us to start transitioning, but
             // wait for a good alternative without having inconsistent UIs.
             str.get_strings([
-                { key: 'emptydragdropregion', component: 'moodle' },
-                { key: 'movecontent', component: 'moodle' },
-                { key: 'tocontent', component: 'moodle' },
+                {key: 'emptydragdropregion', component: 'moodle'},
+                {key: 'movecontent', component: 'moodle'},
+                {key: 'tocontent', component: 'moodle'},
             ]).done(function() {
                 Y.use('moodle-tool_lp-dragdrop-reorder', function() {
 
index 44698d1..ac59ad9 100644 (file)
@@ -57,10 +57,10 @@ define(['jquery',
             e.stopPropagation();
 
             Str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'areyousure', component: 'moodle' },
-                { key: 'delete', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'areyousure', component: 'moodle'},
+                {key: 'delete', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 Notification.confirm(
                     strings[0], // Confirm.
index 9172c19..486c25c 100644 (file)
@@ -47,7 +47,7 @@ define(['jquery', 'core/ajax', 'core/templates'], function($, Ajax, Templates) {
                 methodname: 'tool_lp_search_cohorts',
                 args: {
                     query: query,
-                    context: { contextid: contextid },
+                    context: {contextid: contextid},
                     includes: includes
                 }
             }]);
index b554495..cb9685f 100644 (file)
@@ -65,7 +65,7 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
         // We are chaining ajax requests here.
         var requests = ajax.call([{
             methodname: 'core_competency_duplicate_competency_framework',
-            args: { id: frameworkid }
+            args: {id: frameworkid}
         }, {
             methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
             args: {
@@ -84,7 +84,7 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
         // We are chaining ajax requests here.
         var requests = ajax.call([{
             methodname: 'core_competency_delete_competency_framework',
-            args: { id: frameworkid }
+            args: {id: frameworkid}
         }, {
             methodname: 'tool_lp_data_for_competency_frameworks_manage_page',
             args: {
@@ -97,12 +97,12 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
             if (success === false) {
                 var req = ajax.call([{
                     methodname: 'core_competency_read_competency_framework',
-                    args: { id: frameworkid }
+                    args: {id: frameworkid}
                 }]);
                 req[0].done(function(framework) {
                     str.get_strings([
-                        { key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname },
-                        { key: 'cancel', component: 'moodle' }
+                        {key: 'frameworkcannotbedeleted', component: 'tool_lp', param: framework.shortname},
+                        {key: 'cancel', component: 'moodle'}
                     ]).done(function(strings) {
                         notification.alert(
                             null,
@@ -127,15 +127,15 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
 
         var requests = ajax.call([{
             methodname: 'core_competency_read_competency_framework',
-            args: { id: frameworkid }
+            args: {id: frameworkid}
         }]);
 
         requests[0].done(function(framework) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname },
-                { key: 'delete', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'deletecompetencyframework', component: 'tool_lp', param: framework.shortname},
+                {key: 'delete', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
index 3558b9f..7a030b6 100644 (file)
@@ -118,11 +118,11 @@ define(['jquery',
                     methodname: self._methodName,
                     args: args,
                     done: function(evidence) {
-                        self._trigger('competencyupdated', { args: args, evidence: evidence });
-                    }.bind(self),
+                        self._trigger('competencyupdated', {args: args, evidence: evidence});
+                    },
                     fail: notification.exception
                 }]);
-            }.bind(self));
+            });
         }).fail(notification.exception);
     };
 
index 6889936..0434b56 100644 (file)
@@ -175,7 +175,7 @@ define(['jquery'], function($) {
             if (this.handlers) {
                 $.each(this.handlers, function(selector, handler) {
                     if (eventHandled) {
-                        return false;
+                        return;
                     }
                     if (item.find(selector).length > 0) {
                         var callable = $.proxy(handler, anchor);
index 5a2648b..705ab29 100644 (file)
@@ -69,7 +69,7 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
 
         if (data.competencyId !== 0) {
             ajax.call([
-                { methodname: 'core_competency_read_competency', args: {
+                {methodname: 'core_competency_read_competency', args: {
                     id: data.competencyId
                 }}
             ])[0].done(function(competency) {
@@ -112,13 +112,13 @@ define(['jquery', 'core/ajax', 'core/str', 'tool_lp/competencypicker', 'core/tem
                     };
 
                     return Templates.render('tool_lp/competency_picker_competencyform', context);
-                }.bind(self));
+                });
             };
 
             // On selected competency.
             picker.on('save', function(e, data) {
                 self.setParent(data);
-            }.bind(self));
+            });
 
             picker.display();
         });
index 39efd98..932318f 100644 (file)
@@ -117,7 +117,7 @@ define(['jquery',
             .done(function(newhtml, newjs) {
                 $(self._region).replaceWith(newhtml);
                 templates.runTemplateJS(newjs);
-            }.bind(self))
+            })
             .fail(notification.exception);
     };
 
@@ -139,7 +139,7 @@ define(['jquery',
         // Apply all the promises, and refresh when the last one is resolved.
         return $.when.apply($.when, ajax.call(calls))
             .then(function() {
-                self._renderView.call(self, arguments[arguments.length - 1]);
+                self._renderView(arguments[arguments.length - 1]);
             })
             .fail(notification.exception);
     };
@@ -153,7 +153,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_delete_plan',
-                args: { id: planData.id }
+                args: {id: planData.id}
             }];
         self._callAndRefresh(calls, planData);
     };
@@ -169,15 +169,15 @@ define(['jquery',
 
         requests = ajax.call([{
             methodname: 'core_competency_read_plan',
-            args: { id: planData.id }
+            args: {id: planData.id}
         }]);
 
         requests[0].done(function(plan) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'deleteplan', component: 'tool_lp', param: plan.name },
-                { key: 'delete', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'deleteplan', component: 'tool_lp', param: plan.name},
+                {key: 'delete', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -186,7 +186,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doDelete(planData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
@@ -202,7 +202,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_reopen_plan',
-                args: { planid: planData.id}
+                args: {planid: planData.id}
             }];
         self._callAndRefresh(calls, planData);
     };
@@ -216,15 +216,15 @@ define(['jquery',
         var self = this,
             requests = ajax.call([{
                 methodname: 'core_competency_read_plan',
-                args: { id: planData.id }
+                args: {id: planData.id}
             }]);
 
         requests[0].done(function(plan) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name },
-                { key: 'reopenplan', component: 'tool_lp' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'reopenplanconfirm', component: 'tool_lp', param: plan.name},
+                {key: 'reopenplan', component: 'tool_lp'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -233,7 +233,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doReopenPlan(planData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
@@ -249,7 +249,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_complete_plan',
-                args: { planid: planData.id}
+                args: {planid: planData.id}
             }];
         self._callAndRefresh(calls, planData);
     };
@@ -263,15 +263,15 @@ define(['jquery',
         var self = this,
             requests = ajax.call([{
                 methodname: 'core_competency_read_plan',
-                args: { id: planData.id }
+                args: {id: planData.id}
             }]);
 
         requests[0].done(function(plan) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'completeplanconfirm', component: 'tool_lp', param: plan.name },
-                { key: 'completeplan', component: 'tool_lp' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'completeplanconfirm', component: 'tool_lp', param: plan.name},
+                {key: 'completeplan', component: 'tool_lp'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -280,7 +280,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doCompletePlan(planData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
@@ -295,7 +295,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_unlink_plan_from_template',
-                args: { planid: planData.id}
+                args: {planid: planData.id}
             }];
         self._callAndRefresh(calls, planData);
     };
@@ -309,15 +309,15 @@ define(['jquery',
         var self = this,
             requests = ajax.call([{
                 methodname: 'core_competency_read_plan',
-                args: { id: planData.id }
+                args: {id: planData.id}
             }]);
 
         requests[0].done(function(plan) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name },
-                { key: 'unlinkplantemplate', component: 'tool_lp' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'unlinkplantemplateconfirm', component: 'tool_lp', param: plan.name},
+                {key: 'unlinkplantemplate', component: 'tool_lp'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -326,7 +326,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doUnlinkPlan(planData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
@@ -499,7 +499,7 @@ define(['jquery',
         var competencyid = $(e.target).data('id');
         var requests = ajax.call([{
             methodname: 'tool_lp_list_courses_using_competency',
-            args: { id: competencyid }
+            args: {id: competencyid}
         }]);
 
         requests[0].done(function(courses) {
index 9ba1fd4..67c41ee 100644 (file)
@@ -140,7 +140,7 @@ define(['jquery', 'core/notification', 'core/templates', 'core/ajax', 'tool_lp/d
         }.bind(this));
         body.on('click', '[data-action="cancel"]', function() {
             popup.close();
-        }.bind(this));
+        });
     };
 
     /**
@@ -151,7 +151,7 @@ define(['jquery', 'core/notification', 'core/templates', 'core/ajax', 'tool_lp/d
     ScaleConfig.prototype.setScaleConfig = function() {
         var body = $(this.popup.getContent());
         // Get the data.
-        var data = [{ scaleid: this.scaleid}];
+        var data = [{scaleid: this.scaleid}];
         this.scalevalues.forEach(function(value) {
             var scaledefault = 0;
             var proficient = 0;
index e0ceecb..02fb4c5 100644 (file)
@@ -67,8 +67,8 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
         // We are chaining ajax requests here.
         var requests = ajax.call([{
             methodname: 'core_competency_delete_template',
-            args: { id: templateid,
-                    deleteplans: deleteplans }
+            args: {id: templateid,
+                    deleteplans: deleteplans}
         }, {
             methodname: 'tool_lp_data_for_templates_manage_page',
             args: {
@@ -93,7 +93,7 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
         // We are chaining ajax requests here.
         var requests = ajax.call([{
             methodname: 'core_competency_duplicate_template',
-            args: { id: templateid }
+            args: {id: templateid}
         }, {
             methodname: 'tool_lp_data_for_templates_manage_page',
             args: {
@@ -119,22 +119,22 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
 
         var requests = ajax.call([{
             methodname: 'core_competency_read_template',
-            args: { id: templateid }
+            args: {id: templateid}
         }, {
             methodname: 'core_competency_template_has_related_data',
-            args: { id: templateid }
+            args: {id: templateid}
         }]);
 
         requests[0].done(function(template) {
             requests[1].done(function(templatehasrelateddata) {
                 if (templatehasrelateddata) {
                     str.get_strings([
-                        { key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
-                        { key: 'deletetemplatewithplans', component: 'tool_lp' },
-                        { key: 'deleteplans', component: 'tool_lp' },
-                        { key: 'unlinkplanstemplate', component: 'tool_lp' },
-                        { key: 'confirm', component: 'moodle' },
-                        { key: 'cancel', component: 'moodle' }
+                        {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},
+                        {key: 'deletetemplatewithplans', component: 'tool_lp'},
+                        {key: 'deleteplans', component: 'tool_lp'},
+                        {key: 'unlinkplanstemplate', component: 'tool_lp'},
+                        {key: 'confirm', component: 'moodle'},
+                        {key: 'cancel', component: 'moodle'}
                     ]).done(function(strings) {
                         var actions = [{'text': strings[2], 'value': 'delete'},
                                        {'text': strings[3], 'value': 'unlink'}];
@@ -154,10 +154,10 @@ define(['jquery', 'core/templates', 'core/ajax', 'core/notification', 'core/str'
                     }).fail(notification.exception);
                 } else {
                     str.get_strings([
-                        { key: 'confirm', component: 'moodle' },
-                        { key: 'deletetemplate', component: 'tool_lp', param: template.shortname },
-                        { key: 'delete', component: 'moodle' },
-                        { key: 'cancel', component: 'moodle' }
+                        {key: 'confirm', component: 'moodle'},
+                        {key: 'deletetemplate', component: 'tool_lp', param: template.shortname},
+                        {key: 'delete', component: 'moodle'},
+                        {key: 'cancel', component: 'moodle'}
                     ]).done(function(strings) {
                         notification.confirm(
                         strings[0], // Confirm.
index 179dcf6..8d784f2 100644 (file)
@@ -168,7 +168,7 @@ define(['jquery', 'core/url', 'core/log'], function($, url, log) {
         if (!this.multiSelect) {
             allSelected = allSelected.first();
         }
-        this.treeRoot.trigger('selectionchanged', { selected: allSelected });
+        this.treeRoot.trigger('selectionchanged', {selected: allSelected});
     };
 
     /**
@@ -274,6 +274,8 @@ define(['jquery', 'core/url', 'core/log'], function($, url, log) {
      * @param {Event} e The event.
      * @return {Boolean}
      */
+     // This function should be simplified. In the meantime..
+    // eslint-disable-next-line complexity
     Tree.prototype.handleKeyDown = function(item, e) {
         var currentIndex = this.visibleItems.index(item);
         var newItem = null;
index 334e8ff..0e6df71 100644 (file)
@@ -44,15 +44,15 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates'], function(
 
         if (this._planId) {
             this._methodName = 'tool_lp_data_for_user_competency_summary_in_plan';
-            this._args = { competencyid: this._competencyId, planid: this._planId };
+            this._args = {competencyid: this._competencyId, planid: this._planId};
             this._templateName = 'tool_lp/user_competency_summary_in_plan';
         } else if (this._courseId) {
             this._methodName = 'tool_lp_data_for_user_competency_summary_in_course';
-            this._args = { userid: this._userId, competencyid: this._competencyId, courseid: this._courseId };
+            this._args = {userid: this._userId, competencyid: this._competencyId, courseid: this._courseId};
             this._templateName = 'tool_lp/user_competency_summary_in_course';
         } else {
             this._methodName = 'tool_lp_data_for_user_competency_summary';
-            this._args = { userid: this._userId, competencyid: this._competencyId };
+            this._args = {userid: this._userId, competencyid: this._competencyId};
             this._templateName = 'tool_lp/user_competency_summary';
         }
     };
index 5001991..6600b1f 100644 (file)
@@ -54,7 +54,7 @@ define(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates'
 
         var requests = ajax.call([{
             methodname: 'tool_lp_data_for_user_competency_summary_in_plan',
-            args: { competencyid: competencyId, planid: planId },
+            args: {competencyid: competencyId, planid: planId},
             done: this._contextLoaded.bind(this),
             fail: notification.exception
         }]);
@@ -99,7 +99,7 @@ define(['jquery', 'core/notification', 'core/str', 'core/ajax', 'core/templates'
 
         ajax.call([{
             methodname: 'tool_lp_data_for_plan_page',
-            args: { planid: planId},
+            args: {planid: planId},
             done: this._pageContextLoaded.bind(this),
             fail: notification.exception
         }]);
index f1f5cd3..b9ef686 100644 (file)
@@ -104,7 +104,7 @@ define(['jquery',
         templates.render(self._template, context)
             .done(function(newhtml, newjs) {
                 templates.replaceNode($(self._region), newhtml, newjs);
-            }.bind(self))
+            })
             .fail(notification.exception);
     };
 
@@ -126,7 +126,7 @@ define(['jquery',
         // Apply all the promises, and refresh when the last one is resolved.
         return $.when.apply($.when, ajax.call(calls))
             .then(function() {
-                self._renderView.call(self, arguments[arguments.length - 1]);
+                self._renderView(arguments[arguments.length - 1]);
             })
             .fail(notification.exception);
     };
@@ -140,7 +140,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_delete_user_evidence',
-                args: { id: evidenceData.id }
+                args: {id: evidenceData.id}
             }];
         self._callAndRefresh(calls, evidenceData);
     };
@@ -156,15 +156,15 @@ define(['jquery',
 
         requests = ajax.call([{
             methodname: 'core_competency_read_user_evidence',
-            args: { id: evidenceData.id }
+            args: {id: evidenceData.id}
         }]);
 
         requests[0].done(function(evidence) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name },
-                { key: 'delete', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'deleteuserevidence', component: 'tool_lp', param: evidence.name},
+                {key: 'delete', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -173,7 +173,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doDelete(evidenceData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
@@ -227,7 +227,7 @@ define(['jquery',
         picker.on('save', function(e, data) {
             var competencyIds = data.competencyIds;
             self._doCreateUserEvidenceCompetency(evidenceData, competencyIds, data.requestReview);
-        }.bind(self));
+        });
 
         picker.display();
     };
@@ -295,7 +295,7 @@ define(['jquery',
         var self = this,
             calls = [{
                 methodname: 'core_competency_request_review_of_user_evidence_linked_competencies',
-                args: { id: evidenceData.id }
+                args: {id: evidenceData.id}
             }];
         self._callAndRefresh(calls, evidenceData);
     };
@@ -311,15 +311,15 @@ define(['jquery',
 
         requests = ajax.call([{
             methodname: 'core_competency_read_user_evidence',
-            args: { id: evidenceData.id }
+            args: {id: evidenceData.id}
         }]);
 
         requests[0].done(function(evidence) {
             str.get_strings([
-                { key: 'confirm', component: 'moodle' },
-                { key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name },
-                { key: 'confirm', component: 'moodle' },
-                { key: 'cancel', component: 'moodle' }
+                {key: 'confirm', component: 'moodle'},
+                {key: 'sendallcompetenciestoreview', component: 'tool_lp', param: evidence.name},
+                {key: 'confirm', component: 'moodle'},
+                {key: 'cancel', component: 'moodle'}
             ]).done(function(strings) {
                 notification.confirm(
                     strings[0], // Confirm.
@@ -328,7 +328,7 @@ define(['jquery',
                     strings[3], // Cancel.
                     function() {
                         self._doReviewUserEvidenceCompetencies(evidenceData);
-                    }.bind(self)
+                    }
                 );
             }).fail(notification.exception);
         }).fail(notification.exception);
index 4beab69..e700a17 100644 (file)
@@ -40,11 +40,11 @@ $url = new moodle_url('/admin/tool/lp/coursecompetencies.php', $urlparams);
 list($title, $subtitle) = \tool_lp\page_helper::setup_for_course($url, $course);
 
 $output = $PAGE->get_renderer('tool_lp');
+$page = new \tool_lp\output\course_competencies_page($course->id);
+
 echo $output->header();
 echo $output->heading($title);
 
-
-$page = new \tool_lp\output\course_competencies_page($course->id);
 echo $output->render($page);
 
 echo $output->footer();
index 870104e..62b6892 100644 (file)
@@ -36,6 +36,13 @@ function tool_lp_extend_navigation_course($navigation, $course, $coursecontext)
         return;
     }
 
+    // Check access to the course and competencies page.
+    $capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage');
+    $context = context_course::instance($course->id);
+    if (!has_any_capability($capabilities, $context) || !can_access_course($course)) {
+        return;
+    }
+
     // 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));
index 1dbe965..c111010 100644 (file)
@@ -26,6 +26,7 @@ namespace tool_mobile;
 
 use core_component;
 use core_plugin_manager;
+use context_system;
 
 /**
  * API exposed by tool_mobile
@@ -82,4 +83,35 @@ class api {
         return $pluginsinfo;
     }
 
+    /**
+     * Returns a list of the site public settings, those not requiring authentication.
+     *
+     * @return array with the settings and warnings
+     */
+    public static function get_site_public_settings() {
+        global $CFG, $SITE, $PAGE;
+
+        $context = context_system::instance();
+        // We need this to make work the format text functions.
+        $PAGE->set_context($context);
+
+        $settings = array(
+            'wwwroot' => $CFG->wwwroot,
+            'httpswwwroot' => $CFG->httpswwwroot,
+            'sitename' => external_format_string($SITE->fullname, $context->id, true),
+            'guestlogin' => $CFG->guestloginbutton,
+            'rememberusername' => $CFG->rememberusername,
+            'authloginviaemail' => $CFG->authloginviaemail,
+            'registerauth' => $CFG->registerauth,
+            'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
+            'authinstructions' => format_text($CFG->auth_instructions),
+            'authnoneenabled' => (int) is_enabled_auth('none'),
+            'enablewebservices' => $CFG->enablewebservices,
+            'enablemobilewebservice' => $CFG->enablemobilewebservice,
+            'maintenanceenabled' => $CFG->maintenance_enabled,
+            'maintenancemessage' => format_text($CFG->maintenance_message),
+        );
+        return $settings;
+    }
+
 }
index ca5fb71..148f8a4 100644 (file)
@@ -95,4 +95,54 @@ class external extends external_api {
         );
     }
 
+    /**
+     * Returns description of get_site_public_settings() parameters.
+     *
+     * @return external_function_parameters
+     * @since  Moodle 3.2
+     */
+    public static function get_site_public_settings_parameters() {
+        return new external_function_parameters(array());
+    }
+
+    /**
+     * Returns a list of the site public settings, those not requiring authentication.
+     *
+     * @return array with the settings and warnings
+     * @since  Moodle 3.2
+     */
+    public static function get_site_public_settings() {
+        $result = api::get_site_public_settings();
+        $result['warnings'] = array();
+        return $result;
+    }
+
+    /**
+     * Returns description of get_site_public_settings() result value.
+     *
+     * @return external_description
+     * @since  Moodle 3.2
+     */
+    public static function get_site_public_settings_returns() {
+        return new external_single_structure(
+            array(
+                'wwwroot' => new external_value(PARAM_RAW, 'Site URL.'),
+                'httpswwwroot' => new external_value(PARAM_RAW, 'Site https URL (if httpslogin is enabled).'),
+                'sitename' => new external_value(PARAM_TEXT, 'Site name.'),
+                'guestlogin' => new external_value(PARAM_INT, 'Whether guest login is enabled.'),
+                'rememberusername' => new external_value(PARAM_INT, 'Values: 0 for No, 1 for Yes, 2 for optional.'),
+                'authloginviaemail' => new external_value(PARAM_INT, 'Whether log in via email is enabled.'),
+                'registerauth' => new external_value(PARAM_PLUGIN, 'Authentication method for user registration.'),
+                'forgottenpasswordurl' => new external_value(PARAM_URL, 'Forgotten password URL.'),
+                'authinstructions' => new external_value(PARAM_RAW, 'Authentication instructions.'),
+                'authnoneenabled' => new external_value(PARAM_INT, 'Whether auth none is enabled.'),
+                'enablewebservices' => new external_value(PARAM_INT, 'Whether Web Services are enabled.'),
+                'enablemobilewebservice' => new external_value(PARAM_INT, 'Whether the Mobile service is enabled.'),
+                'maintenanceenabled' => new external_value(PARAM_INT, 'Whether site maintenance is enabled.'),
+                'maintenancemessage' => new external_value(PARAM_RAW, 'Maintenance message.'),
+                'warnings' => new external_warnings(),
+            )
+        );
+    }
+
 }
index f7ddd74..20c6c84 100644 (file)
@@ -31,6 +31,16 @@ $functions = array(
         'description' => 'Returns a list of Moodle plugins supporting the mobile app.',
         'type'        => 'read',
         'services'    => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
+    ),
+
+    'tool_mobile_get_site_public_settings' => array(
+        'classname'   => 'tool_mobile\external',
+        'methodname'  => 'get_site_public_settings',
+        'description' => 'Returns a list of the site public settings, those not requiring authentication.',
+        'type'        => 'read',
+        'services'    => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
+        'ajax'          => true,
+        'loginrequired' => false,
     )
 
 );
index 5a17c57..0ee8fc3 100644 (file)
@@ -54,4 +54,45 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
         $this->assertTrue(is_array($result['plugins']));
     }
 
+    public function test_get_site_public_settings() {
+        global $CFG, $SITE;
+
+        $this->resetAfterTest(true);
+        $result = external::get_site_public_settings();
+        $result = external_api::clean_returnvalue(external::get_site_public_settings_returns(), $result);
+
+        // Test default values.
+        $context = context_system::instance();
+        $expected = array(
+            'wwwroot' => $CFG->wwwroot,
+            'httpswwwroot' => $CFG->httpswwwroot,
+            'sitename' => external_format_string($SITE->fullname, $context->id, true),
+            'guestlogin' => $CFG->guestloginbutton,
+            'rememberusername' => $CFG->rememberusername,
+            'authloginviaemail' => $CFG->authloginviaemail,
+            'registerauth' => $CFG->registerauth,
+            'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
+            'authinstructions' => format_text($CFG->auth_instructions),
+            'authnoneenabled' => (int) is_enabled_auth('none'),
+            'enablewebservices' => $CFG->enablewebservices,
+            'enablemobilewebservice' => $CFG->enablemobilewebservice,
+            'maintenanceenabled' => $CFG->maintenance_enabled,
+            'maintenancemessage' => format_text($CFG->maintenance_message),
+            'warnings' => array()
+        );
+        $this->assertEquals($expected, $result);
+
+        // Change a value.
+        set_config('registerauth', 'email');
+        $authinstructions = 'Something with <b>html tags</b>';
+        set_config('auth_instructions', $authinstructions);
+
+        $expected['registerauth'] = 'email';
+        $expected['authinstructions'] = format_text($authinstructions);
+
+        $result = external::get_site_public_settings();
+        $result = external_api::clean_returnvalue(external::get_site_public_settings_returns(), $result);
+        $this->assertEquals($expected, $result);
+    }
+
 }
index 0efc6e1..0c57e23 100644 (file)
@@ -23,6 +23,6 @@
  */
 
 defined('MOODLE_INTERNAL') || die();
-$plugin->version   = 2016052300; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2016052301; // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2016051900; // Requires this Moodle version.
 $plugin->component = 'tool_mobile'; // Full name of the plugin (used for diagnostics).
index c11665e..793b11f 100644 (file)
@@ -140,6 +140,10 @@ class eventobservers {
             $subscriptions = subscription_manager::get_subscriptions_by_event($eventobj);
             $idstosend = array();
             foreach ($subscriptions as $subscription) {
+                // Only proceed to fire events and notifications if the subscription is active.
+                if (!subscription_manager::subscription_is_active($subscription)) {
+                    continue;
+                }
                 $starttime = $now - $subscription->timewindow;
                 $starttime = ($starttime > $subscription->lastnotificationsent) ? $starttime : $subscription->lastnotificationsent;
                 if ($subscription->courseid == 0) {
index 6e25f6e..98518e9 100644 (file)
@@ -55,17 +55,26 @@ class subscription {
      * Magic get method.
      *
      * @param string $prop property to get.
-     *
      * @return mixed
      * @throws \coding_exception
      */
     public function __get($prop) {
-        if (property_exists($this->subscription, $prop)) {
+        if (isset($this->subscription->$prop)) {
             return $this->subscription->$prop;
         }
         throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
     }
 
+    /**
+     * Magic isset method.
+     *
+     * @param string $prop the property to get.
+     * @return bool true if the property is set, false otherwise.
+     */
+    public function __isset($prop) {
+        return property_exists($this->subscription, $prop);
+    }
+
     /**
      * Get a human readable name for instances associated with this subscription.
      *
index 421984c..c4382e7 100644 (file)
@@ -35,6 +35,10 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class subscription_manager {
+
+    /** @const Period of time, in days, after which an inactive subscription will be removed completely.*/
+    const INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS = 30;
+
     /**
      * Subscribe a user to a given rule.
      *
@@ -456,4 +460,78 @@ class subscription_manager {
 
         return false;
     }
+
+    /**
+     * Activates a group of subscriptions based on an input array of ids.
+     *
+     * @since 3.2.0
+     * @param array $ids of subscription ids.
+     * @return bool true if the operation was successful, false otherwise.
+     */
+    public static function activate_subscriptions(array $ids) {
+        global $DB;
+        if (!empty($ids)) {
+            list($sql, $params) = $DB->get_in_or_equal($ids);
+            $success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', '0', 'id ' . $sql, $params);
+            return $success;
+        }
+        return false;
+    }
+
+    /**
+     * Deactivates a group of subscriptions based on an input array of ids.
+     *
+     * @since 3.2.0
+     * @param array $ids of subscription ids.
+     * @return bool true if the operation was successful, false otherwise.
+     */
+    public static function deactivate_subscriptions(array $ids) {
+        global $DB;
+        if (!empty($ids)) {
+            $inactivedate = time();
+            list($sql, $params) = $DB->get_in_or_equal($ids);
+            $success = $DB->set_field_select('tool_monitor_subscriptions', 'inactivedate', $inactivedate, 'id ' . $sql,
+                                             $params);
+            return $success;
+        }
+        return false;
+    }
+
+    /**
+     * Deletes subscriptions which have been inactive for a period of time.
+     *
+     * @since 3.2.0
+     * @param int $userid if provided, only this user's stale subscriptions will be deleted.
+     * @return bool true if the operation was successful, false otherwise.
+     */
+    public static function delete_stale_subscriptions($userid = 0) {
+        global $DB;
+        // Get the expiry duration, in days.
+        $cutofftime = strtotime("-" . self::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS . " days", time());
+
+        if (!empty($userid)) {
+            // Remove any stale subscriptions for the desired user only.
+            $success = $DB->delete_records_select('tool_monitor_subscriptions',
+                                                  'userid = ? AND inactivedate < ? AND inactivedate <> 0',
+                                                  array($userid, $cutofftime));
+
+        } else {
+            // Remove all stale subscriptions.
+            $success = $DB->delete_records_select('tool_monitor_subscriptions',
+                                                  'inactivedate < ? AND inactivedate <> 0',
+                                                  array($cutofftime));
+        }
+        return $success;
+    }
+
+    /**
+     * Check whether a subscription is active.
+     *
+     * @since 3.2.0
+     * @param \tool_monitor\subscription $subscription instance.
+     * @return bool true if the subscription is active, false otherwise.
+     */
+    public static function subscription_is_active(subscription $subscription) {
+        return empty($subscription->inactivedate);
+    }
 }
diff --git a/admin/tool/monitor/classes/task/check_subscriptions.php b/admin/tool/monitor/classes/task/check_subscriptions.php
new file mode 100644 (file)
index 0000000..8262f10
--- /dev/null
@@ -0,0 +1,274 @@
+<?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/>.
+namespace tool_monitor\task;
+use tool_monitor\subscription;
+use tool_monitor\subscription_manager;
+
+/**
+ * Simple task class responsible for activating, deactivating and removing subscriptions.
+ *
+ * Activation/deactivation is managed by looking at the same access rules used to determine whether a user can
+ * subscribe to the rule in the first place.
+ *
+ * Removal occurs when a subscription has been inactive for a period of time exceeding the lifespan, as set by
+ * subscription_manager::get_inactive_subscription_lifespan().
+ *
+ * I.e.
+ *  - Activation:   If a user can subscribe currently, then an existing subscription should be made active.
+ *  - Deactivation: If a user cannot subscribe currently, then an existing subscription should be made inactive.
+ *  - Removal:      If a user has a subscription that has been inactive for longer than the prescribed period, then
+ *                  delete the subscription entirely.
+ *
+ * @since      3.2.0
+ * @package    tool_monitor
+ * @copyright  2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class check_subscriptions extends \core\task\scheduled_task {
+
+    /** @var array 1d static cache, indexed by userid, storing whether or not the user has been fully set up.*/
+    protected $userssetupcache = array();
+
+    /** @var array 2d static cache, indexed by courseid and userid, storing whether a user can access the course with
+     *  the 'tool/monitor:subscribe' capability.
+     */
+    protected $courseaccesscache = array();
+
+    /**
+     * Get a descriptive name for this task.
+     *
+     * @since 3.2.0
+     * @return string name of the task.
+     */
+    public function get_name() {
+        return get_string('taskchecksubscriptions', 'tool_monitor');
+    }
+
+    /**
+     * Checks all course-level rule subscriptions and activates/deactivates based on current course access.
+     *
+     * The ordering of checks within the task is important for optimisation purposes. The aim is to be able to make a decision
+     * about whether to activate/deactivate each subscription without making unnecessary checks. The ordering roughly follows the
+     * context model, starting with system and user checks and moving down to course and course-module only when necessary.
+     *
+     * For example, if the user is suspended, then any active subscription is made inactive right away. I.e. there is no need to
+     * check site-level, course-level or course-module-level permissions. Likewise, if a subscriptions is site-level, there is no
+     * need to check course-level and course-module-level permissions.
+     *
+     * The task performs the following checks, in this order:
+     * 1. Check for a suspended user, breaking if suspended.
+     * 2. Check for an incomplete (not set up) user, breaking if not fully set up.
+     * 3. Check for the required capability in the relevant context, breaking if the capability is not found.
+     * 4. Check whether the subscription is site-context, breaking if true.
+     * 5. Check whether the user has course access, breaking only if the subscription is not also course-module-level.
+     * 6. Check whether the user has course-module access.
+     *
+     * @since 3.2.0
+     */
+    public function execute() {
+        global $DB;
+
+        if (!get_config('tool_monitor', 'enablemonitor')) {
+            return; // The tool is disabled. Nothing to do.
+        }
+
+        $toactivate   = array(); // Store the ids of subscriptions to be activated upon completion.
+        $todeactivate = array(); // Store the ids of subscriptions to be deactivated upon completion.
+
+        // Resultset rows are ordered by userid and courseid to work nicely with get_fast_modinfo() caching.
+        $sql = "SELECT u.id AS userid, u.firstname AS userfirstname, u.lastname AS userlastname, u.suspended AS usersuspended,
+                       u.email AS useremail, c.visible as coursevisible, c.cacherev as coursecacherev, s.courseid AS subcourseid,
+                       s.userid AS subuserid, s.cmid AS subcmid, s.inactivedate AS subinactivedate, s.id AS subid
+                  FROM {user} u
+                  JOIN {tool_monitor_subscriptions} s ON (s.userid = u.id)
+             LEFT JOIN {course} c ON (c.id = s.courseid)
+                 WHERE u.id = s.userid
+              ORDER BY s.userid, s.courseid";
+        $rs = $DB->get_recordset_sql($sql);
+
+        foreach ($rs as $row) {
+            // Create skeleton records from the result. This should be enough to use in subsequent access calls and avoids DB hits.
+            $sub = $this->get_subscription_from_rowdata($row);
+            $sub = new subscription($sub);
+            if (!isset($user) || $user->id != $sub->userid) {
+                $user= $this->get_user_from_rowdata($row);
+            }
+            if ((!isset($course) || $course->id != $sub->courseid) && !empty($sub->courseid)) {
+                $course = $this->get_course_from_rowdata($row);
+            }
+
+            // The user is suspended at site level, so deactivate any active subscriptions.
+            if ($user->suspended) {
+                if (subscription_manager::subscription_is_active($sub)) {
+                    $todeactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // Is the user fully set up? As per require_login on the subscriptions page.
+            if (!$this->is_user_setup($user)) {
+                if (subscription_manager::subscription_is_active($sub)) {
+                    $todeactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // Determine the context, based on the subscription course id.
+            $sitelevelsubscription = false;
+            if (empty($sub->courseid)) {
+                $context = \context_system::instance();
+                $sitelevelsubscription = true;
+            } else {
+                $context = \context_course::instance($sub->courseid);
+            }
+
+            // Check capability in the context.
+            if (!has_capability('tool/monitor:subscribe', $context, $user)) {
+                if (subscription_manager::subscription_is_active($sub)) {
+                    $todeactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // If the subscription is site-level, then we've run all the checks required to make an access decision.
+            if ($sitelevelsubscription) {
+                if (!subscription_manager::subscription_is_active($sub)) {
+                    $toactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // Check course access.
+            if (!$this->user_can_access_course($user, $course, 'tool/monitor:subscribe')) {
+                if (subscription_manager::subscription_is_active($sub)) {
+                    $todeactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // If the subscription has no course module relationship.
+            if (empty($sub->cmid)) {
+                if (!subscription_manager::subscription_is_active($sub)) {
+                    $toactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // Otherwise, check the course module info. We use the same checks as on the subscription page.
+            $modinfo = get_fast_modinfo($course, $sub->userid);
+            $cm = $modinfo->get_cm($sub->cmid);
+            if (!$cm || !$cm->uservisible || !$cm->available) {
+                if (subscription_manager::subscription_is_active($sub)) {
+                    $todeactivate[] = $sub->id;
+                }
+                continue;
+            }
+
+            // The course module is available and visible, so make a decision.
+            if (!subscription_manager::subscription_is_active($sub)) {
+                $toactivate[] = $sub->id;
+            }
+        }
+        $rs->close();
+
+        // Activate/deactivate/delete relevant subscriptions.
+        subscription_manager::activate_subscriptions($toactivate);
+        subscription_manager::deactivate_subscriptions($todeactivate);
+        subscription_manager::delete_stale_subscriptions();
+    }
+
+    /**
+     * Determines whether a user is fully set up, using cached results where possible.
+     *
+     * @since 3.2.0
+     * @param \stdClass $user the user record.
+     * @return bool true if the user is fully set up, false otherwise.
+     */
+    protected function is_user_setup($user) {
+        if (!isset($this->userssetupcache[$user->id])) {
+            $this->userssetupcache[$user->id] = !user_not_fully_set_up($user);
+        }
+        return $this->userssetupcache[$user->id];
+    }
+
+    /**
+     * Determines a user's access to a course with a given capability, using cached results where possible.
+     *
+     * @since 3.2.0
+     * @param \stdClass $user the user record.
+     * @param \stdClass $course the course record.
+     * @param string $capability the capability to check.
+     * @return bool true if the user can access the course with the specified capability, false otherwise.
+     */
+    protected function user_can_access_course($user, $course, $capability) {
+        if (!isset($this->courseaccesscache[$course->id][$user->id][$capability])) {
+            $this->courseaccesscache[$course->id][$user->id][$capability] = can_access_course($course, $user, $capability, true);
+        }
+        return $this->courseaccesscache[$course->id][$user->id][$capability];
+    }
+
+    /**
+     * Returns a partial subscription record, created from properties of the supplied recordset row object.
+     * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+     *
+     * @since 3.2.0
+     * @param \stdClass $rowdata the row object.
+     * @return \stdClass a partial subscription record.
+     */
+    protected function get_subscription_from_rowdata($rowdata) {
+        $sub = new \stdClass();
+        $sub->id = $rowdata->subid;
+        $sub->userid = $rowdata->subuserid;
+        $sub->courseid = $rowdata->subcourseid;
+        $sub->cmid = $rowdata->subcmid;
+        $sub->inactivedate = $rowdata->subinactivedate;
+        return $sub;
+    }
+
+    /**
+     * Returns a partial course record, created from properties of the supplied recordset row object.
+     * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+     *
+     * @since 3.2.0
+     * @param \stdClass $rowdata the row object.
+     * @return \stdClass a partial course record.
+     */
+    protected function get_course_from_rowdata($rowdata) {
+        $course = new \stdClass();
+        $course->id = $rowdata->subcourseid;
+        $course->visible = $rowdata->coursevisible;
+        $course->cacherev = $rowdata->coursecacherev;
+        return $course;
+    }
+
+    /**
+     * Returns a partial user record, created from properties of the supplied recordset row object.
+     * Intended to return a minimal record for specific use within this class and in subsequent access control calls only.
+     *
+     * @since 3.2.0
+     * @param \stdClass $rowdata the row object.
+     * @return \stdClass a partial user record.
+     */
+    protected function get_user_from_rowdata($rowdata) {
+        $user = new \stdClass();
+        $user->id = $rowdata->userid;
+        $user->firstname = $rowdata->userfirstname;
+        $user->lastname = $rowdata->userlastname;
+        $user->email = $rowdata->useremail;
+        $user->suspended = $rowdata->usersuspended;
+        return $user;
+    }
+}
index 7729199..9a3f1a1 100644 (file)
@@ -38,6 +38,7 @@
         <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="User id of the subscriber"/>
         <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Timestamp of when this subscription was created"/>
         <FIELD NAME="lastnotificationsent" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Timestamp of the time when a notification was last sent for this subscription."/>
+        <FIELD NAME="inactivedate" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index e70c344..20f324d 100644 (file)
@@ -32,5 +32,14 @@ $tasks = array(
         'day' => '*',
         'dayofweek' => '*',
         'month' => '*'
+    ),
+    array(
+        'classname' => 'tool_monitor\task\check_subscriptions',
+        'blocking' => 0,
+        'minute' => 'R',
+        'hour' => 'R',
+        'day' => '*',
+        'dayofweek' => '*',
+        'month' => '*'
     )
 );
index 9f8e53b..aaea29a 100644 (file)
@@ -62,5 +62,20 @@ function xmldb_tool_monitor_upgrade($oldversion) {
     // Moodle v3.1.0 release upgrade line.
     // Put any upgrade step following this.
 
+    if ($oldversion < 2016052305) {
+
+        // Define field inactivedate to be added to tool_monitor_subscriptions.
+        $table = new xmldb_table('tool_monitor_subscriptions');
+        $field = new xmldb_field('inactivedate', XMLDB_TYPE_INTEGER, '10', null, true, null, 0, 'lastnotificationsent');
+
+        // Conditionally launch add field inactivedate.
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Monitor savepoint reached.
+        upgrade_plugin_savepoint(true, 2016052305, 'tool', 'monitor');
+    }
+
     return true;
 }
index c0fbf7f..fb47717 100644 (file)
@@ -99,4 +99,5 @@ $string['subhelp'] = 'Subscription details';
 $string['subhelp_help'] = 'This subscription listens for when the event \'{$a->eventname}\' has been triggered in \'{$a->moduleinstance}\' {$a->frequency} time(s) in {$a->minutes} minute(s).';
 $string['subscribeto'] = 'Subscribe to rule "{$a}"';
 $string['taskcleanevents'] = 'Removes any unnecessary event monitor events';
+$string['taskchecksubscriptions'] = 'Activate/deactivate invalid rule subscriptions';
 $string['unsubscribe'] = 'Unsubscribe';
diff --git a/admin/tool/monitor/tests/subscription_test.php b/admin/tool/monitor/tests/subscription_test.php
new file mode 100644 (file)
index 0000000..3548c7f
--- /dev/null
@@ -0,0 +1,65 @@
+<?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/>.
+defined('MOODLE_INTERNAL') || exit();
+
+/**
+ * Unit tests for the subscription class.
+ * @since 3.2.0
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_subscription_testcase extends advanced_testcase {
+
+    /**
+     * @var \tool_monitor\subscription $subscription object.
+     */
+    private $subscription;
+
+    /**
+     * Test set up.
+     */
+    public function setUp() {
+        $this->resetAfterTest(true);
+
+        // Create the mock subscription.
+        $sub = new stdClass();
+        $sub->id = 100;
+        $sub->name = 'My test rule';
+        $sub->courseid = 20;
+        $this->subscription = $this->getMock('\tool_monitor\subscription',null, array($sub));
+    }
+
+    /**
+     * Test for the magic __isset method.
+     */
+    public function test_magic_isset() {
+        $this->assertEquals(true, isset($this->subscription->name));
+        $this->assertEquals(true, isset($this->subscription->courseid));
+        $this->assertEquals(false, isset($this->subscription->ruleid));
+    }
+
+    /**
+     * Test for the magic __get method.
+     */
+    public function test_magic_get() {
+        $this->assertEquals(20, $this->subscription->courseid);
+        $this->setExpectedException('coding_exception');
+        $this->subscription->ruleid;
+    }
+}
diff --git a/admin/tool/monitor/tests/task_check_subscriptions_test.php b/admin/tool/monitor/tests/task_check_subscriptions_test.php
new file mode 100644 (file)
index 0000000..b526784
--- /dev/null
@@ -0,0 +1,364 @@
+<?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/>.
+defined('MOODLE_INTERNAL') || exit();
+
+/**
+ * Unit tests for the tool_monitor clean events task.
+ * @since 3.2.0
+ *
+ * @package    tool_monitor
+ * @category   test
+ * @copyright  2016 Jake Dallimore <jrhdallimore@gmail.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class tool_monitor_task_check_subscriptions_testcase extends advanced_testcase {
+
+    private $course;
+    private $user;
+    private $rule;
+    private $subscription;
+    private $teacherrole;
+    private $studentrole;
+
+    /**
+     * Test set up.
+     */
+    public function setUp() {
+        global $DB;
+        set_config('enablemonitor', 1, 'tool_monitor');
+        $this->resetAfterTest(true);
+
+        // All tests defined herein need a user, course, rule and subscription, so set these up.
+        $this->user = $this->getDataGenerator()->create_user();
+        $this->course = $this->getDataGenerator()->create_course();
+
+        $rule = new stdClass();
+        $rule->userid = 2; // Rule created by admin.
+        $rule->courseid = $this->course->id;
+        $rule->plugin = 'mod_book';
+        $rule->eventname = '\mod_book\event\course_module_viewed';
+        $rule->timewindow = 500;
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $this->rule = $monitorgenerator->create_rule($rule);
+
+        $sub = new stdClass();
+        $sub->courseid = $this->course->id;
+        $sub->userid = $this->user->id;
+        $sub->ruleid = $this->rule->id;
+        $this->subscription = $monitorgenerator->create_subscription($sub);
+
+        // Also set up a student and a teacher role for use in some tests.
+        $this->teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
+        $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
+    }
+
+    /**
+     * Reloads the subscription object from the DB.
+     *
+     * @return void.
+     */
+    private function reload_subscription() {
+        global $DB;
+        $sub = $DB->get_record('tool_monitor_subscriptions', array('id' => $this->subscription->id));
+        $this->subscription = new \tool_monitor\subscription($sub);
+    }
+
+    /**
+     * Test to confirm the task is named correctly.
+     */
+    public function test_task_name() {
+        $task = new \tool_monitor\task\check_subscriptions();
+        $this->assertEquals(get_string('taskchecksubscriptions', 'tool_monitor'), $task->get_name());
+    }
+
+    /**
+     * Test to confirm that site level subscriptions are activated and deactivated according to system capabilities.
+     */
+    public function test_site_level_subscription() {
+        // Create a site level subscription.
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $sub = new stdClass();
+        $sub->userid = $this->user->id;
+        $sub->ruleid = $this->rule->id;
+        $this->subscription = $monitorgenerator->create_subscription($sub);
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should be inactive as the user doesn't have the capability. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Now, assign the user as a teacher role at system context.
+        $this->getDataGenerator()->role_assign($this->teacherrole->id, $this->user->id, context_system::instance());
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should be active now. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that if the module is disabled, no changes are made to active subscriptions.
+     */
+    public function test_module_disabled() {
+        set_config('enablemonitor', 0, 'tool_monitor');
+
+        // Subscription should be active to start with.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task. Note, we never enrolled the user.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should still be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm an active, valid subscription stays active once the scheduled task is run.
+     */
+    public function test_active_unaffected() {
+        // Enrol the user as a teacher. This role should have the required capability.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+        // Subscription should be active to start with.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should still be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that a subscription for a user without an enrolment to the course is made inactive.
+     */
+    public function test_course_enrolment() {
+        // Subscription should be active until deactivated by the scheduled task. Remember, by default the test setup
+        // doesn't enrol the user, so the first run of the task should deactivate it.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should NOT be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Enrol the user.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // Subscription should now be active again.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that subscriptions for enrolled users without the required capability are made inactive.
+     */
+    public function test_enrolled_user_with_no_capability() {
+        // Enrol the user. By default, students won't have the required capability.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
+
+        // The subscription should be active to start with. Pass in the id only to refetch the data.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should NOT be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that subscriptions for users who fail can_access_course(), are deactivated.
+     */
+    public function test_can_access_course() {
+        // Enrol the user as a teacher. This role should have the required capability.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+        // Strip the ability to see hidden courses, so we'll fail the check_subscriptions->user_can_access_course call.
+        $context = \context_course::instance($this->course->id);
+        assign_capability('moodle/course:viewhiddencourses', CAP_PROHIBIT, $this->teacherrole->id, $context);
+
+        // Subscription should be active to start with.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Hide the course.
+        course_change_visibility($this->course->id, false);
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should be inactive. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that subscriptions for enrolled users who don't have CM access, are deactivated.
+     */
+    public function test_cm_access() {
+        // Enrol the user as a student but grant to ability to subscribe. Students cannot view hidden activities.
+        $context = \context_course::instance($this->course->id);
+        assign_capability('tool/monitor:subscribe', CAP_ALLOW, $this->studentrole->id, $context);
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->studentrole->id);
+
+        // Generate a course module.
+        $book = $this->getDataGenerator()->create_module('book', array('course' => $this->course->id));
+
+        // And add a subscription to it.
+        $sub = new stdClass();
+        $sub->courseid = $this->course->id;
+        $sub->userid = $this->user->id;
+        $sub->ruleid = $this->rule->id;
+        $sub->cmid = $book->cmid;
+        $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
+        $this->subscription = $monitorgenerator->create_subscription($sub);
+
+        // The subscription should be active to start with. Pass in the id only to refetch the data.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should still be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Make the course module invisible, which should in turn make the subscription inactive.
+        set_coursemodule_visible($book->cmid, false);
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should NOT be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Make the course module visible again.
+        set_coursemodule_visible($book->cmid, true);
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should be active. Pass in the id only to refetch the data.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that long term inactive subscriptions are removed entirely.
+     */
+    public function test_stale_subscription_removal() {
+        global $DB;
+        // Manually set the inactivedate to 1 day older than the limit allowed.
+        $daysold = 1 + \tool_monitor\subscription_manager::INACTIVE_SUBSCRIPTION_LIFESPAN_IN_DAYS;
+
+        $inactivedate = strtotime("-$daysold days", time());
+        $DB->set_field('tool_monitor_subscriptions', 'inactivedate', $inactivedate, array('id' => $this->subscription->id));
+
+        // Subscription should be inactive to start with.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // Subscription should now not exist at all.
+        $this->assertEquals(false, $DB->record_exists('tool_monitor_subscriptions', array('id' => $this->subscription->id)));
+    }
+
+    /**
+     * Test to confirm that subscriptions for a partially set up user are deactivated.
+     */
+    public function test_user_not_fully_set_up() {
+        global $DB;
+
+        // Enrol the user as a teacher.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+        // The subscription should be active to start.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Unset the user's email address, so we fail the check_subscriptions->is_user_setup() call.
+        $DB->set_field('user', 'email', '', array('id' => $this->user->id));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should now be inactive.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+
+    /**
+     * Test to confirm that a suspended user's subscriptions are deactivated properly.
+     */
+    public function test_suspended_user() {
+        global $DB;
+
+        // Enrol the user as a teacher. This role should have the required capability.
+        $this->getDataGenerator()->enrol_user($this->user->id, $this->course->id, $this->teacherrole->id);
+
+        // Subscription should be active to start with.
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Suspend the user.
+        $DB->set_field('user', 'suspended', '1', array('id' => $this->user->id));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should now be inactive.
+        $this->reload_subscription();
+        $this->assertEquals(false, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+
+        // Unsuspend the user.
+        $DB->set_field('user', 'suspended', '0', array('id' => $this->user->id));
+
+        // Run the task.
+        $task = new \tool_monitor\task\check_subscriptions();
+        $task->execute();
+
+        // The subscription should now be active again.
+        $this->reload_subscription();
+        $this->assertEquals(true, \tool_monitor\subscription_manager::subscription_is_active($this->subscription));
+    }
+}
index c078165..9e5a5a3 100644 (file)
@@ -26,6 +26,6 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-$plugin->version   = 2016052300;     // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version   = 2016052305;     // The current plugin version (Date: YYYYMMDDXX).
 $plugin->requires  = 2016051900;     // Requires this Moodle version.
 $plugin->component = 'tool_monitor'; // Full name of the plugin (used for diagnostics).
index 2fabeb2..8b115e6 100644 (file)
@@ -31,7 +31,7 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
      * @param {String[]} templateList List of template ids.
      */
     var reloadListTemplate = function(templateList) {
-        templates.render('tool_templatelibrary/search_results', { templates: templateList })
+        templates.render('tool_templatelibrary/search_results', {templates: templateList})
             .done(function(result, js) {
                 templates.replaceNode($('[data-region="searchresults"]'), result, js);
             }).fail(notification.exception);
@@ -49,10 +49,10 @@ define(['jquery', 'core/ajax', 'core/log', 'core/notification', 'core/templates'
         // Trigger the search.
 
         ajax.call([
-            { methodname: 'tool_templatelibrary_list_templates',
-              args: { component: componentStr, search: searchStr },
+            {methodname: 'tool_templatelibrary_list_templates',
+              args: {component: componentStr, search: searchStr},
               done: reloadListTemplate,
-              fail: notification.exception }
+              fail: notification.exception}
         ], true, false);
     };
 
index 5900d32..99d0ce5 100644 (file)
@@ -122,7 +122,7 @@ class edit_field extends XMLDBAction {
             $o.= '      <input type="hidden" name ="name" value="' .  s($field->getName()) .'" />';
             $o.= '      <tr valign="top"><td>Name:</td><td colspan="2">' . s($field->getName()) . '</td></tr>';
         } else {
-            $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" maxlength="30" id="name" value="' . s($field->getName()) . '" /></td></tr>';
+            $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" maxlength="'.xmldb_field::NAME_MAX_LENGTH.'" id="name" value="' . s($field->getName()) . '" /></td></tr>';
         }
         // XMLDB field comment
         $o.= '      <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($field->getComment()) . '</textarea></td></tr>';
index 7725c9e..a72b1c5 100644 (file)
@@ -113,7 +113,7 @@ class edit_index extends XMLDBAction {
         if ($structure->getIndexUses($table->getName(), $index->getName())) {
             $disabled = ' disabled="disabled " ';
         }
-        $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" id="name"' . $disabled . ' value="' . s($index->getName()) . '" /></td></tr>';
+        $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" id="name"' . $disabled . ' value="' . s($index->getName()) . '" /></td></tr>';
         // XMLDB key comment
         $o.= '      <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($index->getComment()) . '</textarea></td></tr>';
         // xmldb_index Type
index c1d1c3b..6e32acf 100644 (file)
@@ -113,7 +113,7 @@ class edit_key extends XMLDBAction {
         if ($structure->getKeyUses($table->getName(), $key->getName())) {
             $disabled = ' disabled="disabled " ';
         }
-        $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="30" id="name"' . $disabled . ' value="' . s($key->getName()) . '" /></td></tr>';
+        $o.= '      <tr valign="top"><td><label for="name" accesskey="n">Name:</label></td><td colspan="2"><input name="name" type="text" size="'.xmldb_field::NAME_MAX_LENGTH.'" id="name"' . $disabled . ' value="' . s($key->getName()) . '" /></td></tr>';
         // XMLDB key comment
         $o.= '      <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td colspan="2"><textarea name="comment" rows="3" cols="80" id="comment">' . s($key->getComment()) . '</textarea></td></tr>';
         // xmldb_key Type
index 4199a6c..5c2e7b9 100644 (file)
@@ -129,7 +129,7 @@ class edit_table extends XMLDBAction {
         if ($structure->getTableUses($table->getName())) {
             $o.= '      <tr valign="top"><td>Name:</td><td><input type="hidden" name ="name" value="' . s($table->getName()) . '" />' . s($table->getName()) .'</td></tr>';
         } else {
-            $o.= '      <tr valign="top"><td><label for="name" accesskey="p">Name:</label></td><td><input name="name" type="text" size="28" maxlength="28" id="name" value="' . s($table->getName()) . '" /></td></tr>';
+            $o.= '      <tr valign="top"><td><label for="name" accesskey="p">Name:</label></td><td><input name="name" type="text" size="'.xmldb_table::NAME_MAX_LENGTH.'" maxlength="'.xmldb_table::NAME_MAX_LENGTH.'" id="name" value="' . s($table->getName()) . '" /></td></tr>';
         }
         $o.= '      <tr valign="top"><td><label for="comment" accesskey="c">Comment:</label></td><td><textarea name="comment" rows="3" cols="80" id="comment">' . s($table->getComment()) . '</textarea></td></tr>';
         $o.= '      <tr valign="top"><td>&nbsp;</td><td><input type="submit" value="' .$this->str['change'] . '" /></td></tr>';
index ebebaa1..b1fa091 100644 (file)
@@ -302,20 +302,27 @@ class auth_plugin_db extends auth_plugin_base {
 
             // Find obsolete users.
             if (count($userlist)) {
-                list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
-                $params['authtype'] = $this->authtype;
-                $sql = "SELECT u.*
+                $remove_users = array();
+                // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
+                $userlistchunks = array_chunk($userlist , 10000);
+                foreach($userlistchunks as $userlistchunk) {
+                    list($notin_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', false);
+                    $params['authtype'] = $this->authtype;
+                    $sql = "SELECT u.id, u.username
                           FROM {user} u
                          WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql";
+                    $params['mnethostid'] = $CFG->mnet_localhost_id;
+                    $remove_users = $remove_users + $DB->get_records_sql($sql, $params);
+                }
             } else {
-                $sql = "SELECT u.*
+                $sql = "SELECT u.id, u.username
                           FROM {user} u
                          WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect";
                 $params = array();
                 $params['authtype'] = $this->authtype;
+                $params['mnethostid'] = $CFG->mnet_localhost_id;
+                $remove_users = $DB->get_records_sql($sql, $params);
             }
-            $params['mnethostid'] = $CFG->mnet_localhost_id;
-            $remove_users = $DB->get_records_sql($sql, $params);
 
             if (!empty($remove_users)) {
                 $trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
@@ -358,12 +365,20 @@ class auth_plugin_db extends auth_plugin_base {
 
             // Only go ahead if we actually have fields to update locally.
             if (!empty($updatekeys)) {
-                list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
-                $params['authtype'] = $this->authtype;
-                $sql = "SELECT u.id, u.username
+                $update_users = array();
+                // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php
+                $userlistchunks = array_chunk($userlist , 10000);
+                foreach($userlistchunks as $userlistchunk) {
+                    list($in_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', true);
+                    $params['authtype'] = $this->authtype;
+                    $params['mnethostid'] = $CFG->mnet_localhost_id;
+                    $sql = "SELECT u.id, u.username
                           FROM {user} u
-                         WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
-                if ($update_users = $DB->get_records_sql($sql, $params)) {
+                         WHERE u.auth = :authtype AND u.deleted = 0 AND u.mnethostid = :mnethostid AND u.username {$in_sql}";
+                    $update_users = $update_users + $DB->get_records_sql($sql, $params);
+                }
+
+                if ($update_users) {
                     $trace->output("User entries to update: ".count($update_users));
 
                     foreach ($update_users as $user) {
index a440bc2..043111e 100644 (file)
@@ -4152,7 +4152,7 @@ class api {
      * @param int $limit Number of records to return.
      * @return \core_competency\evidence[]
      */
-    public static function list_evidence_in_course($userid = 0, $courseid = 0, $competencyid = 0, $sort = 'timecreated, id',
+    public static function list_evidence_in_course($userid = 0, $courseid = 0, $competencyid = 0, $sort = 'timecreated',
                                                    $order = 'DESC', $skip = 0, $limit = 0) {
         static::require_enabled();
 
index 79a8cc5..1e12a36 100644 (file)
@@ -88,7 +88,7 @@ class course_competency_settings extends persistent {
     public static function can_read($courseid) {
         $context = context_course::instance($courseid);
 
-        $capabilities = array('moodle/competency:coursecompetencyview');
+        $capabilities = array('moodle/competency:coursecompetencyview', 'moodle/competency:coursecompetencymanage');
 
         return has_any_capability($capabilities, $context);
     }
index cc8ec05..40e1448 100644 (file)
@@ -311,9 +311,9 @@ class evidence extends persistent {
         );
 
         if (!empty($sort)) {
-            $sortcolumns = explode(',', $sort);
-            $sortcolumns = array_map('trim', $sortcolumns);
-            $sort = ' ORDER BY e.' . implode(', e.', $sortcolumns) . ' ' . $order;
+            $sort = ' ORDER BY e.' . $sort . ' ' . $order . ', e.id ASC';
+        } else {
+            $sort = ' ORDER BY e.id ASC';
         }
 
         $sql = 'SELECT e.*
index 44aaf76..cc96253 100644 (file)
@@ -2615,9 +2615,9 @@ class core_competency_api_testcase extends advanced_testcase {
         $evidence2 = api::add_evidence($u1->id, $c->get_id(), $cmcontext->id, \core_competency\evidence::ACTION_LOG,
             'invaliddata', 'error');
 
-        $result = api::list_evidence_in_course($u1->id, $course->id, $c->get_id());
-        $this->assertEquals($result[0]->get_id(), $evidence2->get_id());
-        $this->assertEquals($result[1]->get_id(), $evidence1->get_id());
+        $result = api::list_evidence_in_course($u1->id, $course->id, $c->get_id(), 'timecreated', 'ASC');
+        $this->assertEquals($evidence1->get_id(), $result[0]->get_id());
+        $this->assertEquals($evidence2->get_id(), $result[1]->get_id());
     }
 
     public function test_list_course_modules_using_competency() {
index ee734d8..73e59f1 100644 (file)
@@ -1,4 +1,9 @@
 {
+    "name": "moodle/moodle",
+    "license": "GPL-3.0",
+    "description": "Moodle - the world's open source learning platform",
+    "type": "project",
+    "homepage": "https://moodle.org",
     "require-dev": {
         "phpunit/phpunit": "4.8.*",
         "phpunit/dbUnit": "1.4.*",
index 7a5f30b..b75defd 100644 (file)
@@ -49,7 +49,7 @@ class mycourse extends \core_search\area\base {
      */
     public function get_recordset_by_timestamp($modifiedfrom = 0) {
         global $DB;
-        return $DB->get_recordset_select('course', 'timemodified >= ?', array($modifiedfrom));
+        return $DB->get_recordset_select('course', 'timemodified >= ?', array($modifiedfrom), 'timemodified ASC');
     }
 
     /**
index e868841..3458723 100644 (file)
@@ -195,9 +195,11 @@ class core_course_external extends external_api {
                 $sectionvalues['id'] = $section->id;
                 $sectionvalues['name'] = get_section_name($course, $section);
                 $sectionvalues['visible'] = $section->visible;
+
+                $options = (object) array('noclean' => true);
                 list($sectionvalues['summary'], $sectionvalues['summaryformat']) =
                         external_format_text($section->summary, $section->summaryformat,
-                                $context->id, 'course', 'section', $section->id);
+                                $context->id, 'course', 'section', $section->id, $options);
                 $sectionvalues['section'] = $section->section;
                 $sectioncontents = array();
 
@@ -431,8 +433,8 @@ class core_course_external extends external_api {
 
             $courseinfo = array();
             $courseinfo['id'] = $course->id;
-            $courseinfo['fullname'] = $course->fullname;
-            $courseinfo['shortname'] = $course->shortname;
+            $courseinfo['fullname'] = external_format_string($course->fullname, $context->id);
+            $courseinfo['shortname'] = external_format_string($course->shortname, $context->id);
             $courseinfo['displayname'] = external_format_string(get_course_display_name_for_list($course), $context->id);
             $courseinfo['categoryid'] = $course->category;
             list($courseinfo['summary'], $courseinfo['summaryformat']) =
@@ -1550,7 +1552,7 @@ class core_course_external extends external_api {
                     }
 
                     if (isset($value)) {
-                        $conditions[$key] = $crit['value'];
+                        $conditions[$key] = $value;
                         $wheres[] = $key . " = :" . $key;
                     }
                 }
index 549b054..71372e7 100644 (file)
@@ -234,6 +234,16 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
 
         $this->assertEquals(1, count($categories));
 
+        // Same query, but forcing a parameters clean.
+        $categories = core_course_external::get_categories(array(
+            array('key' => 'id', 'value' => "$category1->id"),
+            array('key' => 'idnumber', 'value' => $category1->idnumber),
+            array('key' => 'name', 'value' => $category1->name . "<br/>"),
+            array('key' => 'visible', 'value' => '1')), 0);
+        $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories);
+
+        $this->assertEquals(1, count($categories));
+
         // Retrieve categories from parent.
         $categories = core_course_external::get_categories(array(
             array('key' => 'parent', 'value' => $category3->id)), 1);
@@ -521,7 +531,9 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
 
         $generatedcourses = array();
         $coursedata['idnumber'] = 'idnumbercourse1';
-        $coursedata['fullname'] = 'Course 1 for PHPunit test';
+        // Adding tags here to check that format_string is applied.
+        $coursedata['fullname'] = '<b>Course 1 for PHPunit test</b>';
+        $coursedata['shortname'] = '<b>Course 1 for PHPunit test</b>';
         $coursedata['summary'] = 'Course 1 description';
         $coursedata['summaryformat'] = FORMAT_MOODLE;
         $course1  = self::getDataGenerator()->create_course($coursedata);
@@ -551,14 +563,16 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         $this->assertEquals(2, count($courses));
 
         foreach ($courses as $course) {
+            $coursecontext = context_course::instance($course['id']);
             $dbcourse = $generatedcourses[$course['id']];
             $this->assertEquals($course['idnumber'], $dbcourse->idnumber);
-            $this->assertEquals($course['fullname'], $dbcourse->fullname);
-            $this->assertEquals($course['displayname'], get_course_display_name_for_list($dbcourse));
+            $this->assertEquals($course['fullname'], external_format_string($dbcourse->fullname, $coursecontext->id));
+            $this->assertEquals($course['displayname'], external_format_string(get_course_display_name_for_list($dbcourse),
+                $coursecontext->id));
             // Summary was converted to the HTML format.
             $this->assertEquals($course['summary'], format_text($dbcourse->summary, FORMAT_MOODLE, array('para' => false)));
             $this->assertEquals($course['summaryformat'], FORMAT_HTML);
-            $this->assertEquals($course['shortname'], $dbcourse->shortname);
+            $this->assertEquals($course['shortname'], external_format_string($dbcourse->shortname, $coursecontext->id));
             $this->assertEquals($course['categoryid'], $dbcourse->category);
             $this->assertEquals($course['format'], $dbcourse->format);
             $this->assertEquals($course['showgrades'], $dbcourse->showgrades);
@@ -686,6 +700,7 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
      * @return array A list with the course object and course modules objects
      */
     private function prepare_get_course_contents_test() {
+        global $DB;
         $course  = self::getDataGenerator()->create_course();
         $forumdescription = 'This is the forum description';
         $forum = $this->getDataGenerator()->create_module('forum',
@@ -710,6 +725,10 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         $roleid = $this->assignUserCapability('moodle/course:view', $context->id);
         $this->assignUserCapability('moodle/course:update', $context->id, $roleid);
 
+        $conditions = array('course' => $course->id, 'section' => 2);
+        $DB->set_field('course_sections', 'summary', 'Text with iframe <iframe src="https://moodle.org"></iframe>', $conditions);
+        rebuild_course_cache($course->id, true);
+
         return array($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm);
     }
 
@@ -755,6 +774,8 @@ class core_course_externallib_testcase extends externallib_advanced_testcase {
         $this->assertCount(4, $firstsection['modules']);
         $this->assertCount(1, $lastsection['modules']);
         $this->assertEquals(2, $lastsection['section']);
+        $this->assertContains('<iframe', $lastsection['summary']);
+        $this->assertContains('</iframe>', $lastsection['summary']);
 
         try {
             $sections = core_course_external::get_course_contents($course->id,
index 2bd50f0..6bb4865 100644 (file)
@@ -7,7 +7,7 @@
     require_once($CFG->libdir.'/completionlib.php');
 
     $id          = optional_param('id', 0, PARAM_INT);
-    $name        = optional_param('name', '', PARAM_RAW);
+    $name        = optional_param('name', '', PARAM_TEXT);
     $edit        = optional_param('edit', -1, PARAM_BOOL);
     $hide        = optional_param('hide', 0, PARAM_INT);
     $show        = optional_param('show', 0, PARAM_INT);
index fe94ff0..d10c10e 100644 (file)
Binary files a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js and b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop-debug.js differ
index 7befde3..76a249b 100644 (file)
Binary files a/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js and b/course/yui/build/moodle-course-dragdrop/moodle-course-dragdrop.js differ
index 0b3587a..57e0b16 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management-debug.js and b/course/yui/build/moodle-course-management/moodle-course-management-debug.js differ
index 883b964..35520d1 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management.js and b/course/yui/build/moodle-course-management/moodle-course-management.js differ
index 212e98d..4d7656b 100644 (file)
Binary files a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js and b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes-debug.js differ
index 212e98d..4d7656b 100644 (file)
Binary files a/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js and b/course/yui/build/moodle-course-toolboxes/moodle-course-toolboxes.js differ
index 00b30d4..b1e3ef5 100644 (file)
@@ -1,3 +1,4 @@
+/* eslint-disable no-unused-vars */
 /**
  * Drag and Drop for course sections and course modules.
  *
index b7fac22..889ccec 100644 (file)
@@ -1,3 +1,5 @@
+/* global Item */
+
 /**
  * A managed category.
  *
index 0a00ab1..12ca9be 100644 (file)
@@ -1,3 +1,5 @@
+/* global DragDrop, Category, Course */
+
 /**
  * Provides drop down menus for list of action links.
  *
index 474180a..893b21c 100644 (file)
@@ -1,3 +1,5 @@
+/* global Item */
+
 /**
  * A managed course.
  *
index 3ad3e0f..f622d29 100644 (file)
@@ -1,3 +1,5 @@
+/* global Console */
+
 /**
  * Drag and Drop handler
  *
index f475b89..4d87574 100644 (file)
@@ -1,3 +1,5 @@
+/* global TOOLBOX, BODY, SELECTOR, INDENTLIMITS */
+
 /**
  * Resource and activity toolbox class.
  *
index fe9b84e..a28ca9a 100644 (file)
@@ -1,3 +1,5 @@
+/* global SELECTOR, TOOLBOX */
+
 /**
  * Resource and activity toolbox class.
  *
index e3f6c8a..019b359 100644 (file)
@@ -1,3 +1,4 @@
+/* eslint-disable no-unused-vars */
 /**
  * Resource and activity toolbox class.
  *
@@ -196,4 +197,3 @@ Y.extend(TOOLBOX, Y.Base, {
     }
 }
 );
-
index 8db19d9..711cc69 100644 (file)
@@ -300,7 +300,7 @@ class core_enrol_external extends external_api {
         $params = self::validate_parameters(self::get_users_courses_parameters(), array('userid'=>$userid));
 
         $courses = enrol_get_users_courses($params['userid'], true, 'id, shortname, fullname, idnumber, visible,
-                   summary, summaryformat, format, showgrades, lang, enablecompletion');
+                   summary, summaryformat, format, showgrades, lang, enablecompletion, category');
         $result = array();
 
         foreach ($courses as $course) {
@@ -323,12 +323,24 @@ class core_enrol_external extends external_api {
 
             list($course->summary, $course->summaryformat) =
                 external_format_text($course->summary, $course->summaryformat, $context->id, 'course', 'summary', null);
-
-            $result[] = array('id' => $course->id, 'shortname' => $course->shortname, 'fullname' => $course->fullname,
-                'idnumber' => $course->idnumber, 'visible' => $course->visible, 'enrolledusercount' => $enrolledusercount,
-                'summary' => $course->summary, 'summaryformat' => $course->summaryformat, 'format' => $course->format,
-                'showgrades' => $course->showgrades, 'lang' => $course->lang, 'enablecompletion' => $course->enablecompletion
-                );
+            $course->fullname = external_format_string($course->fullname, $context->id);
+            $course->shortname = external_format_string($course->shortname, $context->id);
+
+            $result[] = array(
+                'id' => $course->id,
+                'shortname' => $course->shortname,
+                'fullname' => $course->fullname,
+           &nbs