Merge branch 'wip-mdl-42261' of git://github.com/rajeshtaneja/moodle
authorDan Poltawski <dan@moodle.com>
Fri, 25 Oct 2013 03:33:19 +0000 (11:33 +0800)
committerDan Poltawski <dan@moodle.com>
Fri, 25 Oct 2013 03:33:19 +0000 (11:33 +0800)
484 files changed:
admin/auth_config.php
admin/tool/behat/renderer.php
admin/tool/behat/version.php
admin/tool/generator/cli/maketestplan.php
auth/cas/config.html
auth/db/config.html
auth/fc/config.html
auth/ldap/auth.php
auth/ldap/config.html
auth/ldap/tests/plugin_test.php
auth/shibboleth/index.php
auth/yui/passwordunmask/passwordunmask.js [new file with mode: 0644]
backup/moodle2/backup_xml_transformer.class.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
blocks/quiz_results/block_quiz_results.php
blocks/tests/behat/return_block_original_state.feature [new file with mode: 0644]
blog/edit.php
blog/rsslib.php
calendar/lib.php
calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-debug.js
calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-min.js
calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager.js
calendar/yui/src/eventmanager/js/eventmanager.js
course/ajax/management.php
course/classes/management/helper.php
course/classes/management_renderer.php
course/dndupload.js
course/format/renderer.php
course/management.php
course/tests/behat/behat_course.php
course/tests/behat/course_category_management_listing.feature
course/tests/behat/course_controls.feature
course/tests/behat/create_delete_course.feature
course/yui/build/moodle-course-management/moodle-course-management-debug.js
course/yui/build/moodle-course-management/moodle-course-management-min.js
course/yui/build/moodle-course-management/moodle-course-management.js
course/yui/dragdrop/dragdrop.js
course/yui/src/management/js/category.js
course/yui/src/management/js/console.js
course/yui/toolboxes/toolboxes.js
enrol/imsenterprise/README.txt [deleted file]
enrol/imsenterprise/TODO.txt [deleted file]
enrol/imsenterprise/db/install.php
enrol/imsenterprise/db/upgrade.php
enrol/imsenterprise/entv1p1_conformance_summary.html [deleted file]
enrol/imsenterprise/importnow.php
enrol/imsenterprise/lib.php
enrol/imsenterprise/locallib.php
enrol/imsenterprise/settings.php
enrol/imsenterprise/tests/imsenterprise_test.php
enrol/imsenterprise/version.php
enrol/meta/lib.php
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-debug.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker-min.js
filter/glossary/yui/build/moodle-filter_glossary-autolinker/moodle-filter_glossary-autolinker.js
filter/glossary/yui/src/autolinker/js/autolinker.js
filter/glossary/yui/src/autolinker/meta/autolinker.json
group/tests/behat/auto_creation.feature
install/lang/ky/langconfig.php [moved from lib/editor/atto/classes/plugininfo/atto.php with 63% similarity]
install/lang/pt/error.php
install/lang/pt/install.php
lang/en/cache.php
lang/en/moodle.php
lib/adminlib.php
lib/behat/classes/util.php
lib/classes/event/user_list_viewed.php [new file with mode: 0644]
lib/classes/event/user_loggedin.php
lib/classes/event/user_profile_viewed.php [new file with mode: 0644]
lib/classes/plugin_manager.php
lib/classes/plugininfo/filter.php
lib/db/caches.php
lib/db/install.xml
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/atto/db/install.php [deleted file]
lib/editor/atto/db/subplugins.php [deleted file]
lib/editor/atto/lang/en/editor_atto.php [deleted file]
lib/editor/atto/lib.php [deleted file]
lib/editor/atto/plugins/bold/lang/en/atto_bold.php [deleted file]
lib/editor/atto/plugins/bold/lib.php [deleted file]
lib/editor/atto/plugins/bold/version.php [deleted file]
lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-debug.js [deleted file]
lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button-min.js [deleted file]
lib/editor/atto/plugins/bold/yui/build/moodle-atto_bold-button/moodle-atto_bold-button.js [deleted file]
lib/editor/atto/plugins/bold/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/bold/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/bold/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/clear/lang/en/atto_clear.php [deleted file]
lib/editor/atto/plugins/clear/lib.php [deleted file]
lib/editor/atto/plugins/clear/version.php [deleted file]
lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-debug.js [deleted file]
lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button-min.js [deleted file]
lib/editor/atto/plugins/clear/yui/build/moodle-atto_clear-button/moodle-atto_clear-button.js [deleted file]
lib/editor/atto/plugins/clear/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/clear/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/clear/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/html/lang/en/atto_html.php [deleted file]
lib/editor/atto/plugins/html/lib.php [deleted file]
lib/editor/atto/plugins/html/version.php [deleted file]
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-debug.js [deleted file]
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button-min.js [deleted file]
lib/editor/atto/plugins/html/yui/build/moodle-atto_html-button/moodle-atto_html-button.js [deleted file]
lib/editor/atto/plugins/html/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/html/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/html/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/image/lang/en/atto_image.php [deleted file]
lib/editor/atto/plugins/image/lib.php [deleted file]
lib/editor/atto/plugins/image/version.php [deleted file]
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js [deleted file]
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js [deleted file]
lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js [deleted file]
lib/editor/atto/plugins/image/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/image/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/image/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/indent/lang/en/atto_indent.php [deleted file]
lib/editor/atto/plugins/indent/lib.php [deleted file]
lib/editor/atto/plugins/indent/version.php [deleted file]
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-debug.js [deleted file]
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button-min.js [deleted file]
lib/editor/atto/plugins/indent/yui/build/moodle-atto_indent-button/moodle-atto_indent-button.js [deleted file]
lib/editor/atto/plugins/indent/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/indent/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/indent/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/italic/lang/en/atto_italic.php [deleted file]
lib/editor/atto/plugins/italic/lib.php [deleted file]
lib/editor/atto/plugins/italic/version.php [deleted file]
lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-debug.js [deleted file]
lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button-min.js [deleted file]
lib/editor/atto/plugins/italic/yui/build/moodle-atto_italic-button/moodle-atto_italic-button.js [deleted file]
lib/editor/atto/plugins/italic/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/italic/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/italic/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/link/lang/en/atto_link.php [deleted file]
lib/editor/atto/plugins/link/lib.php [deleted file]
lib/editor/atto/plugins/link/version.php [deleted file]
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js [deleted file]
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js [deleted file]
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js [deleted file]
lib/editor/atto/plugins/link/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/link/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/link/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/media/lang/en/atto_media.php [deleted file]
lib/editor/atto/plugins/media/lib.php [deleted file]
lib/editor/atto/plugins/media/version.php [deleted file]
lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-debug.js [deleted file]
lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button-min.js [deleted file]
lib/editor/atto/plugins/media/yui/build/moodle-atto_media-button/moodle-atto_media-button.js [deleted file]
lib/editor/atto/plugins/media/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/media/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/media/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/orderedlist/lang/en/atto_orderedlist.php [deleted file]
lib/editor/atto/plugins/orderedlist/lib.php [deleted file]
lib/editor/atto/plugins/orderedlist/version.php [deleted file]
lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-debug.js [deleted file]
lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button-min.js [deleted file]
lib/editor/atto/plugins/orderedlist/yui/build/moodle-atto_orderedlist-button/moodle-atto_orderedlist-button.js [deleted file]
lib/editor/atto/plugins/orderedlist/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/orderedlist/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/orderedlist/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/outdent/lang/en/atto_outdent.php [deleted file]
lib/editor/atto/plugins/outdent/lib.php [deleted file]
lib/editor/atto/plugins/outdent/version.php [deleted file]
lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-debug.js [deleted file]
lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button-min.js [deleted file]
lib/editor/atto/plugins/outdent/yui/build/moodle-atto_outdent-button/moodle-atto_outdent-button.js [deleted file]
lib/editor/atto/plugins/outdent/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/outdent/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/outdent/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/strike/lang/en/atto_strike.php [deleted file]
lib/editor/atto/plugins/strike/lib.php [deleted file]
lib/editor/atto/plugins/strike/version.php [deleted file]
lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-debug.js [deleted file]
lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button-min.js [deleted file]
lib/editor/atto/plugins/strike/yui/build/moodle-atto_strike-button/moodle-atto_strike-button.js [deleted file]
lib/editor/atto/plugins/strike/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/strike/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/strike/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/title/lang/en/atto_title.php [deleted file]
lib/editor/atto/plugins/title/lib.php [deleted file]
lib/editor/atto/plugins/title/version.php [deleted file]
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-debug.js [deleted file]
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button-min.js [deleted file]
lib/editor/atto/plugins/title/yui/build/moodle-atto_title-button/moodle-atto_title-button.js [deleted file]
lib/editor/atto/plugins/title/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/title/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/title/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/underline/lang/en/atto_underline.php [deleted file]
lib/editor/atto/plugins/underline/lib.php [deleted file]
lib/editor/atto/plugins/underline/version.php [deleted file]
lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-debug.js [deleted file]
lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button-min.js [deleted file]
lib/editor/atto/plugins/underline/yui/build/moodle-atto_underline-button/moodle-atto_underline-button.js [deleted file]
lib/editor/atto/plugins/underline/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/underline/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/underline/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/unlink/lang/en/atto_unlink.php [deleted file]
lib/editor/atto/plugins/unlink/lib.php [deleted file]
lib/editor/atto/plugins/unlink/version.php [deleted file]
lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-debug.js [deleted file]
lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button-min.js [deleted file]
lib/editor/atto/plugins/unlink/yui/build/moodle-atto_unlink-button/moodle-atto_unlink-button.js [deleted file]
lib/editor/atto/plugins/unlink/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/unlink/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/unlink/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/plugins/unorderedlist/lang/en/atto_unorderedlist.php [deleted file]
lib/editor/atto/plugins/unorderedlist/lib.php [deleted file]
lib/editor/atto/plugins/unorderedlist/version.php [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-debug.js [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button-min.js [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/build/moodle-atto_unorderedlist-button/moodle-atto_unorderedlist-button.js [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/src/button/build.json [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/src/button/js/button.js [deleted file]
lib/editor/atto/plugins/unorderedlist/yui/src/button/meta/editor.json [deleted file]
lib/editor/atto/styles.css [deleted file]
lib/editor/atto/version.php [deleted file]
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js [deleted file]
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js [deleted file]
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js [deleted file]
lib/editor/atto/yui/src/editor/build.json [deleted file]
lib/editor/atto/yui/src/editor/js/editor.js [deleted file]
lib/editor/atto/yui/src/editor/meta/editor.json [deleted file]
lib/editorlib.php
lib/excellib.class.php
lib/filelib.php
lib/filterlib.php
lib/form/url.php
lib/formslib.php
lib/javascript-static.js
lib/moodlelib.php
lib/outputrequirementslib.php
lib/pear/OLE.php [deleted file]
lib/pear/README.txt
lib/pear/README_MOODLE.txt
lib/setup.php
lib/tablelib.php
lib/tests/behat/behat_data_generators.php
lib/tests/datalib_test.php
lib/tests/filterlib_test.php
lib/tests/tablelib_test.php [new file with mode: 0644]
lib/thirdpartylibs.xml
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.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-min.js
lib/yui/build/moodle-core-notification-dialogue/moodle-core-notification-dialogue.js
lib/yui/dragdrop/dragdrop.js
lib/yui/src/actionmenu/js/actionmenu.js
lib/yui/src/notification/js/dialogue.js
login/change_password.php
login/signup.php
login/signup_form.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-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/gradeform.php
mod/assign/locallib.php
mod/assign/tests/locallib_test.php
mod/assignment/lib.php
mod/chat/gui_header_js/chatinput.php
mod/chat/gui_sockets/chatinput.php
mod/chat/view.php
mod/choice/tests/behat/limit_responses.feature [new file with mode: 0644]
mod/data/field/url/field.class.php
mod/feedback/item/multichoice/lib.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/item/numeric/lib.php
mod/feedback/item/textarea/lib.php
mod/feedback/item/textfield/lib.php
mod/forum/discuss.php
mod/forum/lib.php
mod/forum/rsslib.php
mod/lti/grade.php
mod/lti/lang/en/lti.php
mod/lti/service.php
mod/lti/servicelib.php
mod/lti/tests/locallib_test.php
mod/quiz/report/statistics/report.php
mod/upgrade.txt
pix/e/abbr.png [deleted file]
pix/e/abbr.svg [deleted file]
pix/e/absolute.png [deleted file]
pix/e/absolute.svg [deleted file]
pix/e/acronym.png [deleted file]
pix/e/acronym.svg [deleted file]
pix/e/advance_hr.png [deleted file]
pix/e/advance_hr.svg [deleted file]
pix/e/align_center.png [deleted file]
pix/e/align_center.svg [deleted file]
pix/e/align_left.png [deleted file]
pix/e/align_left.svg [deleted file]
pix/e/align_right.png [deleted file]
pix/e/align_right.svg [deleted file]
pix/e/anchor.png [deleted file]
pix/e/anchor.svg [deleted file]
pix/e/backward.png [deleted file]
pix/e/backward.svg [deleted file]
pix/e/bold.png [deleted file]
pix/e/bold.svg [deleted file]
pix/e/bullet_list.png [deleted file]
pix/e/bullet_list.svg [deleted file]
pix/e/cell_props.png [deleted file]
pix/e/cell_props.svg [deleted file]
pix/e/cite.png [deleted file]
pix/e/cite.svg [deleted file]
pix/e/cleanup_messy_code.png [deleted file]
pix/e/cleanup_messy_code.svg [deleted file]
pix/e/clear_formatting.png [deleted file]
pix/e/clear_formatting.svg [deleted file]
pix/e/copy.png [deleted file]
pix/e/copy.svg [deleted file]
pix/e/cut.png [deleted file]
pix/e/cut.svg [deleted file]
pix/e/decrease_indent.png [deleted file]
pix/e/decrease_indent.svg [deleted file]
pix/e/delete.png [deleted file]
pix/e/delete.svg [deleted file]
pix/e/delete_col.png [deleted file]
pix/e/delete_col.svg [deleted file]
pix/e/delete_row.png [deleted file]
pix/e/delete_row.svg [deleted file]
pix/e/delete_table.png [deleted file]
pix/e/delete_table.svg [deleted file]
pix/e/document_properties.png [deleted file]
pix/e/document_properties.svg [deleted file]
pix/e/dragmath.png [deleted file]
pix/e/dragmath.svg [deleted file]
pix/e/emoticons.png [deleted file]
pix/e/emoticons.svg [deleted file]
pix/e/find_replace.png [deleted file]
pix/e/find_replace.svg [deleted file]
pix/e/forward.png [deleted file]
pix/e/forward.svg [deleted file]
pix/e/fullpage.png [deleted file]
pix/e/fullpage.svg [deleted file]
pix/e/fullscreen.png [deleted file]
pix/e/fullscreen.svg [deleted file]
pix/e/help.png [deleted file]
pix/e/help.svg [deleted file]
pix/e/increase_indent.png [deleted file]
pix/e/increase_indent.svg [deleted file]
pix/e/insert.png [deleted file]
pix/e/insert.svg [deleted file]
pix/e/insert_col_after.png [deleted file]
pix/e/insert_col_after.svg [deleted file]
pix/e/insert_col_before.png [deleted file]
pix/e/insert_col_before.svg [deleted file]
pix/e/insert_date.png [deleted file]
pix/e/insert_date.svg [deleted file]
pix/e/insert_edit_image.png [deleted file]
pix/e/insert_edit_image.svg [deleted file]
pix/e/insert_edit_link.png [deleted file]
pix/e/insert_edit_link.svg [deleted file]
pix/e/insert_edit_video.png [deleted file]
pix/e/insert_edit_video.svg [deleted file]
pix/e/insert_file.png [deleted file]
pix/e/insert_file.svg [deleted file]
pix/e/insert_horizontal_ruler.png [deleted file]
pix/e/insert_horizontal_ruler.svg [deleted file]
pix/e/insert_nonbreaking_space.png [deleted file]
pix/e/insert_nonbreaking_space.svg [deleted file]
pix/e/insert_row_after.png [deleted file]
pix/e/insert_row_after.svg [deleted file]
pix/e/insert_row_before.png [deleted file]
pix/e/insert_row_before.svg [deleted file]
pix/e/insert_time.png [deleted file]
pix/e/insert_time.svg [deleted file]
pix/e/italic.png [deleted file]
pix/e/italic.svg [deleted file]
pix/e/justify.png [deleted file]
pix/e/justify.svg [deleted file]
pix/e/layers.png [deleted file]
pix/e/layers.svg [deleted file]
pix/e/layers_over.png [deleted file]
pix/e/layers_over.svg [deleted file]
pix/e/layers_under.png [deleted file]
pix/e/layers_under.svg [deleted file]
pix/e/left_to_right.png [deleted file]
pix/e/left_to_right.svg [deleted file]
pix/e/manage_files.png [deleted file]
pix/e/manage_files.svg [deleted file]
pix/e/merge_cells.png [deleted file]
pix/e/merge_cells.svg [deleted file]
pix/e/new_document.png [deleted file]
pix/e/new_document.svg [deleted file]
pix/e/numbered_list.png [deleted file]
pix/e/numbered_list.svg [deleted file]
pix/e/page_break.png [deleted file]
pix/e/page_break.svg [deleted file]
pix/e/paste.png [deleted file]
pix/e/paste.svg [deleted file]
pix/e/paste_text.png [deleted file]
pix/e/paste_text.svg [deleted file]
pix/e/paste_word.png [deleted file]
pix/e/paste_word.svg [deleted file]
pix/e/prevent_autolink.png [deleted file]
pix/e/prevent_autolink.svg [deleted file]
pix/e/preview.png [deleted file]
pix/e/preview.svg [deleted file]
pix/e/print.png [deleted file]
pix/e/print.svg [deleted file]
pix/e/question.png [deleted file]
pix/e/question.svg [deleted file]
pix/e/redo.png [deleted file]
pix/e/redo.svg [deleted file]
pix/e/remove_link.png [deleted file]
pix/e/remove_link.svg [deleted file]
pix/e/resize.png [deleted file]
pix/e/resize.svg [deleted file]
pix/e/restore_draft.png [deleted file]
pix/e/restore_draft.svg [deleted file]
pix/e/restore_last_draft.png [deleted file]
pix/e/restore_last_draft.svg [deleted file]
pix/e/right_to_left.png [deleted file]
pix/e/right_to_left.svg [deleted file]
pix/e/row_props.png [deleted file]
pix/e/row_props.svg [deleted file]
pix/e/save.png [deleted file]
pix/e/save.svg [deleted file]
pix/e/search.png [deleted file]
pix/e/search.svg [deleted file]
pix/e/select_all.png [deleted file]
pix/e/select_all.svg [deleted file]
pix/e/show_invisible_characters.png [deleted file]
pix/e/show_invisible_characters.svg [deleted file]
pix/e/source_code.png [deleted file]
pix/e/source_code.svg [deleted file]
pix/e/special_character.png [deleted file]
pix/e/special_character.svg [deleted file]
pix/e/spellcheck.png [deleted file]
pix/e/spellcheck.svg [deleted file]
pix/e/split_cells.png [deleted file]
pix/e/split_cells.svg [deleted file]
pix/e/strikethrough.png [deleted file]
pix/e/strikethrough.svg [deleted file]
pix/e/styleprops.png [deleted file]
pix/e/styleprops.svg [deleted file]
pix/e/subscript.png [deleted file]
pix/e/subscript.svg [deleted file]
pix/e/superscript.png [deleted file]
pix/e/superscript.svg [deleted file]
pix/e/table.png [deleted file]
pix/e/table.svg [deleted file]
pix/e/table_props.png [deleted file]
pix/e/table_props.svg [deleted file]
pix/e/template.png [deleted file]
pix/e/template.svg [deleted file]
pix/e/text_color.png [deleted file]
pix/e/text_color.svg [deleted file]
pix/e/text_highlight.png [deleted file]
pix/e/text_highlight.svg [deleted file]
pix/e/tick.png [deleted file]
pix/e/tick.svg [deleted file]
pix/e/toggle_blockquote.png [deleted file]
pix/e/toggle_blockquote.svg [deleted file]
pix/e/underline.png [deleted file]
pix/e/underline.svg [deleted file]
pix/e/undo.png [deleted file]
pix/e/undo.svg [deleted file]
pix/e/visual_aid.png [deleted file]
pix/e/visual_aid.svg [deleted file]
pix/e/visual_blocks.png [deleted file]
pix/e/visual_blocks.svg [deleted file]
report/backups/index.php
repository/filepicker.js
repository/skydrive/lang/en/repository_skydrive.php
repository/upload/tests/behat/behat_repository_upload.php
tag/lib.php
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/course.css
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/style/moodle.css
theme/yui_combo.php
user/editlib.php
user/index.php
user/tests/editlib_test.php [new file with mode: 0644]
user/view.php
version.php

index 50bcfc9..ed5fb41 100644 (file)
@@ -78,6 +78,9 @@ echo '<p style="text-align: center"><input type="submit" value="' . get_string("
 echo "</div>\n";
 echo "</form>\n";
 
+$PAGE->requires->string_for_js('unmaskpassword', 'core_form');
+$PAGE->requires->yui_module('moodle-auth-passwordunmask', 'M.auth.passwordunmask');
+
 echo $OUTPUT->footer();
 exit;
 
index 0f5ac5e..324241b 100644 (file)
@@ -105,6 +105,15 @@ class tool_behat_renderer extends plugin_renderer_base {
                 $stepsdefinitions
             );
 
+            // Replace simple OR options.
+            $regex = '#\(\?P<[^>]+>([^\)|]+\|[^\)]+)\)#';
+            $stepsdefinitions = preg_replace_callback($regex,
+                function($matches){
+                    return html_writer::select(explode('|', $matches[1]), uniqid());
+                },
+                $stepsdefinitions
+            );
+
         }
 
         // Steps definitions.
index e9e5e39..4bcdf71 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100;
+$plugin->version   = 2013101700;
 $plugin->requires  = 2013050100; // Requires Moodle 2.5.
 $plugin->component = 'tool_behat';
index 456393d..78ab7e4 100644 (file)
@@ -94,7 +94,7 @@ if (empty($CFG->tool_generator_users_password) || is_bool($CFG->tool_generator_u
 }
 
 // Switch to admin user account.
-session_set_user(get_admin());
+\core\session\manager::set_user(get_admin());
 
 // Create files.
 $courseid = $DB->get_field('course', 'id', array('shortname' => $shortname));
index ff9c863..13c2989 100644 (file)
@@ -321,7 +321,7 @@ if (!ldap_paged_results_supported($config->ldap_version)) {
         <label for="bind_pw"><?php print_string('auth_ldap_bind_pw_key', 'auth_ldap') ?></label>
     </td>
     <td>
-        <input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" />
+        <input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" autocomplete="off"/>
         <?php if (isset($err['bind_pw'])) { echo $OUTPUT->error_text($err['bind_pw']); } ?>
     </td>
     <td>
index 6ae7140..9366967 100644 (file)
 <tr valign="top" class="required">
     <td align="right"><label for="pass"><?php print_string("auth_dbpass_key", "auth_db") ?></label></td>
     <td>
-        <input id="pass" name="pass" type="password" size="30" value="<?php p($config->pass)?>" />
+        <input id="pass" name="pass" type="password" size="30" value="<?php p($config->pass)?>" autocomplete="off"/>
         <?php
 
         if (isset($err["pass"])) {
index 0d0da60..6029411 100644 (file)
@@ -61,7 +61,7 @@
 <tr valign="top" class="required">
     <td align="right"><label for="passwd"><?php print_string("auth_fcpasswd_key", "auth_fc") ?>:</label></td>
     <td>
-        <input name="passwd" id="passwd" type="password" size="30" maxlength="12" value="<?php echo $config->passwd?>" />
+        <input name="passwd" id="passwd" type="password" size="30" maxlength="12" value="<?php echo $config->passwd?>" autocomplete="off"/>
     <?php  if (isset($err["passwd"])) echo $OUTPUT->error_text($err["passwd"]); ?>
     </td>
     <td>
index 0f80f7d..f92c08d 100644 (file)
@@ -1739,12 +1739,11 @@ class auth_plugin_ldap extends auth_plugin_base {
             return false;
         }
         $username   = $cf[$key];
+
         // Here we want to trigger the whole authentication machinery
         // to make sure no step is bypassed...
         $user = authenticate_user_login($username, $key);
         if ($user) {
-            add_to_log(SITEID, 'user', 'login', "view.php?id=$USER->id&course=".SITEID,
-                       $user->id, 0, $user->id);
             complete_user_login($user);
 
             // Cleanup the key to prevent reuse...
@@ -1763,7 +1762,10 @@ class auth_plugin_ldap extends auth_plugin_base {
                 $urltogo = $CFG->wwwroot.'/';
                 unset($SESSION->wantsurl);
             }
-            redirect($urltogo);
+            // We do not want to redirect if we are in a PHPUnit test.
+            if (!PHPUNIT_TEST) {
+                redirect($urltogo);
+            }
         }
         // Should never reach here.
         return false;
index 0b5de31..7c05937 100644 (file)
@@ -227,7 +227,7 @@ if (!ldap_paged_results_supported($config->ldap_version)) {
         <label for="bind_pw"><?php print_string('auth_ldap_bind_pw_key', 'auth_ldap') ?></label>
     </td>
     <td>
-        <input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" />
+        <input name="bind_pw" id="bind_pw" type="password" size="30" value="<?php echo $config->bind_pw?>" autocomplete="off"/>
         <?php if (isset($err['bind_pw'])) { echo $OUTPUT->error_text($err['bind_pw']); } ?>
     </td>
     <td>
index b468fde..296948b 100644 (file)
@@ -236,6 +236,73 @@ class auth_ldap_plugin_testcase extends advanced_testcase {
         ldap_close($connection);
     }
 
+    /**
+     * Test logging in via LDAP calls a user_loggedin event.
+     */
+    public function test_ldap_user_loggedin_event() {
+        global $CFG, $DB, $USER;
+
+        require_once($CFG->dirroot . '/auth/ldap/auth.php');
+
+        $this->resetAfterTest();
+
+        $this->setAdminUser();
+
+        $user = clone($USER);
+
+        // The USER variable no longer stores the password hash, so set it here.
+        $user->password = 'password';
+
+        // Note: we are just going to trigger the function that calls the event,
+        // not actually perform a LDAP login, for the sake of sanity.
+        $ldap = new auth_plugin_ldap();
+
+        // Set the key for the cache flag we want to set which is used by LDAP.
+        set_cache_flag($ldap->pluginconfig . '/ntlmsess', sesskey(), $user->username, AUTH_NTLMTIMEOUT);
+
+        // We are going to need to set the sesskey as the user's password in order for the LDAP log in to work.
+        update_internal_user_password($user, sesskey());
+
+        // The function ntlmsso_finish is responsible for triggering the event, so call it directly and catch the event.
+        $sink = $this->redirectEvents();
+        // We need to supress this function call, or else we will get the message "session_regenerate_id(): Cannot
+        // regenerate session id - headers already sent" as the ntlmsso_finish function calls complete_user_login
+        @$ldap->ntlmsso_finish();
+        $events = $sink->get_events();
+        $sink->close();
+
+        // Unset the password now.
+        unset($user->password);
+
+        // Get the user from the DB and set the expected variables.
+        $dbuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
+        $user->firstaccess = (int) $dbuser->firstaccess;
+        $user->lastaccess = (int) $dbuser->lastaccess;
+        $user->currentlogin = (int) $dbuser->currentlogin;
+        $user->sesskey = sesskey();
+        $user->lastcourseaccess = array();
+        $user->currentcourseaccess = array();
+        $user->groupmember = array();
+        $user->profile = array();
+        $user->preference = array(
+            '_lastloaded' => time()
+        );
+
+        // Check that the event is valid.
+        $this->assertCount(2, $events);
+        $event = $events[0];
+        $this->assertInstanceOf('\core\event\user_updated', $event);
+        $event = $events[1];
+        $this->assertInstanceOf('\core\event\user_loggedin', $event);
+        $this->assertEquals('user', $event->objecttable);
+        $this->assertEquals('2', $event->objectid);
+        $this->assertEquals(context_system::instance()->id, $event->contextid);
+        $this->assertEquals($user, $event->get_record_snapshot('user', 2));
+        $expectedlog = array(SITEID, 'user', 'login', 'view.php?id=' . $USER->id . '&course=' . SITEID, $user->id,
+            0, $user->id);
+        $this->assertEventLegacyLogData($expectedlog, $event);
+    }
+
     protected function create_ldap_user($connection, $topdn, $i) {
         $o = array();
         $o['objectClass']   = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
index 3eaf702..5256750 100644 (file)
 
         if ($shibbolethauth->user_login($frm->username, $frm->password)
                 && $user = authenticate_user_login($frm->username, $frm->password)) {
-
-            enrol_check_plugins($user);
-            \core\session\manager::set_user($user);
-
-            $USER->loggedin = true;
-            $USER->site     = $CFG->wwwroot; // for added security, store the site in the
-
-            update_user_login_times();
-
-            // Don't show previous shibboleth username on login page
-
-            set_login_session_preferences();
-
-            unset($SESSION->lang);
-            $SESSION->justloggedin = true;
-
-            add_to_log(SITEID, 'user', 'login', "view.php?id=$USER->id&course=".SITEID, $USER->id, 0, $USER->id);
+            complete_user_login($user);
 
             if (user_not_fully_set_up($USER)) {
                 $urltogo = $CFG->wwwroot.'/user/edit.php?id='.$USER->id.'&amp;course='.SITEID;
diff --git a/auth/yui/passwordunmask/passwordunmask.js b/auth/yui/passwordunmask/passwordunmask.js
new file mode 100644 (file)
index 0000000..6e85f9e
--- /dev/null
@@ -0,0 +1,42 @@
+YUI.add('moodle-auth-passwordunmask', function(Y) {
+    var PASSWORDUNMASK = function() {
+        PASSWORDUNMASK.superclass.constructor.apply(this, arguments);
+    }
+
+    Y.extend(PASSWORDUNMASK, Y.Base, {
+        // Initialize checkboxes.
+        initializer : function(params) {
+            this.add_checkboxes();
+        },
+        // Create checkboxes for all unmasking passwords.
+        add_checkboxes : function() {
+            Y.all('#authmenu input[type=password]').each(function(node) {
+                var checkboxlabel = M.util.get_string('unmaskpassword', 'core_form');
+                var elementid = node.get('id');
+                var elementname = node.get('name');
+
+                // Retain unmask div from previous implementation.
+                var unmaskdiv = Y.Node.create('<div id="'+elementid+'unmaskdiv" class="unmask"></div>');
+
+                // Add checkbox for unmasking to unmaskdiv.
+                var unmaskchb = Y.Node.create('<input id="'+elementid+'unmask" type="checkbox" name="'+elementname+'unmask">');
+                unmaskdiv.appendChild(unmaskchb);
+                //Attach event using static javascript function for unmasking password.
+                unmaskchb.on('click', function() {unmaskPassword(elementid);});
+
+                // Add label for checkbox to unmaskdiv.
+                var unmasklabel = Y.Node.create('<label for="'+elementid+'unmask">'+checkboxlabel+'</label>');
+                unmaskdiv.appendChild(unmasklabel);
+
+                // Insert unmask div in the same div as password input.
+                node.get('parentNode').insert(unmaskdiv, node.get('lastNode'));
+            });
+            return;
+        }
+    });
+
+    M.auth = M.auth || {};
+    M.auth.passwordunmask = function(params) {
+        return new PASSWORDUNMASK(params);
+    }
+}, '@VERSION@', {requires:['base', 'node']});
index 3b9a02a..63fb8a8 100644 (file)
@@ -35,7 +35,7 @@ global $LINKS_ENCODERS_CACHE;
 $LINKS_ENCODERS_CACHE = array();
 
 /**
- * Class implementing the @xml_contenttrasnformed logic to be applied in moodle2 backups
+ * Class implementing the @xml_contenttransformed logic to be applied in moodle2 backups
  *
  * TODO: Finish phpdocs
  */
@@ -61,13 +61,13 @@ class backup_xml_transformer extends xml_contenttransformer {
 
         // Array or object, debug and try our best recursively, shouldn't happen but...
         if (is_array($content)) {
-            debugging('Backup XML transformer should process arrays but plain content always', DEBUG_DEVELOPER);
+            debugging('Backup XML transformer should not process arrays but plain content only', DEBUG_DEVELOPER);
             foreach($content as $key => $plaincontent) {
                 $content[$key] = $this->process($plaincontent);
             }
             return $content;
         } else if (is_object($content)) {
-            debugging('Backup XML transformer should not process objects but plain content always', DEBUG_DEVELOPER);
+            debugging('Backup XML transformer should not process objects but plain content only', DEBUG_DEVELOPER);
             foreach((array)$content as $key => $plaincontent) {
                 $content[$key] = $this->process($plaincontent);
             }
index 9fa8a9b..eeb6322 100644 (file)
@@ -239,7 +239,8 @@ class restore_gradebook_structure_step extends restore_structure_step {
 
             $newitemid = $DB->insert_record('grade_grades', $data);
         } else {
-            debugging("Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'");
+            $message = "Mapped user id not found for user id '{$olduserid}', grade item id '{$data->itemid}'";
+            $this->log($message, backup::LOG_DEBUG);
         }
     }
 
@@ -937,7 +938,6 @@ class restore_groups_members_structure_step extends restore_structure_step {
                     // Bad luck, plugin could not restore the data, let's add normal membership.
                     groups_add_member($data->groupid, $data->userid);
                     $message = "Restore of '$data->component/$data->itemid' group membership is not supported, using standard group membership instead.";
-                    debugging($message);
                     $this->log($message, backup::LOG_WARNING);
                 }
             }
@@ -1622,7 +1622,6 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
             // Bad luck, plugin could not restore the data, let's add normal membership.
             role_assign($data->roleid, $data->userid, $data->contextid);
             $message = "Restore of '$data->component/$data->itemid' role assignments is not supported, using manual role assignments instead.";
-            debugging($message);
             $this->log($message, backup::LOG_WARNING);
         }
     }
@@ -1768,7 +1767,6 @@ class restore_enrolments_structure_step extends restore_structure_step {
             if (!enrol_is_enabled($data->enrol) or !isset($this->plugins[$data->enrol])) {
                 $this->set_mapping('enrol', $oldid, 0);
                 $message = "Enrol plugin '$data->enrol' data can not be restored because it is not enabled, use migration to manual enrolments";
-                debugging($message);
                 $this->log($message, backup::LOG_WARNING);
                 return;
             }
index 0d0155d..a1aafb8 100644 (file)
@@ -882,9 +882,11 @@ abstract class restore_dbops {
             $newcontextid = $forcenewcontextid;
         } else {
             // Get new context, must exist or this will fail
-            if (!$newcontextid = self::get_backup_ids_record($restoreid, 'context', $oldcontextid)->newitemid) {
+            $newcontextrecord = self::get_backup_ids_record($restoreid, 'context', $oldcontextid);
+            if (!$newcontextrecord || !$newcontextrecord->newitemid) {
                 throw new restore_dbops_exception('unknown_context_mapping', $oldcontextid);
             }
+            $newcontextid = $newcontextrecord->newitemid;
         }
 
         // Sometimes it's possible to have not the oldcontextids stored into backup_ids_temp->parentitemid
index 949565a..af51a7c 100644 (file)
@@ -32,11 +32,11 @@ defined('MOODLE_INTERNAL') || die();
  */
 abstract class backup_cron_automated_helper {
 
-    /** automated backups are active and ready to run */
+    /** Automated backups are active and ready to run */
     const STATE_OK = 0;
-    /** automated backups are disabled and will not be run */
+    /** Automated backups are disabled and will not be run */
     const STATE_DISABLED = 1;
-    /** automated backups are all ready running! */
+    /** Automated backups are all ready running! */
     const STATE_RUNNING = 2;
 
     /** Course automated backup completed successfully */
@@ -49,6 +49,8 @@ abstract class backup_cron_automated_helper {
     const BACKUP_STATUS_SKIPPED = 3;
     /** Course automated backup had warnings */
     const BACKUP_STATUS_WARNING = 4;
+    /** Course automated backup has yet to be run */
+    const BACKUP_STATUS_NOTYETRUN = 5;
 
     /** Run if required by the schedule set in config. Default. **/
     const RUN_ON_SCHEDULE = 0;
@@ -120,12 +122,13 @@ abstract class backup_cron_automated_helper {
 
             $rs = $DB->get_recordset('course');
             foreach ($rs as $course) {
-                $backupcourse = $DB->get_record('backup_courses', array('courseid'=>$course->id));
+                $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
                 if (!$backupcourse) {
                     $backupcourse = new stdClass;
                     $backupcourse->courseid = $course->id;
-                    $DB->insert_record('backup_courses',$backupcourse);
-                    $backupcourse = $DB->get_record('backup_courses', array('courseid'=>$course->id));
+                    $backupcourse->laststatus = self::BACKUP_STATUS_NOTYETRUN;
+                    $DB->insert_record('backup_courses', $backupcourse);
+                    $backupcourse = $DB->get_record('backup_courses', array('courseid' => $course->id));
                 }
 
                 // The last backup is considered as successful when OK or SKIPPED.
@@ -166,6 +169,7 @@ abstract class backup_cron_automated_helper {
                 // that have not been modified since previous backup.
                 if ($shouldrunnow && !$skipped && $lastbackupwassuccessful && $config->backup_auto_skip_modif_prev) {
                     // Check log if there were any modifications to the course content.
+                    $sqlwhere = "course=:courseid AND time>:time AND ".$DB->sql_like('action', ':action', false, true, true);
                     $params = array('courseid' => $course->id,
                                     'time' => $backupcourse->laststarttime,
                                     'action' => '%view%');
@@ -229,16 +233,17 @@ abstract class backup_cron_automated_helper {
             $count = backup_cron_automated_helper::get_backup_status_array();
             $haserrors = ($count[self::BACKUP_STATUS_ERROR] != 0 || $count[self::BACKUP_STATUS_UNFINISHED] != 0);
 
-            //Build the message text
-            //Summary
-            $message .= get_string('summary')."\n";
+            // Build the message text.
+            // Summary.
+            $message .= get_string('summary') . "\n";
             $message .= "==================================================\n";
-            $message .= "  ".get_string('courses').": ".array_sum($count)."\n";
-            $message .= "  ".get_string('ok').": ".$count[self::BACKUP_STATUS_OK]."\n";
-            $message .= "  ".get_string('skipped').": ".$count[self::BACKUP_STATUS_SKIPPED]."\n";
-            $message .= "  ".get_string('error').": ".$count[self::BACKUP_STATUS_ERROR]."\n";
-            $message .= "  ".get_string('unfinished').": ".$count[self::BACKUP_STATUS_UNFINISHED]."\n";
-            $message .= "  ".get_string('warning').": ".$count[self::BACKUP_STATUS_WARNING]."\n\n";
+            $message .= '  ' . get_string('courses') . '; ' . array_sum($count) . "\n";
+            $message .= '  ' . get_string('ok') . '; ' . $count[self::BACKUP_STATUS_OK] . "\n";
+            $message .= '  ' . get_string('skipped') . '; ' . $count[self::BACKUP_STATUS_SKIPPED] . "\n";
+            $message .= '  ' . get_string('error') . '; ' . $count[self::BACKUP_STATUS_ERROR] . "\n";
+            $message .= '  ' . get_string('unfinished') . '; ' . $count[self::BACKUP_STATUS_UNFINISHED] . "\n";
+            $message .= '  ' . get_string('warning') . '; ' . $count[self::BACKUP_STATUS_WARNING] . "\n";
+            $message .= '  ' . get_string('backupnotyetrun') . '; ' . $count[self::BACKUP_STATUS_NOTYETRUN]."\n\n";
 
             //Reference
             if ($haserrors) {
@@ -301,7 +306,8 @@ abstract class backup_cron_automated_helper {
             self::BACKUP_STATUS_OK => 0,
             self::BACKUP_STATUS_UNFINISHED => 0,
             self::BACKUP_STATUS_SKIPPED => 0,
-            self::BACKUP_STATUS_WARNING => 0
+            self::BACKUP_STATUS_WARNING => 0,
+            self::BACKUP_STATUS_NOTYETRUN => 0
         );
 
         $statuses = $DB->get_records_sql('SELECT DISTINCT bc.laststatus, COUNT(bc.courseid) AS statuscount FROM {backup_courses} bc GROUP BY bc.laststatus');
index 8795a68..26b9648 100644 (file)
@@ -378,7 +378,9 @@ class block_quiz_results extends block_base {
 
             // Now grab all the users from the database
             $userids = array_merge(array_keys($best), array_keys($worst));
-            $users = $DB->get_records_list('user', 'id', $userids, '', 'id, firstname, lastname, idnumber');
+            $fields = array_merge(array('id', 'idnumber'), get_all_user_name_fields());
+            $fields = implode(',', $fields);
+            $users = $DB->get_records_list('user', 'id', $userids, '', $fields);
 
             // Ready for output!
 
diff --git a/blocks/tests/behat/return_block_original_state.feature b/blocks/tests/behat/return_block_original_state.feature
new file mode 100644 (file)
index 0000000..07baab8
--- /dev/null
@@ -0,0 +1,49 @@
+@core @core_block
+Feature: The context of a block can always be returned to it's original state.
+  In order to revert actions when configuring blocks
+  As an admin
+  I need to be able to return the block to original state
+
+  @javascript
+  Scenario: Add and configure a block to display on every page and revert back
+    Given the following "courses" exists:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And I log in as "admin"
+    When I follow "Course 1"
+    And I follow "Turn editing on"
+    And I add the "Tags" block
+    Then I should see "Tags" in the "Tags" "block"
+    And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
+    And I follow "Configure Tags block"
+    And I fill the moodle form with:
+      | Display on page types | Any page |
+    And I press "Save changes"
+    And I follow "Course 1"
+    And I add a "Assignment" to section "1" and I fill the form with:
+      | Assignment name | Assignment1 |
+      | Description | Description |
+    And I follow "Assignment1"
+    And I follow "Configure Tags block"
+    And I fill the moodle form with:
+      | Display on page types | Any assignment module page |
+    And I press "Save changes"
+    And I should see "Tags" in the "Tags" "block"
+    And I follow "Course 1"
+    And "Tags" "block" should not exists
+    And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
+    And "Tags" "block" should not exists
+    And I follow "Course 1"
+    And I add a "Assignment" to section "1" and I fill the form with:
+      | Assignment name | Assignment2 |
+      | Description | Description |
+    And I follow "Assignment2"
+    And I should see "Tags" in the "Tags" "block"
+    And I follow "Configure Tags block"
+    And I fill the moodle form with:
+      | Display on page types | Any page |
+    And I press "Save changes"
+    And I follow "Course 1"
+    And I should see "Tags" in the "Tags" "block"
+    And I click on "Participants" "link" in the "//li[p/span[contains(normalize-space(string(.)), 'Current course')]]" "xpath_element"
+    And I should see "Tags" in the "Tags" "block"
index 5c06993..fadc8ad 100644 (file)
@@ -117,6 +117,7 @@ if ($action === 'delete'){
             print_error('nopermissionstodeleteentry', 'blog');
         } else {
             $entry->delete();
+            blog_rss_delete_file($userid);
             redirect($returnurl);
         }
     } else if (blog_user_can_edit_entry($entry)) {
index 59ddd23..12854d6 100644 (file)
@@ -319,3 +319,17 @@ function blog_rss_save_file($type, $id, $tagid=0, $contents='') {
     return $status;
 }
 
+/**
+ * Delete the supplied user's cached blog post RSS feed.
+ * Only user blogs are available by RSS.
+ * This doesn't call rss_delete_file() as blog RSS caching uses it's own file structure.
+ *
+ * @param int $userid
+ */
+function blog_rss_delete_file($userid) {
+    $filename = blog_rss_file_name('user', $userid);
+    if (file_exists($filename)) {
+        unlink($filename);
+    }
+}
+
index 6c7bf6e..c11141e 100644 (file)
@@ -398,7 +398,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
             } else if(isset($typesbyday[$day]['startuser'])) {
                 $class .= ' calendar_event_user';
             }
-            $cell = html_writer::link($dayhref, $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid));
+            $cell = html_writer::link($dayhref, $day, array('id' => $popupid));
         } else {
             $cell = $day;
         }
@@ -441,7 +441,7 @@ function calendar_get_mini($courses, $groups, $users, $calmonth = false, $calyea
             if(! isset($eventsbyday[$day])) {
                 $class .= ' eventnone';
                 $popupid = calendar_get_popup(true, false);
-                $cell = html_writer::link('#', $day, array('aria-controls' => $popupid.'_panel', 'id' => $popupid));
+                $cell = html_writer::link('#', $day, array('id' => $popupid));
             }
             $cell = get_accesshide($today.' ').$cell;
         }
index 8a06c3d..d9c932a 100644 (file)
Binary files a/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-debug.js and b/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-debug.js differ
index 332dafb..3722351 100644 (file)
Binary files a/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-min.js and b/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager-min.js differ
index 8a06c3d..d9c932a 100644 (file)
Binary files a/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager.js and b/calendar/yui/build/moodle-calendar-eventmanager/moodle-calendar-eventmanager.js differ
index 6e883a6..42c3ca2 100644 (file)
@@ -50,6 +50,7 @@ Y.extend(EVENT, Y.Base, {
                 width : Math.floor(constraint.get('offsetWidth')*0.9)+"px"
             });
             panel.render(td);
+            node.setAttribute('aria-controls', panel.get('id'));
             panel.get('boundingBox').addClass('calendar-event-panel');
             panel.get('boundingBox').setAttribute('aria-live', 'off');
             this.on('showevent', panel.show, panel);
index 9a0e4e6..3f85b13 100644 (file)
@@ -33,6 +33,7 @@ require_once($CFG->dirroot.'/course/lib.php');
 $action = required_param('action', PARAM_ALPHA);
 require_sesskey(); // Gotta have the sesskey.
 require_login(); // Gotta be logged in (of course).
+$PAGE->set_context(context_system::instance());
 
 // Prepare an outcome object. We always use this.
 $outcome = new stdClass;
@@ -103,6 +104,18 @@ switch ($action) {
             );
         }
         break;
+    case 'expandcategory':
+        $categoryid = required_param('categoryid', PARAM_INT);
+        $coursecat = coursecat::get($categoryid);
+        \core_course\management\helper::record_expanded_category($coursecat);
+        $outcome->outcome = true;
+        break;
+    case 'collapsecategory':
+        $categoryid = required_param('categoryid', PARAM_INT);
+        $coursecat = coursecat::get($categoryid);
+        \core_course\management\helper::record_expanded_category($coursecat, false);
+        $outcome->outcome = true;
+        break;
     case 'getsubcategorieshtml' :
         $categoryid = required_param('categoryid', PARAM_INT);
         /* @var core_course_management_renderer $renderer */
index f4848ef..8979c8c 100644 (file)
@@ -41,6 +41,13 @@ defined('MOODLE_INTERNAL') || die;
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class helper {
+
+    /**
+     * The expanded category structure if its already being loaded from the cache.
+     * @var null|array
+     */
+    protected static $expandedcategories = null;
+
     /**
      * Returns course details in an array ready to be printed.
      *
@@ -273,6 +280,14 @@ class helper {
                 'attributes' => array('class' => 'action-edit')
             );
         }
+        // Delete.
+        if ($course->can_delete()) {
+            $actions[] = array(
+                'url' => new \moodle_url('/course/delete.php', array('id' => $course->id)),
+                'icon' => new \pix_icon('t/delete', \get_string('delete')),
+                'attributes' => array('class' => 'action-delete')
+            );
+        }
         // Show/Hide.
         if ($course->can_change_visibility()) {
             $actions[] = array(
@@ -803,4 +818,87 @@ class helper {
         $params = array('path' => $path);
         return $DB->get_records_sql($sql, $params);
     }
+
+    /**
+     * Records when a category is expanded or collapsed so that when the user
+     *
+     * @param \coursecat $coursecat The category we're working with.
+     * @param bool $expanded True if the category is expanded now.
+     */
+    public static function record_expanded_category(\coursecat $coursecat, $expanded = true) {
+        // If this ever changes we are going to reset it and reload the categories as required.
+        self::$expandedcategories = null;
+        $categoryid = $coursecat->id;
+        $path = $coursecat->get_parents();
+        /* @var \cache_session $cache */
+        $cache = \cache::make('core', 'userselections');
+        $categories = $cache->get('categorymanagementexpanded');
+        if (!is_array($categories)) {
+            if (!$expanded) {
+                // No categories recorded, nothing to remove.
+                return;
+            }
+            $categories = array();
+        }
+        if ($expanded) {
+            $ref =& $categories;
+            foreach ($coursecat->get_parents() as $path) {
+                if (!isset($ref[$path]) || !is_array($ref[$path])) {
+                    $ref[$path] = array();
+                }
+                $ref =& $ref[$path];
+            }
+            if (!isset($ref[$categoryid])) {
+                $ref[$categoryid] = true;
+            }
+        } else {
+            $found = true;
+            $ref =& $categories;
+            foreach ($coursecat->get_parents() as $path) {
+                if (!isset($ref[$path])) {
+                    $found = false;
+                    break;
+                }
+                $ref =& $ref[$path];
+            }
+            if ($found) {
+                $ref[$categoryid] = null;
+                unset($ref[$categoryid]);
+            }
+        }
+        $cache->set('categorymanagementexpanded', $categories);
+    }
+
+    /**
+     * Returns the categories that should be expanded when displaying the interface.
+     *
+     * @param int|null $withpath If specified a path to require as the parent.
+     * @return \coursecat[] An array of Category ID's to expand.
+     */
+    public static function get_expanded_categories($withpath = null) {
+        if (self::$expandedcategories === null) {
+            /* @var \cache_session $cache */
+            $cache = \cache::make('core', 'userselections');
+            self::$expandedcategories = $cache->get('categorymanagementexpanded');
+            if (self::$expandedcategories === false) {
+                self::$expandedcategories = array();
+            }
+        }
+        if (empty($withpath)) {
+            return array_keys(self::$expandedcategories);
+        }
+        $parents = explode('/', trim($withpath, '/'));
+        $ref =& self::$expandedcategories;
+        foreach ($parents as $parent) {
+            if (!isset($ref[$parent])) {
+                return array();
+            }
+            $ref =& $ref[$parent];
+        }
+        if (is_array($ref)) {
+            return array_keys($ref);
+        } else {
+            return array($parent);
+        }
+    }
 }
index f656490..943727a 100644 (file)
@@ -118,7 +118,9 @@ class core_course_management_renderer extends plugin_renderer_base {
             $selectedparents[] = $category->id;
             $selectedcategory = $category->id;
         }
-        $catatlevel = array_shift($selectedparents);
+        $catatlevel = \core_course\management\helper::get_expanded_categories('');
+        $catatlevel[] = array_shift($selectedparents);
+        $catatlevel = array_unique($catatlevel);
 
         $listing = coursecat::get(0)->get_children();
 
@@ -135,7 +137,7 @@ class core_course_management_renderer extends plugin_renderer_base {
         foreach ($listing as $listitem) {
             // Render each category in the listing.
             $subcategories = array();
-            if ($listitem->id == $catatlevel) {
+            if (in_array($listitem->id, $catatlevel)) {
                 $subcategories = $listitem->get_children();
             }
             $html .= $this->category_listitem(
@@ -166,6 +168,7 @@ class core_course_management_renderer extends plugin_renderer_base {
      */
     public function category_listitem(coursecat $category, array $subcategories, $totalsubcategories,
                                       $selectedcategory = null, $selectedcategories = array()) {
+
         $isexpandable = ($totalsubcategories > 0);
         $isexpanded = (!empty($subcategories));
         $activecategory = ($selectedcategory === $category->id);
@@ -204,7 +207,7 @@ class core_course_management_renderer extends plugin_renderer_base {
 
         $html = html_writer::start_tag('li', $attributes);
         $html .= html_writer::start_div('clearfix');
-        $html .= html_writer::start_div('float-left');
+        $html .= html_writer::start_div('float-left ba-checkbox');
         $html .= html_writer::empty_tag('input', $bcatinput).'&nbsp;';
         $html .= html_writer::end_div();
         $html .= $icon;
@@ -224,9 +227,11 @@ class core_course_management_renderer extends plugin_renderer_base {
         $html .= html_writer::end_div();
         if ($isexpanded) {
             $html .= html_writer::start_tag('ul', array('class' => 'ml', 'role' => 'group'));
-            $catatlevel = array_shift($selectedcategories);
+            $catatlevel = \core_course\management\helper::get_expanded_categories($category->path);
+            $catatlevel[] = array_shift($selectedcategories);
+            $catatlevel = array_unique($catatlevel);
             foreach ($subcategories as $listitem) {
-                $childcategories = ($listitem->id == $catatlevel) ? $listitem->get_children() : array();
+                $childcategories = (in_array($listitem->id, $catatlevel)) ? $listitem->get_children() : array();
                 $html .= $this->category_listitem(
                     $listitem,
                     $childcategories,
@@ -561,7 +566,7 @@ class core_course_management_renderer extends plugin_renderer_base {
             $html .= html_writer::div($this->output->pix_icon('i/dragdrop', get_string('dndcourse')), 'float-left drag-handle');
         }
 
-        $html .= html_writer::start_div('float-left');
+        $html .= html_writer::start_div('ba-checkbox float-left');
         $html .= html_writer::empty_tag('input', $bulkcourseinput).'&nbsp;';
         $html .= html_writer::end_div();
         $html .= html_writer::link($viewcourseurl, $text, array('class' => 'float-left coursename'));
index 118647c..d279d39 100644 (file)
@@ -1055,7 +1055,8 @@ M.course_dndupload = {
     add_editing: function(elementid) {
         var node = Y.one('#' + elementid);
         YUI().use('moodle-course-coursebase', function(Y) {
-            M.course.register_new_module(node);
+            Y.log("Invoking setup_for_resource", 'debug', 'coursedndupload');
+            M.course.coursebase.invoke_function('setup_for_resource', node);
         });
         if (M.core.actionmenu && M.core.actionmenu.newDOMNode) {
             M.core.actionmenu.newDOMNode(node);
index 5edf309..229b29c 100644 (file)
@@ -157,7 +157,8 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         }
 
         $o.= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
-            'class' => 'section main clearfix'.$sectionstyle));
+            'class' => 'section main clearfix'.$sectionstyle, 'role'=>'region',
+            'aria-label'=> get_section_name($course, $section)));
 
         $leftcontent = $this->section_left_content($section, $course, $onsectionpage);
         $o.= html_writer::tag('div', $leftcontent, array('class' => 'left side'));
@@ -303,14 +304,15 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
             $classattr .= ' current';
         }
 
+        $title = get_section_name($course, $section);
         $o = '';
-        $o .= html_writer::start_tag('li', array('id' => 'section-'.$section->section, 'class' => $classattr));
+        $o .= html_writer::start_tag('li', array('id' => 'section-'.$section->section,
+            'class' => $classattr, 'role'=>'region', 'aria-label'=> $title));
 
         $o .= html_writer::tag('div', '', array('class' => 'left side'));
         $o .= html_writer::tag('div', '', array('class' => 'right side'));
         $o .= html_writer::start_tag('div', array('class' => 'content'));
 
-        $title = get_section_name($course, $section);
         if ($section->uservisible) {
             $title = html_writer::tag('a', $title,
                     array('href' => course_get_url($course, $section->section), 'class' => $linkclasses));
index 5a22de5..7463b11 100644 (file)
@@ -98,6 +98,9 @@ $PAGE->set_pagelayout('admin');
 $PAGE->set_title($title);
 $PAGE->set_heading($title);
 
+// This is a system level page that operates on other contexts.
+require_login();
+
 if (!coursecat::has_capability_on_any(array('moodle/category:manage', 'moodle/course:create'))) {
     // The user isn't able to manage any categories. Lets redirect them to the relevant course/index.php page.
     $url = new moodle_url('/course/index.php');
@@ -141,9 +144,6 @@ if (!$issearching && $category !== null) {
     }
 }
 
-// This is a system level page that operates on other contexts.
-require_login();
-
 $notificationspass = array();
 $notificationsfail = array();
 
index f42eaff..e572403 100644 (file)
@@ -499,13 +499,37 @@ class behat_course extends behat_base {
         // Adding chr(10) to save changes.
         $activity = $this->escape($activityname);
         return array(
-            new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $activity . '" activity'),
+            new Given('I open "' . $activity . '" actions menu'),
             new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $activity .'" activity'),
             new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"'),
             new Given('I wait "2" seconds')
         );
     }
 
+    /**
+     * Opens an activity actions menu if it is not already opened.
+     *
+     * @Given /^I open "(?P<activity_name_string>(?:[^"]|\\")*)" actions menu$/
+     * @throws DriverException The step is not available when Javascript is disabled
+     * @param string $activityname
+     * @return Given
+     */
+    public function i_open_actions_menu($activityname) {
+
+        if (!$this->running_javascript()) {
+            throw new DriverException('Activities actions menu not available when Javascript is disabled');
+        }
+
+        // If it is already opened we do nothing.
+        $activitynode = $this->get_activity_node($activityname);
+        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
+        if (!empty($classes['action-menu-shown'])) {
+            return;
+        }
+
+        return new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $this->escape($activityname) . '" activity');
+    }
+
     /**
      * Indents to the right the activity or resource specified by it's name. Editing mode should be on.
      *
@@ -518,7 +542,7 @@ class behat_course extends behat_base {
         $steps = array();
         $activity = $this->escape($activityname);
         if ($this->running_javascript()) {
-            $steps[] = new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $activity . '" activity');
+            $steps[] = new Given('I open "' . $activity . '" actions menu');
         }
         $steps[] = new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $activity . '" activity');
 
@@ -541,7 +565,7 @@ class behat_course extends behat_base {
         $steps = array();
         $activity = $this->escape($activityname);
         if ($this->running_javascript()) {
-            $steps[] = new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $activity . '" activity');
+            $steps[] = new Given('I open "' . $activity . '" actions menu');
         }
         $steps[] = new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $activity . '" activity');
 
@@ -599,7 +623,7 @@ class behat_course extends behat_base {
         $steps = array();
         $activity = $this->escape($activityname);
         if ($this->running_javascript()) {
-            $steps[] = new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $activity . '" activity');
+            $steps[] = new Given('I open "' . $activity . '" actions menu');
         }
         $steps[] = new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activity . '" activity');
         $steps[] = new Given('I press "' . get_string('continue') .'"');
@@ -619,7 +643,7 @@ class behat_course extends behat_base {
         $steps = array();
         $activity = $this->escape($activityname);
         if ($this->running_javascript()) {
-            $steps[] = new Given('I click on "' . get_string('actions', 'moodle') . '" "link" in the "' . $activity . '" activity');
+            $steps[] = new Given('I open "' . $activity . '" actions menu');
         }
         $steps[] = new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activity . '" activity');
         $steps[] = new Given('I press "' . get_string('continue') .'"');
@@ -1209,7 +1233,7 @@ class behat_course extends behat_base {
             throw new ExpectationException("Could not find the actions for $listingtype", $this->getSession());
         }
         $actionnode = $actionsnode->find('css', '.action-'.$action);
-        if ($actionnode === null && $this->running_javascript()) {
+        if ($this->running_javascript() && !$actionnode->isVisible()) {
             $actionsnode->find('css', 'a.toggle-display')->click();
             if ($actionnode) {
                 $actionnode = $listingnode->find('css', '.action-'.$action);
index e1873a8..e9edfda 100644 (file)
@@ -693,3 +693,109 @@ Feature: Course category management interface performs as expected
     # Redirect
     And I should see "Edit course settings"
     And I should see "Course 1"
+
+  @javascript
+  Scenario: Test AJAX expanded categories stay open.
+    Given the following "categories" exists:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+      | Cat 2 | 0 | CAT2 |
+      | Cat 1-1 | CAT1 | CAT3 |
+      | Cat 1-2 | CAT1 | CAT4 |
+      | Cat 1-1-1 | CAT3 | CAT5 |
+      | Cat 1-1-2 | CAT3 | CAT6 |
+      | Cat 2-1 | CAT2 | CAT7 |
+      | Cat 2-1-1 | CAT7 | CAT8 |
+      | Cat 2-1-1-1 | CAT8 | CAT10 |
+      | Cat 2-1-2 | CAT7 | CAT9 |
+      | Cat 2-1-2-1 | CAT9 | CAT11 |
+
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see the "Course categories" management page
+    And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I click to expand category "CAT2" in the management interface
+    # AJAX action - no redirect.
+    And I click to expand category "CAT7" in the management interface
+    # AJAX action - no redirect.
+    And I click to expand category "CAT9" in the management interface
+    # AJAX action - no redirect.
+    And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I click on "Cat 1" "link"
+    # Redirect.
+    And I should see the "Course categories and courses" management page
+    And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I click on "Re-sort subcategories" "link" in the ".category-listing-actions" "css_element"
+    And I click on "By idnumber" "link" in the ".category-listing-actions" "css_element"
+    # Redirect.
+    And I should see the "Course categories and courses" management page
+    And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element"
+
+  @javascript
+  Scenario: Test category expansion after deletion
+    Given the following "categories" exists:
+      | name | category | idnumber |
+      | Cat A (1) | 0 | CAT1 |
+      | Cat B (2) | 0 | CAT2 |
+      | Cat C (1-1) | CAT1 | CAT3 |
+      | Cat D (2-1) | CAT2 | CAT4 |
+      | Cat E (2-1-1) | CAT4 | CAT5 |
+
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see the "Course categories" management page
+    And I should see "Cat A (1)" in the "#course-category-listings ul.ml" "css_element"
+    And I should see "Cat B (2)" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat C (1-1)" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat D (2-1)" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat E (2-1-1)" in the "#course-category-listings ul.ml" "css_element"
+    And I click to expand category "CAT1" in the management interface
+    And I should see "Cat C (1-1)" in the "#course-category-listings ul.ml" "css_element"
+    # AJAX action - no redirect.
+    And I click to expand category "CAT2" in the management interface
+    And I should see "Cat D (2-1)" in the "#course-category-listings ul.ml" "css_element"
+    # AJAX action - no redirect.
+    And I click to expand category "CAT4" in the management interface
+    And I should see "Cat E (2-1-1)" in the "#course-category-listings ul.ml" "css_element"
+    # AJAX action - no redirect.
+    And I click on "delete" action for "Cat B (2)" in management category listing
+    # Redirect.
+    And I should see "Delete category: Cat B (2)"
+    And I should see "Contents of Cat B (2)"
+    And I press "Delete"
+    # Redirect
+    And I should see "Delete category: Cat B (2)"
+    And I should see "Deleted course category Cat B (2)"
+    And I press "Continue"
+    # Redirect.
+    And I should see the "Course categories and courses" management page
+    And I should see "Cat A (1)" in the "#course-category-listings ul.ml" "css_element"
+    And I should not see "Cat B (2)" in the "#course-category-listings ul.ml" "css_element"
\ No newline at end of file
index 98f41c0..2550d7f 100644 (file)
@@ -55,13 +55,13 @@ Feature: Course activity controls works as expected
     And "#section-2" "css_element" <should_see_other_sections> exists
     And I indent left "Test forum name 1" activity
     And "#section-2" "css_element" <should_see_other_sections> exists
-    And I click on "Actions" "link" in the "Test forum name 1" activity
+    And I open "Test forum name 1" actions menu
     And I click on "Update" "link" in the "Test forum name 1" activity
     And I should see "Updating Forum"
     And I should see "Display description on course page"
     And I press "Save and return to course"
     And "#section-2" "css_element" <should_see_other_sections> exists
-    And I click on "Actions" "link" in the "Test forum name 1" activity
+    And I open "Test forum name 1" actions menu
     And I click on "Hide" "link" in the "Test forum name 1" activity
     And "#section-2" "css_element" <should_see_other_sections> exists
     And I duplicate "Test forum name 2" activity editing the new copy with:
@@ -90,7 +90,6 @@ Feature: Course activity controls works as expected
       | weeks        | 1             | "1 January - 7 January" | should not                | should not                                               |
       | weeks        | 1             | "Course 1"              | should                    | should not                                               |
 
-
   Scenario Outline: General activities course controls using topics and weeks formats, and paged mode and not paged mode works as expected
     Given the following "users" exists:
       | username | firstname | lastname | email |
@@ -129,13 +128,11 @@ Feature: Course activity controls works as expected
     And "#section-2" "css_element" <should_see_other_sections> exists
     And I indent left "Test forum name 1" activity
     And "#section-2" "css_element" <should_see_other_sections> exists
-    And I click on "Actions" "link" in the "Test forum name 1" activity
     And I click on "Update" "link" in the "Test forum name 1" activity
     And I should see "Updating Forum"
     And I should see "Display description on course page"
     And I press "Save and return to course"
     And "#section-2" "css_element" <should_see_other_sections> exists
-    And I click on "Actions" "link" in the "Test forum name 1" activity
     And I click on "Hide" "link" in the "Test forum name 1" activity
     And "#section-2" "css_element" <should_see_other_sections> exists
     And I delete "Test forum name 1" activity
index d8e7d4d..0f248b7 100644 (file)
@@ -33,7 +33,39 @@ Feature: Test we can both create and delete a course.
     And I should see "Cat 1" in the "#category-listing" "css_element"
     And I should see "Test course: create a course" in the "#course-listing" "css_element"
 
-  Scenario: Delete a course
+  Scenario: Delete a course via its management listing
+    Given the following "categories" exists:
+      | name | category 0| idnumber |
+      | Cat 1 | 0 | CAT1 |
+    And the following "courses" exists:
+      | category | fullname | shortname | idnumber |
+      | CAT1 | Test course: create a course | TCCAC | TC3401 |
+      | CAT1 | Test course 2: create another course | TC2CAC | TC3402 |
+
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see the "Course categories" management page
+    And I click on category "Cat 1" in the management interface
+    # Redirect
+    And I should see the "Course categories and courses" management page
+    And I should see "Cat 1" in the "#category-listing" "css_element"
+    And I should see "Test course: create a course" in the "#course-listing" "css_element"
+    And I should see "Test course 2: create another course" in the "#course-listing" "css_element"
+    And I click on "delete" action for "Test course: create a course" in management course listing
+    # Redirect
+    And I should see "Delete TCCAC"
+    And I should see "Test course: create a course (TCCAC)"
+    And I press "Continue"
+    # Redirect
+    And I should see "Deleting TCCAC"
+    And I should see "TCCAC has been completely deleted"
+    And I press "Continue"
+    # Redirect
+    And I should see the "Course categories and courses" management page
+    And I should see "Cat 1" in the "#category-listing" "css_element"
+    And I should see "Test course 2: create another course" in the "#course-listing" "css_element"
+
+  Scenario: Delete a course via its management details page
     Given the following "categories" exists:
       | name | category 0| idnumber |
       | Cat 1 | 0 | CAT1 |
index 3da1048..af49f08 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 28a3ee9..c8c707d 100644 (file)
Binary files a/course/yui/build/moodle-course-management/moodle-course-management-min.js and b/course/yui/build/moodle-course-management/moodle-course-management-min.js differ
index ec6b8f4..02452ba 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 d7d8d4e..fbf3728 100644 (file)
@@ -27,7 +27,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
     Y.extend(DRAGSECTION, M.core.dragdrop, {
         sectionlistselector : null,
 
-        initializer : function(params) {
+        initializer : function() {
             // Set group for parent class
             this.groups = [ CSS.SECTIONDRAGGABLE ];
             this.samenodeclass = M.course.format.get_sectionwrapperclass();
@@ -129,35 +129,56 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             this.drop_hit(e);
         },
 
+        get_section_index: function(node) {
+            var sectionlistselector = '.' + CSS.COURSECONTENT + ' ' + M.course.format.get_section_selector(Y),
+                sectionList = Y.all(sectionlistselector),
+                nodeIndex = sectionList.indexOf(node),
+                zeroIndex = sectionList.indexOf(Y.one('#section-0'));
+
+            return (nodeIndex - zeroIndex);
+        },
+
         drop_hit : function(e) {
             var drag = e.drag;
-            // Get a reference to our drag node
-            var dragnode = drag.get('node');
-            var dropnode = e.drop.get('node');
-            // Prepare some variables
-            var dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode);
-            var dropnodeid = Y.Moodle.core_course.util.section.getId(dropnode);
 
-            var loopstart = dragnodeid;
-            var loopend = dropnodeid;
+            // Get references to our nodes and their IDs.
+            var dragnode = drag.get('node'),
+                dragnodeid = Y.Moodle.core_course.util.section.getId(dragnode),
+                loopstart = dragnodeid,
+
+                dropnodeindex = this.get_section_index(dragnode),
+                loopend = dropnodeindex;
+
+            if (dragnodeid === dropnodeindex) {
+                Y.log("Skipping move - same location moving " + dragnodeid + " to " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
+                return;
+            }
+
+            Y.log("Moving from position " + dragnodeid + " to position " + dropnodeindex, 'debug', 'moodle-course-dragdrop');
 
-            if (this.goingup) {
-                loopstart = dropnodeid;
+            if (loopstart > loopend) {
+                // If we're going up, we need to swap the loop order
+                // because loops can't go backwards.
+                loopstart = dropnodeindex;
                 loopend = dragnodeid;
             }
 
-            // Get the list of nodes
+            // Get the list of nodes.
             drag.get('dragNode').removeClass(CSS.COURSECONTENT);
             var sectionlist = Y.Node.all(this.sectionlistselector);
 
-            // Add lightbox if it not there
+            // Add a lightbox if it's not there.
             var lightbox = M.util.add_lightbox(Y, dragnode);
 
-            var params = {};
+            // Handle any variables which we must pass via AJAX.
+            var params = {},
+                pageparams = this.get('config').pageparams,
+                varname;
 
-            // Handle any variables which we must pass back through to
-            var pageparams = this.get('config').pageparams;
             for (varname in pageparams) {
+                if (!pageparams.hasOwnProperty(varname)) {
+                    continue;
+                }
                 params[varname] = pageparams[varname];
             }
 
@@ -167,16 +188,15 @@ YUI.add('moodle-course-dragdrop', function(Y) {
             params['class'] = 'section';
             params.field = 'move';
             params.id = dragnodeid;
-            params.value = dropnodeid;
+            params.value = dropnodeindex;
 
-            // Do AJAX request
+            // Perform the AJAX request.
             var uri = M.cfg.wwwroot + this.get('ajaxurl');
-
             Y.io(uri, {
                 method: 'POST',
                 data: params,
                 on: {
-                    start : function(tid) {
+                    start : function() {
                         lightbox.show();
                     },
                     success: function(tid, response) {
@@ -190,30 +210,40 @@ YUI.add('moodle-course-dragdrop', function(Y) {
                             M.course.format.process_sections(Y, sectionlist, responsetext, loopstart, loopend);
                         } catch (e) {}
 
+                        // Update all of the section IDs - first unset them, then set them
+                        // to avoid duplicates in the DOM.
+                        var index;
+
                         // Classic bubble sort algorithm is applied to the section
                         // nodes between original drag node location and the new one.
+                        var swapped = false;
                         do {
-                            var swapped = false;
-                            for (var i = loopstart; i <= loopend; i++) {
-                                if (Y.Moodle.core_course.util.section.getId(sectionlist.item(i-1)) > Y.Moodle.core_course.util.section.getId(sectionlist.item(i))) {
-                                    // Swap section id
-                                    var sectionid = sectionlist.item(i-1).get('id');
-                                    sectionlist.item(i-1).set('id', sectionlist.item(i).get('id'));
-                                    sectionlist.item(i).set('id', sectionid);
-                                    // See what format needs to swap
-                                    M.course.format.swap_sections(Y, i-1, i);
-                                    // Update flag
+                            swapped = false;
+                            for (index = loopstart; index <= loopend; index++) {
+                                if (Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) >
+                                            Y.Moodle.core_course.util.section.getId(sectionlist.item(index))) {
+                                    Y.log("Swapping " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index - 1)) +
+                                            " with " + Y.Moodle.core_course.util.section.getId(sectionlist.item(index)));
+                                    // Swap section id.
+                                    var sectionid = sectionlist.item(index - 1).get('id');
+                                    sectionlist.item(index - 1).set('id', sectionlist.item(index).get('id'));
+                                    sectionlist.item(index).set('id', sectionid);
+
+                                    // See what format needs to swap.
+                                    M.course.format.swap_sections(Y, index - 1, index);
+
+                                    // Update flag.
                                     swapped = true;
                                 }
                             }
                             loopend = loopend - 1;
                         } while (swapped);
 
-                        // Finally, hide the lightbox
-                        window.setTimeout(function(e) {
+                        window.setTimeout(function() {
                             lightbox.hide();
                         }, 250);
                     },
+
                     failure: function(tid, response) {
                         this.ajax_failure(response);
                         lightbox.hide();
index c8ca2fb..ea09c8c 100644 (file)
@@ -155,6 +155,7 @@ Category.prototype = {
             title : M.util.get_string('collapse', 'moodle'),
             alt : M.util.get_string('collapse', 'moodle')
         });
+        this.get('console').performAjaxAction('expandcategory', {categoryid : this.get('categoryid')}, null, this);
     },
 
     /**
@@ -170,6 +171,7 @@ Category.prototype = {
             title : M.util.get_string('expand', 'moodle'),
             alt : M.util.get_string('expand', 'moodle')
         });
+        this.get('console').performAjaxAction('collapsecategory', {categoryid : this.get('categoryid')}, null, this);
     },
 
     /**
index 5303b3a..394d63c 100644 (file)
@@ -421,6 +421,11 @@ Console.prototype = {
         args.action = action;
         args.ajax = '1';
         args.sesskey = M.cfg.sesskey;
+        if (callback === null) {
+            callback = function() {
+                Y.log("'Action '"+action+"' completed", 'debug', 'moodle-course-management');
+            };
+        }
         io.send(this.get('ajaxurl'), {
             method : 'POST',
             on : {
index 372652a..2971198 100644 (file)
@@ -204,12 +204,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
          */
         initializer : function(config) {
             M.course.coursebase.register_module(this);
-            Y.all(SELECTOR.ACTIVITYLI).each(function(activity){
-                activity.setData('toolbox', this);
-                activity.all(SELECTOR.COMMANDSPAN+ ' ' + SELECTOR.ACTIVITYACTION).each(function(){
-                    this.setData('activity', activity);
-                });
-            }, this);
             Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
         },
 
@@ -233,7 +227,8 @@ YUI.add('moodle-course-toolboxes', function(Y) {
             // From the anchor we can get both the activity (added during initialisation) and the action being
             // performed (added by the UI as a data attribute).
             var action = node.getData('action'),
-                activity = node.getData('activity');
+                activity = node.ancestor(SELECTOR.ACTIVITYLI);
+
             if (!node.test('a') || !action || !activity) {
                 // It wasn't a valid action node.
                 return;
@@ -689,7 +684,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 action = 'hide';
             }
             if (visible != shouldbevisible) {
-                this.handle_resource_dim(buttonnode, buttonnode.getData('activity'), action);
+                this.handle_resource_dim(buttonnode, element, action);
             }
         }
     }, {
@@ -800,7 +795,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 // NOTE: resourcestotoggle is returned as a string instead
                 // of a Number so we must cast our activityid to a String.
                 if (Y.Array.indexOf(response.resourcestotoggle, "" + activityid) != -1) {
-                    node.getData('toolbox').handle_resource_dim(button, node, action);
+                    M.course.resource_toolbox.handle_resource_dim(button, node, action);
                 }
             }, this);
         },
@@ -876,18 +871,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
         return new SECTIONTOOLBOX(config);
     };
 
-    M.course.register_new_module = function(module) {
-        if (typeof module === 'string') {
-            module = Y.one(module);
-        }
-        if (M.course.resource_toolbox !== null) {
-            module.setData('toolbox', M.course.resource_toolbox);
-            module.all(SELECTOR.COMMANDSPAN+ ' ' + SELECTOR.ACTIVITYACTION).each(function(){
-                this.setData('activity', module);
-            });
-        }
-    }
-
 },
 '@VERSION@', {
     requires : ['base', 'node', 'io', 'moodle-course-coursebase']
diff --git a/enrol/imsenterprise/README.txt b/enrol/imsenterprise/README.txt
deleted file mode 100644 (file)
index ff5ae8c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-
-  IMS Enterprise 1.1 file enrolment module for Moodle
-    (also reported to work with v1.01 and v1.0 data)
-
-  (c) 2005-2006 Dan Stowell
-  Released under the Gnu Public Licence (GPL)
-
-INSTALLATION
-
-Please see INSTALL.txt.
-
-
-DESCRIPTION
-
-This enrolment script will repeatedly read an XML file from a
-specified location. The XML file should conform to the IMS Enterprise
-1.1 specification, containing <person>, <group>, and <membership>
-elements to specify which students/teachers should be added/removed
-from the course. User accounts and/or Moodle courses can be created
-by the script if they aren't yet registered (this is an option which
-can be turned on/off).
-
-(The IMS 1.0 specification is significantly different from the 1.1
-spec. This code has been made flexible so it should in theory be
-able to handle IMS 1.0 as well, but I haven't directly tested it
-with v1.0 Enterprise data.
-The one restriction that may be important is that the plugin assumes
-that the <membership> elements come after the others. The 1.1 spec
-demands this, but the 1.0 spec does not make this restriction.)
-
-
-HOW USERS/COURSES ARE MATCHED AGAINST MOODLE'S DATABASE
-
-IMS Enterprise data typically contains a "sourcedid" for each person
-or group (course) record, which represents the canonical identifier
-used by the source system. This is separate from the "userid" for a
-person, which is also present in the data and should represent the
-login userid which a person is intended to use in Moodle. (In some
-systems these may have the same value.)
-
-This script uses the "sourcedid" as the lookup to determine if the
-user/course exists in the database, in both cases looking at the
-"idnumber" field. This "idnumber" is not typically displayed in
-Moodle. When creating a user, the "userid" field must not be blank,
-because it is stored as the user's Moodle login ID.
-
-
-TECHNICAL NOTE
-
-The script uses an optimised pattern-matching (regex) method for
-processing the XML, rather than any built-in XML handling. This is for
-two reasons: firstly, because some systems produce very sloppy
-(even invalid) XML and we'd like to be able to process it anyway; and
-secondly, because PHP 4 and PHP 5 handle XML differently, and we'd
-like to be independent of that changeover.
-
-
-
-FOR MORE INFO / HELP
-
-Please visit the community forums at www.moodle.org and search to see
-if any relevant help has already been posted. If not, ask away!
-
diff --git a/enrol/imsenterprise/TODO.txt b/enrol/imsenterprise/TODO.txt
deleted file mode 100644 (file)
index f7f890e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-DEVELOPMENT PRIORITIES, IN ORDER OF PRIORITY:
-
-
-
-TO DO:
-
-- Handling of references to <group>s as <member>s
-
-- Support for enrolling using multiple files? At present only one filename
-     is supported. Some systems may wish/need to do multiple. This may be tricky
-     since multiple filepaths may be too long for a single moodle config
-     variable (255 chars max)
-
-- Process group's "relationship"?
-
-- Activate <photo> handling, dependent upon acceptance of
-     modification to Moodle's gdlib.php
-
-
-
-DONE:
-
-v0.6:
-
-- Reduce processing from two passes to one pass (dependent on <membership> tags
-     coming after any person/group tags to which they refer)
-
-- The log data should NOT contain any personal information! NO STUDENT NAMES
-
-- Support for restricting according to <target>
-
-- Support for recstatus attribute on group/person/role
-
-- Processing of <timeframe> tag to add ability to specify start/end of enrolment
-
-- Write conformance summary
-
-
-
index db69d20..dcf3f7c 100644 (file)
@@ -24,6 +24,9 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+/**
+ * Post installation procedure
+ */
 function xmldb_enrol_imsenterprise_install() {
     global $CFG, $DB;
 
index 77a63ff..12c50c0 100644 (file)
  * This file keeps track of upgrades to the imsenterprise enrolment plugin
  *
  * @package    enrol_imsenterprise
- * @copyright  2011 Petr Skoda {@link http://skodak.org
+ * @copyright  2011 Petr Skoda (http://skodak.org)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 defined('MOODLE_INTERNAL') || die();
 
+/**
+ * Performs upgrade of the database structure and data
+ *
+ * @param int $oldversion the version we are upgrading from
+ * @return bool true
+ */
 function xmldb_enrol_imsenterprise_upgrade($oldversion) {
     global $CFG, $DB;
 
     $dbman = $DB->get_manager();
 
+    // Moodle v2.3.0 release upgrade line.
+    // Put any upgrade step following this.
 
-    // Moodle v2.3.0 release upgrade line
-    // Put any upgrade step following this
-
-
-    // Moodle v2.4.0 release upgrade line
-    // Put any upgrade step following this
-
+    // Moodle v2.4.0 release upgrade line.
+    // Put any upgrade step following this.
 
     // Moodle v2.5.0 release upgrade line.
     // Put any upgrade step following this.
 
-
     return true;
 }
-
-
diff --git a/enrol/imsenterprise/entv1p1_conformance_summary.html b/enrol/imsenterprise/entv1p1_conformance_summary.html
deleted file mode 100644 (file)
index 6d125e7..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
-<head>
-<title>Enterprise Conformance Summary for v1.1</title>
-<style type="text/css">
-dl {}
-dl dt { font-weight: bold; margin-top: 10px; font-size: 24pt; }
-dl dd {margin-left: 20px; padding-left: 0px;}
-dl dd dl dt {background: black; color: white; font-size: 12pt; }
-dl dd dl dd dl dt {background: white; color: black; }
-td.y {background-color: rgb(210, 255, 210);}
-td.n {background-color: rgb(255, 230, 230);}
-</style>
-</head>
-
-<body>
-<!-- Exported from Jreepad -->
-<dl>
-<dt><a name="Enterprise Conformance Summary for v1.1"></a>Enterprise Conformance Summary for v1.1</dt>
-<dd>
-  <p>This table gives a summmary of the elements that may be found in an IMS Enterprise 1.1 data file,
-     and Moodle's suppport for those elements via this enrolment plugin.</p>
-  <table border='1' cellspacing='0' cellpadding='2'>
-    <tr>
-      <td></td>
-      <td>Accept</td>
-      <td>Notes</td>
-    </tr>
-    <tr>
-      <td>properties</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>person</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;recstatus</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;sourcedid</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;userid</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;name</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;demographics</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;email</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;url</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;tel</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;adr</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;photo</td>
-      <td>Y?</td>
-      <td>This currently is possible but requires a small modification to one of Moodle's
-        library files, so for a standard simple installation, the import of user photos is
-        not supported.</td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;systemrole</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;institutionrole</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;datasource</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>group</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;recstatus</td>
-      <td class="y">Y</td>
-      <td>&quot;Delete&quot; command will hide rather than permanently delete a course.</td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;sourcedid</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;grouptype</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;description</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;org</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;timeframe</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;enrollcontrol</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;email</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;url</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;relationship</td>
-      <td class="n">N</td>
-      <td>This could be useful to implement in future, to specify also-known-as (i.e. course aliases)...</td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;datasource</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>membership</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;sourcedid</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;member</td>
-      <td class="y">Y</td>
-      <td>NB: Can only enrol &lt;person&gt;s as members - not &lt;group&gt;s (idtype=2)</td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;sourcedid</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;role</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;recstatus</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;subrole</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;status</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;userid</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;datetime</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;timeframe</td>
-      <td class="y">Y</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;imterimresult</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;finalresult</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;email</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-    <tr>
-      <td>&nbsp;&nbsp;&nbsp;&nbsp;datasource</td>
-      <td class="n">N</td>
-      <td></td>
-    </tr>
-  </table></dd>
-</dl>
-<p>Note: All the core data structures are supported.</p>
-</body>
-</html>
index 102d038..f3feda7 100644 (file)
@@ -1,11 +1,33 @@
 <?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/>.
+
+/**
+ * Import IMS Enterprise file immediately.
+ *
+ * @package    enrol_imsenterprise
+ * @copyright  2006 Dan Stowell
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 require_once(dirname(dirname(dirname(__FILE__))) . '/config.php');
 require_login(0, false);
 require_capability('moodle/site:config', context_system::instance());
 
 $site = get_site();
 
-/// get language strings
+// Get language strings.
 $PAGE->set_context(context_system::instance());
 
 $PAGE->set_url('/enrol/imsenterprise/importnow.php');
@@ -14,7 +36,8 @@ $PAGE->set_heading(get_string('importimsfile', 'enrol_imsenterprise'));
 $PAGE->navbar->add(get_string('administrationsite'));
 $PAGE->navbar->add(get_string('plugins', 'admin'));
 $PAGE->navbar->add(get_string('enrolments', 'enrol'));
-$PAGE->navbar->add(get_string('pluginname', 'enrol_imsenterprise'), new moodle_url('/admin/settings.php', array('section'=>'enrolsettingsimsenterprise')));
+$PAGE->navbar->add(get_string('pluginname', 'enrol_imsenterprise'),
+    new moodle_url('/admin/settings.php', array('section' => 'enrolsettingsimsenterprise')));
 $PAGE->navbar->add(get_string('importimsfile', 'enrol_imsenterprise'));
 $PAGE->navigation->clear_cache();
 
@@ -22,17 +45,12 @@ echo $OUTPUT->header();
 
 require_once('lib.php');
 
-//echo "Creating the IMS Enterprise enroller object\n";
 $enrol = new enrol_imsenterprise_plugin();
 
 ?>
 <p>Launching the IMS Enterprise "cron" function. The import log will appear below (giving details of any
 problems that might require attention).</p>
 <pre style="margin:10px; padding: 2px; border: 1px solid black; background-color: white; color: black;"><?php
-//error_reporting(E_ALL);
 $enrol->cron();
 ?></pre><?php
 echo $OUTPUT->footer();
-
-exit;
-?>
index e2cf641..5528e55 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-/*
-
-Note for programmers:
-
-This class uses regular expressions to mine the data file. The main reason is
-that XML handling changes from PHP 4 to PHP 5, so this should work on both.
-
-One drawback is that the pattern-matching doesn't (currently) handle XML
-namespaces - it only copes with a <group> tag if it says <group>, and not
-(for example) <ims:group>.
-
-This should also be able to handle VERY LARGE FILES - so the entire IMS file is
-NOT loaded into memory at once. It's handled line-by-line, 'forgetting' tags as
-soon as they are processed.
-
-N.B. The "sourcedid" ID code is translated to Moodle's "idnumber" field, both
-for users and for courses.
-
-*/
-
 require_once($CFG->dirroot.'/group/lib.php');
 
 
+/**
+ * IMS Enterprise file enrolment plugin.
+ *
+ * @copyright  2010 Eugene Venter
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 class enrol_imsenterprise_plugin extends enrol_plugin {
 
-var $log;
-
+    /**
+     * @var $logfp resource file pointer for writing log data to.
+     */
+    protected $logfp;
 
-/**
-* Read in an IMS Enterprise file.
-* Originally designed to handle v1.1 files but should be able to handle
-* earlier types as well, I believe.
-*
-*/
-function cron() {
-    global $CFG;
-
-    // Get configs
-    $imsfilelocation    = $this->get_config('imsfilelocation');
-    $logtolocation      = $this->get_config('logtolocation');
-    $mailadmins         = $this->get_config('mailadmins');
-    $prev_time          = $this->get_config('prev_time');
-    $prev_md5           = $this->get_config('prev_md5');
-    $prev_path          = $this->get_config('prev_path');
-
-    if (empty($imsfilelocation)) {
-        // $filename = "$CFG->dirroot/enrol/imsenterprise/example.xml";  // Default location
-        $filename = "$CFG->dataroot/1/imsenterprise-enrol.xml";  // Default location
-    } else {
-        $filename = $imsfilelocation;
-    }
+    /**
+     * @var $continueprocessing bool flag to determine if processing should continue.
+     */
+    protected $continueprocessing;
 
-    $this->logfp = false; // File pointer for writing log data to
-    if(!empty($logtolocation)) {
-        $this->logfp = fopen($logtolocation, 'a');
-    }
+    /**
+     * @var $xmlcache string cache of xml lines.
+     */
+    protected $xmlcache;
 
-    $fileisnew = false;
-    if ( file_exists($filename) ) {
-        @set_time_limit(0);
-        $starttime = time();
-
-        $this->log_line('----------------------------------------------------------------------');
-        $this->log_line("IMS Enterprise enrol cron process launched at " . userdate(time()));
-        $this->log_line('Found file '.$filename);
-        $this->xmlcache = '';
-
-        // Make sure we understand how to map the IMS-E roles to Moodle roles
-        $this->load_role_mappings();
-        // Make sure we understand how to map the IMS-E course names to Moodle course names.
-        $this->load_course_mappings();
-
-        $md5 = md5_file($filename); // NB We'll write this value back to the database at the end of the cron
-        $filemtime = filemtime($filename);
-
-        // Decide if we want to process the file (based on filepath, modification time, and MD5 hash)
-        // This is so we avoid wasting the server's efforts processing a file unnecessarily
-        if(empty($prev_path)  || ($filename != $prev_path)) {
-            $fileisnew = true;
-        } elseif(isset($prev_time) && ($filemtime <= $prev_time)) {
-            $this->log_line('File modification time is not more recent than last update - skipping processing.');
-        } elseif(isset($prev_md5) && ($md5 == $prev_md5)) {
-            $this->log_line('File MD5 hash is same as on last update - skipping processing.');
-        } else {
-            $fileisnew = true; // Let's process it!
-        }
-
-        if($fileisnew) {
-
-            $listoftags = array('group', 'person', 'member', 'membership', 'comments', 'properties'); // The list of tags which should trigger action (even if only cache trimming)
-            $this->continueprocessing = true; // The <properties> tag is allowed to halt processing if we're demanding a matching target
-
-            // FIRST PASS: Run through the file and process the group/person entries
-            if (($fh = fopen($filename, "r")) != false) {
-
-                $line = 0;
-                while ((!feof($fh)) && $this->continueprocessing) {
-
-                    $line++;
-                    $curline = fgets($fh);
-                    $this->xmlcache .= $curline; // Add a line onto the XML cache
-
-                    while (true) {
-                      // If we've got a full tag (i.e. the most recent line has closed the tag) then process-it-and-forget-it.
-                      // Must always make sure to remove tags from cache so they don't clog up our memory
-                      if($tagcontents = $this->full_tag_found_in_cache('group', $curline)) {
-                          $this->process_group_tag($tagcontents);
-                          $this->remove_tag_from_cache('group');
-                      } elseif($tagcontents = $this->full_tag_found_in_cache('person', $curline)) {
-                          $this->process_person_tag($tagcontents);
-                          $this->remove_tag_from_cache('person');
-                      } elseif($tagcontents = $this->full_tag_found_in_cache('membership', $curline)) {
-                          $this->process_membership_tag($tagcontents);
-                          $this->remove_tag_from_cache('membership');
-                      } elseif($tagcontents = $this->full_tag_found_in_cache('comments', $curline)) {
-                          $this->remove_tag_from_cache('comments');
-                      } elseif($tagcontents = $this->full_tag_found_in_cache('properties', $curline)) {
-                          $this->process_properties_tag($tagcontents);
-                          $this->remove_tag_from_cache('properties');
-                      } else {
-                          break;
-                      }
-                    } // End of while-tags-are-detected
-                } // end of while loop
-                fclose($fh);
-                fix_course_sortorder();
-            } // end of if(file_open) for first pass
-
-            /*
-
-
-            SECOND PASS REMOVED
-            Since the IMS specification v1.1 insists that "memberships" should come last,
-            and since vendors seem to have done this anyway (even with 1.0),
-            we can sensibly perform the import in one fell swoop.
-
-
-            // SECOND PASS: Now go through the file and process the membership entries
-            $this->xmlcache = '';
-            if (($fh = fopen($filename, "r")) != false) {
-                $line = 0;
-                while ((!feof($fh)) && $this->continueprocessing) {
-                    $line++;
-                    $curline = fgets($fh);
-                    $this->xmlcache .= $curline; // Add a line onto the XML cache
-
-                    while(true){
-                  // Must always make sure to remove tags from cache so they don't clog up our memory
-                  if($tagcontents = $this->full_tag_found_in_cache('group', $curline)){
-                          $this->remove_tag_from_cache('group');
-                      }elseif($tagcontents = $this->full_tag_found_in_cache('person', $curline)){
-                          $this->remove_tag_from_cache('person');
-                      }elseif($tagcontents = $this->full_tag_found_in_cache('membership', $curline)){
-                          $this->process_membership_tag($tagcontents);
-                          $this->remove_tag_from_cache('membership');
-                      }elseif($tagcontents = $this->full_tag_found_in_cache('comments', $curline)){
-                          $this->remove_tag_from_cache('comments');
-                      }elseif($tagcontents = $this->full_tag_found_in_cache('properties', $curline)){
-                          $this->remove_tag_from_cache('properties');
-                      }else{
-                    break;
-                  }
-                }
-                } // end of while loop
-                fclose($fh);
-            } // end of if(file_open) for second pass
+    /**
+     * @var $coursemappings array of mappings between IMS data fields and moodle course fields.
+     */
+    protected $coursemappings;
 
+    /**
+     * @var $rolemappings array of mappings between IMS roles and moodle roles.
+     */
+    protected $rolemappings;
 
-           */
+    /**
+     * Read in an IMS Enterprise file.
+     * Originally designed to handle v1.1 files but should be able to handle
+     * earlier types as well, I believe.
+     *
+     */
+    public function cron() {
+        global $CFG;
 
-            $timeelapsed = time() - $starttime;
-            $this->log_line('Process has completed. Time taken: '.$timeelapsed.' seconds.');
+        // Get configs.
+        $imsfilelocation = $this->get_config('imsfilelocation');
+        $logtolocation = $this->get_config('logtolocation');
+        $mailadmins = $this->get_config('mailadmins');
+        $prevtime = $this->get_config('prev_time');
+        $prevmd5 = $this->get_config('prev_md5');
+        $prevpath = $this->get_config('prev_path');
 
+        if (empty($imsfilelocation)) {
+            $filename = "$CFG->dataroot/1/imsenterprise-enrol.xml";  // Default location.
+        } else {
+            $filename = $imsfilelocation;
+        }
 
-        } // END of "if file is new"
+        $this->logfp = false;
+        if (!empty($logtolocation)) {
+            $this->logfp = fopen($logtolocation, 'a');
+        }
 
+        $fileisnew = false;
+        if ( file_exists($filename) ) {
+            @set_time_limit(0);
+            $starttime = time();
 
-        // These variables are stored so we can compare them against the IMS file, next time round.
-        $this->set_config('prev_time', $filemtime);
-        $this->set_config('prev_md5',  $md5);
-        $this->set_config('prev_path', $filename);
+            $this->log_line('----------------------------------------------------------------------');
+            $this->log_line("IMS Enterprise enrol cron process launched at " . userdate(time()));
+            $this->log_line('Found file '.$filename);
+            $this->xmlcache = '';
 
+            // Make sure we understand how to map the IMS-E roles to Moodle roles.
+            $this->load_role_mappings();
+            // Make sure we understand how to map the IMS-E course names to Moodle course names.
+            $this->load_course_mappings();
+
+            $md5 = md5_file($filename); // NB We'll write this value back to the database at the end of the cron.
+            $filemtime = filemtime($filename);
+
+            // Decide if we want to process the file (based on filepath, modification time, and MD5 hash)
+            // This is so we avoid wasting the server's efforts processing a file unnecessarily.
+            if (empty($prevpath)  || ($filename != $prevpath)) {
+                $fileisnew = true;
+            } else if (isset($prevtime) && ($filemtime <= $prevtime)) {
+                $this->log_line('File modification time is not more recent than last update - skipping processing.');
+            } else if (isset($prevmd5) && ($md5 == $prevmd5)) {
+                $this->log_line('File MD5 hash is same as on last update - skipping processing.');
+            } else {
+                $fileisnew = true; // Let's process it!
+            }
 
+            if ($fileisnew) {
+
+                // The <properties> tag is allowed to halt processing if we're demanding a matching target.
+                $this->continueprocessing = true;
+
+                // Run through the file and process the group/person entries.
+                if (($fh = fopen($filename, "r")) != false) {
+
+                    $line = 0;
+                    while ((!feof($fh)) && $this->continueprocessing) {
+
+                        $line++;
+                        $curline = fgets($fh);
+                        $this->xmlcache .= $curline; // Add a line onto the XML cache.
+
+                        while (true) {
+                            // If we've got a full tag (i.e. the most recent line has closed the tag) then process-it-and-forget-it.
+                            // Must always make sure to remove tags from cache so they don't clog up our memory.
+                            if ($tagcontents = $this->full_tag_found_in_cache('group', $curline)) {
+                                $this->process_group_tag($tagcontents);
+                                $this->remove_tag_from_cache('group');
+                            } else if ($tagcontents = $this->full_tag_found_in_cache('person', $curline)) {
+                                $this->process_person_tag($tagcontents);
+                                $this->remove_tag_from_cache('person');
+                            } else if ($tagcontents = $this->full_tag_found_in_cache('membership', $curline)) {
+                                $this->process_membership_tag($tagcontents);
+                                $this->remove_tag_from_cache('membership');
+                            } else if ($tagcontents = $this->full_tag_found_in_cache('comments', $curline)) {
+                                $this->remove_tag_from_cache('comments');
+                            } else if ($tagcontents = $this->full_tag_found_in_cache('properties', $curline)) {
+                                $this->process_properties_tag($tagcontents);
+                                $this->remove_tag_from_cache('properties');
+                            } else {
+                                break;
+                            }
+                        }
+                    }
+                    fclose($fh);
+                    fix_course_sortorder();
+                }
 
-    }else{ // end of if(file_exists)
-        $this->log_line('File not found: '.$filename);
-    }
+                $timeelapsed = time() - $starttime;
+                $this->log_line('Process has completed. Time taken: '.$timeelapsed.' seconds.');
 
-    if (!empty($mailadmins) && $fileisnew) {
-        $msg = "An IMS enrolment has been carried out within Moodle.\nTime taken: $timeelapsed seconds.\n\n";
-        if(!empty($logtolocation)){
-            if($this->logfp){
-                $msg .= "Log data has been written to:\n";
-                $msg .= "$logtolocation\n";
-                $msg .= "(Log file size: ".ceil(filesize($logtolocation)/1024)."Kb)\n\n";
-            }else{
-                $msg .= "The log file appears not to have been successfully written.\nCheck that the file is writeable by the server:\n";
-                $msg .= "$logtolocation\n\n";
             }
-        }else{
-            $msg .= "Logging is currently not active.";
-        }
-
-        $eventdata = new stdClass();
-        $eventdata->modulename        = 'moodle';
-        $eventdata->component         = 'enrol_imsenterprise';
-        $eventdata->name              = 'imsenterprise_enrolment';
-        $eventdata->userfrom          = get_admin();
-        $eventdata->userto            = get_admin();
-        $eventdata->subject           = "Moodle IMS Enterprise enrolment notification";
-        $eventdata->fullmessage       = $msg;
-        $eventdata->fullmessageformat = FORMAT_PLAIN;
-        $eventdata->fullmessagehtml   = '';
-        $eventdata->smallmessage      = '';
-        message_send($eventdata);
-
-        $this->log_line('Notification email sent to administrator.');
 
-    }
+            // These variables are stored so we can compare them against the IMS file, next time round.
+            $this->set_config('prev_time', $filemtime);
+            $this->set_config('prev_md5',  $md5);
+            $this->set_config('prev_path', $filename);
 
-    if($this->logfp){
-      fclose($this->logfp);
-    }
+        } else {
+            $this->log_line('File not found: '.$filename);
+        }
 
+        if (!empty($mailadmins) && $fileisnew) {
+            $timeelapsed = isset($timeelapsed) ? $timeelapsed : 0;
+            $msg = "An IMS enrolment has been carried out within Moodle.\nTime taken: $timeelapsed seconds.\n\n";
+            if (!empty($logtolocation)) {
+                if ($this->logfp) {
+                    $msg .= "Log data has been written to:\n";
+                    $msg .= "$logtolocation\n";
+                    $msg .= "(Log file size: ".ceil(filesize($logtolocation) / 1024)."Kb)\n\n";
+                } else {
+                    $msg .= "The log file appears not to have been successfully written.\n";
+                    $msg .= "Check that the file is writeable by the server:\n";
+                    $msg .= "$logtolocation\n\n";
+                }
+            } else {
+                $msg .= "Logging is currently not active.";
+            }
 
-} // end of cron() function
+            $eventdata = new stdClass();
+            $eventdata->modulename        = 'moodle';
+            $eventdata->component         = 'enrol_imsenterprise';
+            $eventdata->name              = 'imsenterprise_enrolment';
+            $eventdata->userfrom          = get_admin();
+            $eventdata->userto            = get_admin();
+            $eventdata->subject           = "Moodle IMS Enterprise enrolment notification";
+            $eventdata->fullmessage       = $msg;
+            $eventdata->fullmessageformat = FORMAT_PLAIN;
+            $eventdata->fullmessagehtml   = '';
+            $eventdata->smallmessage      = '';
+            message_send($eventdata);
+
+            $this->log_line('Notification email sent to administrator.');
 
-/**
-* Check if a complete tag is found in the cached data, which usually happens
-* when the end of the tag has only just been loaded into the cache.
-* Returns either false, or the contents of the tag (including start and end).
-* @param string $tagname Name of tag to look for
-* @param string $latestline The very last line in the cache (used for speeding up the match)
-*/
-function full_tag_found_in_cache($tagname, $latestline){ // Return entire element if found. Otherwise return false.
-    if(strpos(strtolower($latestline), '</'.strtolower($tagname).'>')===false){
-        return false;
-    }elseif(preg_match('{(<'.$tagname.'\b.*?>.*?</'.$tagname.'>)}is', $this->xmlcache, $matches)){
-        return $matches[1];
-    }else return false;
-}
+        }
 
-/**
-* Remove complete tag from the cached data (including all its contents) - so
-* that the cache doesn't grow to unmanageable size
-* @param string $tagname Name of tag to look for
-*/
-function remove_tag_from_cache($tagname){ // Trim the cache so we're not in danger of running out of memory.
-    ///echo "<p>remove_tag_from_cache: $tagname</p>";  flush();  ob_flush();
-    //  echo "<p>remove_tag_from_cache:<br />".htmlspecialchars($this->xmlcache);
-    $this->xmlcache = trim(preg_replace('{<'.$tagname.'\b.*?>.*?</'.$tagname.'>}is', '', $this->xmlcache, 1)); // "1" so that we replace only the FIRST instance
-    //  echo "<br />".htmlspecialchars($this->xmlcache)."</p>";
-}
+        if ($this->logfp) {
+            fclose($this->logfp);
+        }
 
-/**
-* Very simple convenience function to return the "recstatus" found in person/group/role tags.
-* 1=Add, 2=Update, 3=Delete, as specified by IMS, and we also use 0 to indicate "unspecified".
-* @param string $tagdata the tag XML data
-* @param string $tagname the name of the tag we're interested in
-*/
-function get_recstatus($tagdata, $tagname){
-    if(preg_match('{<'.$tagname.'\b[^>]*recstatus\s*=\s*["\'](\d)["\']}is', $tagdata, $matches)){
-        // echo "<p>get_recstatus($tagname) found status of $matches[1]</p>";
-        return intval($matches[1]);
-    }else{
-        // echo "<p>get_recstatus($tagname) found nothing</p>";
-        return 0; // Unspecified
     }
-}
 
-/**
-* Process the group tag. This defines a Moodle course.
-* @param string $tagconents The raw contents of the XML element
-*/
-function process_group_tag($tagcontents) {
-    global $DB;
-
-    // Get configs
-    $truncatecoursecodes    = $this->get_config('truncatecoursecodes');
-    $createnewcourses       = $this->get_config('createnewcourses');
-    $createnewcategories    = $this->get_config('createnewcategories');
-
-    // Process tag contents
-    $group = new stdClass();
-    if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
-        $group->coursecode = trim($matches[1]);
+    /**
+     * Check if a complete tag is found in the cached data, which usually happens
+     * when the end of the tag has only just been loaded into the cache.
+     *
+     * @param string $tagname Name of tag to look for
+     * @param string $latestline The very last line in the cache (used for speeding up the match)
+     * @return bool|string false, or the contents of the tag (including start and end).
+     */
+    protected function full_tag_found_in_cache($tagname, $latestline) {
+        // Return entire element if found. Otherwise return false.
+        if (strpos(strtolower($latestline), '</'.strtolower($tagname).'>') === false) {
+            return false;
+        } else if (preg_match('{(<'.$tagname.'\b.*?>.*?</'.$tagname.'>)}is', $this->xmlcache, $matches)) {
+            return $matches[1];
+        } else {
+            return false;
+        }
     }
 
-    if (preg_match('{<description>.*?<long>(.*?)</long>.*?</description>}is', $tagcontents, $matches)) {
-        $group->long = trim($matches[1]);
-    }
-    if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
-        $group->short = trim($matches[1]);
-    }
-    if (preg_match('{<description>.*?<full>(.*?)</full>.*?</description>}is', $tagcontents, $matches)) {
-        $group->full = trim($matches[1]);
+    /**
+     * Remove complete tag from the cached data (including all its contents) - so
+     * that the cache doesn't grow to unmanageable size
+     *
+     * @param string $tagname Name of tag to look for
+     */
+    protected function remove_tag_from_cache($tagname) {
+        // Trim the cache so we're not in danger of running out of memory.
+        // "1" so that we replace only the FIRST instance.
+        $this->xmlcache = trim(preg_replace('{<'.$tagname.'\b.*?>.*?</'.$tagname.'>}is', '', $this->xmlcache, 1));
     }
 
-    if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
-        $group->category = trim($matches[1]);
+    /**
+     * Very simple convenience function to return the "recstatus" found in person/group/role tags.
+     * 1=Add, 2=Update, 3=Delete, as specified by IMS, and we also use 0 to indicate "unspecified".
+     *
+     * @param string $tagdata the tag XML data
+     * @param string $tagname the name of the tag we're interested in
+     * @return int recstatus value
+     */
+    protected static function get_recstatus($tagdata, $tagname) {
+        if (preg_match('{<'.$tagname.'\b[^>]*recstatus\s*=\s*["\'](\d)["\']}is', $tagdata, $matches)) {
+            return intval($matches[1]);
+        } else {
+            return 0; // Unspecified.
+        }
     }
 
-    $recstatus = ($this->get_recstatus($tagcontents, 'group'));
-    //echo "<p>get_recstatus for this group returned $recstatus</p>";
-
-    if (!(strlen($group->coursecode)>0)) {
-        $this->log_line('Error at line '.$line.': Unable to find course code in \'group\' element.');
-    } else {
-        // First, truncate the course code if desired
-        if (intval($truncatecoursecodes)>0) {
-            $group->coursecode = ($truncatecoursecodes > 0)
-                     ? substr($group->coursecode, 0, intval($truncatecoursecodes))
-                     : $group->coursecode;
+    /**
+     * Process the group tag. This defines a Moodle course.
+     *
+     * @param string $tagcontents The raw contents of the XML element
+     */
+    protected function process_group_tag($tagcontents) {
+        global $DB;
+
+        // Get configs.
+        $truncatecoursecodes    = $this->get_config('truncatecoursecodes');
+        $createnewcourses       = $this->get_config('createnewcourses');
+        $createnewcategories    = $this->get_config('createnewcategories');
+
+        // Process tag contents.
+        $group = new stdClass();
+        if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
+            $group->coursecode = trim($matches[1]);
         }
 
-        /* -----------Course aliasing is DEACTIVATED until a more general method is in place---------------
+        if (preg_match('{<description>.*?<long>(.*?)</long>.*?</description>}is', $tagcontents, $matches)) {
+            $group->long = trim($matches[1]);
+        }
+        if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
+            $group->short = trim($matches[1]);
+        }
+        if (preg_match('{<description>.*?<full>(.*?)</full>.*?</description>}is', $tagcontents, $matches)) {
+            $group->full = trim($matches[1]);
+        }
 
-       // Second, look in the course alias table to see if the code should be translated to something else
-        if($aliases = $DB->get_field('enrol_coursealias', 'toids', array('fromid'=>$group->coursecode))){
-            $this->log_line("Found alias of course code: Translated $group->coursecode to $aliases");
-            // Alias is allowed to be a comma-separated list, so let's split it
-            $group->coursecode = explode(',', $aliases);
+        if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
+            $group->category = trim($matches[1]);
         }
-       */
 
-        // For compatibility with the (currently inactive) course aliasing, we need this to be an array
-        $group->coursecode = array($group->coursecode);
+        $recstatus = ($this->get_recstatus($tagcontents, 'group'));
 
-        // Third, check if the course(s) exist
-        foreach ($group->coursecode as $coursecode) {
-            $coursecode = trim($coursecode);
-            if (!$DB->get_field('course', 'id', array('idnumber'=>$coursecode))) {
-                if (!$createnewcourses) {
-                    $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
-                } else {
+        if (empty($group->coursecode)) {
+            $this->log_line('Error: Unable to find course code in \'group\' element.');
+        } else {
+            // First, truncate the course code if desired.
+            if (intval($truncatecoursecodes) > 0) {
+                $group->coursecode = ($truncatecoursecodes > 0)
+                    ? substr($group->coursecode, 0, intval($truncatecoursecodes))
+                    : $group->coursecode;
+            }
 
-                    // Create the (hidden) course(s) if not found
-                    $courseconfig = get_config('moodlecourse'); // Load Moodle Course shell defaults
+            // For compatibility with the (currently inactive) course aliasing, we need this to be an array.
+            $group->coursecode = array($group->coursecode);
 
-                    // New course.
-                    $course = new stdClass();
-                    foreach ($this->coursemappings as $courseattr => $imsname) {
+            // Third, check if the course(s) exist.
+            foreach ($group->coursecode as $coursecode) {
+                $coursecode = trim($coursecode);
+                if (!$DB->get_field('course', 'id', array('idnumber' => $coursecode))) {
+                    if (!$createnewcourses) {
+                        $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
+                    } else {
 
-                        if ($imsname == 'ignore') {
-                            continue;
-                        }
+                        // Create the (hidden) course(s) if not found
+                        $courseconfig = get_config('moodlecourse'); // Load Moodle Course shell defaults.
 
-                        // Check if the IMS file contains the mapped tag, otherwise fallback on coursecode.
-                        if ($imsname == 'coursecode') {
-                            $course->{$courseattr} = $coursecode;
-                        } else if (!empty($group->{$imsname})) {
-                            $course->{$courseattr} = $group->{$imsname};
-                        } else {
-                            $this->log_line('No ' . $imsname . ' description tag found for ' . $coursecode . ' coursecode, using ' . $coursecode . ' instead');
-                            $course->{$courseattr} = $coursecode;
+                        // New course.
+                        $course = new stdClass();
+                        foreach ($this->coursemappings as $courseattr => $imsname) {
+
+                            if ($imsname == 'ignore') {
+                                continue;
+                            }
+
+                            // Check if the IMS file contains the mapped tag, otherwise fallback on coursecode.
+                            if ($imsname == 'coursecode') {
+                                $course->{$courseattr} = $coursecode;
+                            } else if (!empty($group->{$imsname})) {
+                                $course->{$courseattr} = $group->{$imsname};
+                            } else {
+                                $this->log_line('No ' . $imsname . ' description tag found for '
+                                    .$coursecode . ' coursecode, using ' . $coursecode . ' instead');
+                                $course->{$courseattr} = $coursecode;
+                            }
                         }
-                    }
 
-                    $course->idnumber = $coursecode;
-                    $course->format = $courseconfig->format;
-                    $course->visible = $courseconfig->visible;
-                    $course->newsitems = $courseconfig->newsitems;
-                    $course->showgrades = $courseconfig->showgrades;
-                    $course->showreports = $courseconfig->showreports;
-                    $course->maxbytes = $courseconfig->maxbytes;
-                    $course->groupmode = $courseconfig->groupmode;
-                    $course->groupmodeforce = $courseconfig->groupmodeforce;
-                    $course->enablecompletion = $courseconfig->enablecompletion;
-                    // Insert default names for teachers/students, from the current language
-
-                    // Handle course categorisation (taken from the group.org.orgunit field if present)
-                    if (!empty($group->category)) {
-                        // If the category is defined and exists in Moodle, we want to store it in that one
-                        if ($catid = $DB->get_field('course_categories', 'id', array('name'=>$group->category))) {
-                            $course->category = $catid;
-                        } else if ($createnewcategories) {
-                            // Else if we're allowed to create new categories, let's create this one
-                            $newcat = new stdClass();
-                            $newcat->name = $group->category;
-                            $newcat->visible = 0;
-                            $catid = $DB->insert_record('course_categories', $newcat);
-                            $course->category = $catid;
-                            $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
+                        $course->idnumber = $coursecode;
+                        $course->format = $courseconfig->format;
+                        $course->visible = $courseconfig->visible;
+                        $course->newsitems = $courseconfig->newsitems;
+                        $course->showgrades = $courseconfig->showgrades;
+                        $course->showreports = $courseconfig->showreports;
+                        $course->maxbytes = $courseconfig->maxbytes;
+                        $course->groupmode = $courseconfig->groupmode;
+                        $course->groupmodeforce = $courseconfig->groupmodeforce;
+                        $course->enablecompletion = $courseconfig->enablecompletion;
+                        // Insert default names for teachers/students, from the current language.
+
+                        // Handle course categorisation (taken from the group.org.orgunit field if present).
+                        if (!empty($group->category)) {
+                            // If the category is defined and exists in Moodle, we want to store it in that one.
+                            if ($catid = $DB->get_field('course_categories', 'id', array('name' => $group->category))) {
+                                $course->category = $catid;
+                            } else if ($createnewcategories) {
+                                // Else if we're allowed to create new categories, let's create this one.
+                                $newcat = new stdClass();
+                                $newcat->name = $group->category;
+                                $newcat->visible = 0;
+                                $catid = $DB->insert_record('course_categories', $newcat);
+                                $course->category = $catid;
+                                $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
+                            } else {
+                                // If not found and not allowed to create, stick with default.
+                                $this->log_line('Category '.$group->category.' not found in Moodle database, so using '.
+                                    'default category instead.');
+                                $course->category = $this->get_default_category_id();
+                            }
                         } else {
-                            // If not found and not allowed to create, stick with default
-                            $this->log_line('Category '.$group->category.' not found in Moodle database, so using default category instead.');
                             $course->category = $this->get_default_category_id();
                         }
-                    } else {
-                        $course->category = $this->get_default_category_id();
-                    }
-                    $course->timecreated = time();
-                    $course->startdate = time();
-                    // Choose a sort order that puts us at the start of the list!
-                    $course->sortorder = 0;
-                    $courseid = $DB->insert_record('course', $course);
+                        $course->timecreated = time();
+                        $course->startdate = time();
+                        // Choose a sort order that puts us at the start of the list!
+                        $course->sortorder = 0;
+                        $courseid = $DB->insert_record('course', $course);
 
-                    // Setup default enrolment plugins
-                    $course->id = $courseid;
-                    enrol_course_updated(true, $course, null);
+                        // Setup default enrolment plugins.
+                        $course->id = $courseid;
+                        enrol_course_updated(true, $course, null);
 
-                    // Setup the blocks
-                    $course = $DB->get_record('course', array('id' => $courseid));
-                    blocks_add_default_course_blocks($course);
+                        // Setup the blocks.
+                        $course = $DB->get_record('course', array('id' => $courseid));
+                        blocks_add_default_course_blocks($course);
 
-                    // Create default 0-section
-                    course_create_sections_if_missing($course, 0);
+                        // Create default 0-section.
+                        course_create_sections_if_missing($course, 0);
 
-                    add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
+                        add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
 
-                    $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
+                        $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
+                    }
+                } else if ($recstatus == 3 && ($courseid = $DB->get_field('course', 'id', array('idnumber' => $coursecode)))) {
+                    // If course does exist, but recstatus==3 (delete), then set the course as hidden.
+                    $DB->set_field('course', 'visible', '0', array('id' => $courseid));
                 }
-            } else if ($recstatus==3 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))) {
-                // If course does exist, but recstatus==3 (delete), then set the course as hidden
-                $DB->set_field('course', 'visible', '0', array('id'=>$courseid));
             }
-        } // End of foreach(coursecode)
-    }
-} // End process_group_tag()
-
-/**
-* Process the person tag. This defines a Moodle user.
-* @param string $tagconents The raw contents of the XML element
-*/
-function process_person_tag($tagcontents){
-    global $CFG, $DB;
-
-    // Get plugin configs
-    $imssourcedidfallback   = $this->get_config('imssourcedidfallback');
-    $fixcaseusernames       = $this->get_config('fixcaseusernames');
-    $fixcasepersonalnames   = $this->get_config('fixcasepersonalnames');
-    $imsdeleteusers         = $this->get_config('imsdeleteusers');
-    $createnewusers         = $this->get_config('createnewusers');
-
-    $person = new stdClass();
-    if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)){
-        $person->idnumber = trim($matches[1]);
-    }
-    if(preg_match('{<name>.*?<n>.*?<given>(.+?)</given>.*?</n>.*?</name>}is', $tagcontents, $matches)){
-        $person->firstname = trim($matches[1]);
-    }
-    if(preg_match('{<name>.*?<n>.*?<family>(.+?)</family>.*?</n>.*?</name>}is', $tagcontents, $matches)){
-        $person->lastname = trim($matches[1]);
-    }
-    if(preg_match('{<userid>(.*?)</userid>}is', $tagcontents, $matches)){
-        $person->username = trim($matches[1]);
-    }
-    if($imssourcedidfallback && trim($person->username)==''){
-      // This is the point where we can fall back to useing the "sourcedid" if "userid" is not supplied
-      // NB We don't use an "elseif" because the tag may be supplied-but-empty
-        $person->username = $person->idnumber;
-    }
-    if(preg_match('{<email>(.*?)</email>}is', $tagcontents, $matches)){
-        $person->email = trim($matches[1]);
-    }
-    if(preg_match('{<url>(.*?)</url>}is', $tagcontents, $matches)){
-        $person->url = trim($matches[1]);
-    }
-    if(preg_match('{<adr>.*?<locality>(.+?)</locality>.*?</adr>}is', $tagcontents, $matches)){
-        $person->city = trim($matches[1]);
-    }
-    if(preg_match('{<adr>.*?<country>(.+?)</country>.*?</adr>}is', $tagcontents, $matches)){
-        $person->country = trim($matches[1]);
+        }
     }
 
-    // Fix case of some of the fields if required
-    if($fixcaseusernames && isset($person->username)){
-        $person->username = strtolower($person->username);
-    }
-    if($fixcasepersonalnames){
-        if(isset($person->firstname)){
-            $person->firstname = ucwords(strtolower($person->firstname));
+    /**
+     * Process the person tag. This defines a Moodle user.
+     *
+     * @param string $tagcontents The raw contents of the XML element
+     */
+    protected function process_person_tag($tagcontents) {
+        global $CFG, $DB;
+
+        // Get plugin configs.
+        $imssourcedidfallback   = $this->get_config('imssourcedidfallback');
+        $fixcaseusernames       = $this->get_config('fixcaseusernames');
+        $fixcasepersonalnames   = $this->get_config('fixcasepersonalnames');
+        $imsdeleteusers         = $this->get_config('imsdeleteusers');
+        $createnewusers         = $this->get_config('createnewusers');
+
+        $person = new stdClass();
+        if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
+            $person->idnumber = trim($matches[1]);
         }
-        if(isset($person->lastname)){
-            $person->lastname = ucwords(strtolower($person->lastname));
+        if (preg_match('{<name>.*?<n>.*?<given>(.+?)</given>.*?</n>.*?</name>}is', $tagcontents, $matches)) {
+            $person->firstname = trim($matches[1]);
+        }
+        if (preg_match('{<name>.*?<n>.*?<family>(.+?)</family>.*?</n>.*?</name>}is', $tagcontents, $matches)) {
+            $person->lastname = trim($matches[1]);
+        }
+        if (preg_match('{<userid>(.*?)</userid>}is', $tagcontents, $matches)) {
+            $person->username = trim($matches[1]);
+        }
+        if ($imssourcedidfallback && trim($person->username) == '') {
+            // This is the point where we can fall back to useing the "sourcedid" if "userid" is not supplied
+            // NB We don't use an "elseif" because the tag may be supplied-but-empty.
+            $person->username = $person->idnumber;
+        }
+        if (preg_match('{<email>(.*?)</email>}is', $tagcontents, $matches)) {
+            $person->email = trim($matches[1]);
+        }
+        if (preg_match('{<url>(.*?)</url>}is', $tagcontents, $matches)) {
+            $person->url = trim($matches[1]);
+        }
+        if (preg_match('{<adr>.*?<locality>(.+?)</locality>.*?</adr>}is', $tagcontents, $matches)) {
+            $person->city = trim($matches[1]);
+        }
+        if (preg_match('{<adr>.*?<country>(.+?)</country>.*?</adr>}is', $tagcontents, $matches)) {
+            $person->country = trim($matches[1]);
         }
-    }
 
-    $recstatus = ($this->get_recstatus($tagcontents, 'person'));
+        // Fix case of some of the fields if required.
+        if ($fixcaseusernames && isset($person->username)) {
+            $person->username = strtolower($person->username);
+        }
+        if ($fixcasepersonalnames) {
+            if (isset($person->firstname)) {
+                $person->firstname = ucwords(strtolower($person->firstname));
+            }
+            if (isset($person->lastname)) {
+                $person->lastname = ucwords(strtolower($person->lastname));
+            }
+        }
 
+        $recstatus = ($this->get_recstatus($tagcontents, 'person'));
 
-    // Now if the recstatus is 3, we should delete the user if-and-only-if the setting for delete users is turned on
-    if($recstatus==3){
+        // Now if the recstatus is 3, we should delete the user if-and-only-if the setting for delete users is turned on.
+        if ($recstatus == 3) {
 
-        if($imsdeleteusers){ // If we're allowed to delete user records
-            // Do not dare to hack the user.deleted field directly in database!!!
-            if ($user = $DB->get_record('user', array('username'=>$person->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
-                if (delete_user($user)) {
-                    $this->log_line("Deleted user '$person->username' (ID number $person->idnumber).");
+            if ($imsdeleteusers) { // If we're allowed to delete user records.
+                // Do not dare to hack the user.deleted field directly in database!!!
+                $params = array('username' => $person->username, 'mnethostid' => $CFG->mnet_localhost_id, 'deleted ' => 0);
+                if ($user = $DB->get_record('user', $params)) {
+                    if (delete_user($user)) {
+                        $this->log_line("Deleted user '$person->username' (ID number $person->idnumber).");
+                    } else {
+                        $this->log_line("Error deleting '$person->username' (ID number $person->idnumber).");
+                    }
                 } else {
-                    $this->log_line("Error deleting '$person->username' (ID number $person->idnumber).");
+                    $this->log_line("Can not delete user '$person->username' (ID number $person->idnumber) - user does not exist.");
                 }
             } else {
-                $this->log_line("Can not delete user '$person->username' (ID number $person->idnumber) - user does not exist.");
+                $this->log_line("Ignoring deletion request for user '$person->username' (ID number $person->idnumber).");
             }
-        }else{
-            $this->log_line("Ignoring deletion request for user '$person->username' (ID number $person->idnumber).");
-        }
 
-    }else{ // Add or update record
+        } else { // Add or update record.
+
+            // If the user exists (matching sourcedid) then we don't need to do anything.
+            if (!$DB->get_field('user', 'id', array('idnumber' => $person->idnumber)) && $createnewusers) {
+                // If they don't exist and haven't a defined username, we log this as a potential problem.
+                if ((!isset($person->username)) || (strlen($person->username) == 0)) {
+                    $this->log_line("Cannot create new user for ID # $person->idnumber".
+                        "- no username listed in IMS data for this person.");
+                } else if ($DB->get_field('user', 'id', array('username' => $person->username))) {
+                    // If their idnumber is not registered but their user ID is, then add their idnumber to their record.
+                    $DB->set_field('user', 'idnumber', $person->idnumber, array('username' => $person->username));
+                } else {
 
+                    // If they don't exist and they have a defined username, and $createnewusers == true, we create them.
+                    $person->lang = $CFG->lang;
+                    // TODO: MDL-15863 this needs more work due to multiauth changes, use first auth for now.
+                    $auth = explode(',', $CFG->auth);
+                    $auth = reset($auth);
+                    $person->auth = $auth;
+                    $person->confirmed = 1;
+                    $person->timemodified = time();
+                    $person->mnethostid = $CFG->mnet_localhost_id;
+                    $id = $DB->insert_record('user', $person);
+                    $this->log_line("Created user record ('.$id.') for user '$person->username' (ID number $person->idnumber).");
+                }
+            } else if ($createnewusers) {
+                $this->log_line("User record already exists for user '$person->username' (ID number $person->idnumber).");
 
-        // If the user exists (matching sourcedid) then we don't need to do anything.
-        if(!$DB->get_field('user', 'id', array('idnumber'=>$person->idnumber)) && $createnewusers){
-            // If they don't exist and haven't a defined username, we log this as a potential problem.
-            if((!isset($person->username)) || (strlen($person->username)==0)){
-                $this->log_line("Cannot create new user for ID # $person->idnumber - no username listed in IMS data for this person.");
-            } else if ($DB->get_field('user', 'id', array('username'=>$person->username))){
-                // If their idnumber is not registered but their user ID is, then add their idnumber to their record
-                $DB->set_field('user', 'idnumber', $person->idnumber, array('username'=>$person->username));
+                // It is totally wrong to mess with deleted users flag directly in database!!!
+                // There is no official way to undelete user, sorry..
             } else {
-
-            // If they don't exist and they have a defined username, and $createnewusers == true, we create them.
-            $person->lang = $CFG->lang;
-            $auth = explode(',', $CFG->auth); //TODO: this needs more work due tu multiauth changes, use first auth for now
-            $auth = reset($auth);
-            $person->auth = $auth;
-            $person->confirmed = 1;
-            $person->timemodified = time();
-            $person->mnethostid = $CFG->mnet_localhost_id;
-            $id = $DB->insert_record('user', $person);
-    /*
-    Photo processing is deactivated until we hear from Moodle dev forum about modification to gdlib.
-
-                                 //Antoni Mas. 07/12/2005. If a photo URL is specified then we might want to load
-                                 // it into the user's profile. Beware that this may cause a heavy overhead on the server.
-                                 if($CFG->enrol_processphoto){
-                                   if(preg_match('{<photo>.*?<extref>(.*?)</extref>.*?</photo>}is', $tagcontents, $matches)){
-                                     $person->urlphoto = trim($matches[1]);
-                                   }
-                                   //Habilitam el flag que ens indica que el personatge t foto prpia.
-                                   $person->picture = 1;
-                                   //Llibreria creada per nosaltres mateixos.
-                                   require_once($CFG->dirroot.'/lib/gdlib.php');
-                                   if ($usernew->picture = save_profile_image($id, $person->urlphoto,'user')) { TODO: use process_new_icon() instead
-                                     $DB->set_field('user', 'picture', $usernew->picture, array('id'=>$id));  /// Note picture in DB
-                                   }
-                                 }
-    */
-                $this->log_line("Created user record for user '$person->username' (ID number $person->idnumber).");
+                $this->log_line("No user record found for '$person->username' (ID number $person->idnumber).");
             }
-        } elseif ($createnewusers) {
-            $this->log_line("User record already exists for user '$person->username' (ID number $person->idnumber).");
 
-            // It is totally wrong to mess with deleted users flag directly in database!!!
-            // There is no official way to undelete user, sorry..
-        }else{
-            $this->log_line("No user record found for '$person->username' (ID number $person->idnumber).");
         }
 
-    } // End of are-we-deleting-or-adding
+    }
 
-} // End process_person_tag()
+    /**
+     * Process the membership tag. This defines whether the specified Moodle users
+     * should be added/removed as teachers/students.
+     *
+     * @param string $tagcontents The raw contents of the XML element
+     */
+    protected function process_membership_tag($tagcontents) {
+        global $DB;
 
-/**
-* Process the membership tag. This defines whether the specified Moodle users
-* should be added/removed as teachers/students.
-* @param string $tagconents The raw contents of the XML element
-*/
-function process_membership_tag($tagcontents){
-    global $DB;
-
-    // Get plugin configs
-    $truncatecoursecodes = $this->get_config('truncatecoursecodes');
-    $imscapitafix = $this->get_config('imscapitafix');
-
-    $memberstally = 0;
-    $membersuntally = 0;
-
-    // In order to reduce the number of db queries required, group name/id associations are cached in this array:
-    $groupids = array();
-
-    $ship = new stdClass();
-
-    if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)){
-        $ship->coursecode = ($truncatecoursecodes > 0)
-                                 ? substr(trim($matches[1]), 0, intval($truncatecoursecodes))
-                                 : trim($matches[1]);
-        $ship->courseid = $DB->get_field('course', 'id', array('idnumber'=>$ship->coursecode));
-    }
-    if($ship->courseid && preg_match_all('{<member>(.*?)</member>}is', $tagcontents, $membermatches, PREG_SET_ORDER)){
-        $courseobj = new stdClass();
-        $courseobj->id = $ship->courseid;
-
-        foreach($membermatches as $mmatch){
-            $member = new stdClass();
-            $memberstoreobj = new stdClass();
-            if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $mmatch[1], $matches)){
-                $member->idnumber = trim($matches[1]);
-            }
-            if(preg_match('{<role\s+roletype=["\'](.+?)["\'].*?>}is', $mmatch[1], $matches)){
-                $member->roletype = trim($matches[1]); // 01 means Student, 02 means Instructor, 3 means ContentDeveloper, and there are more besides
-            } elseif($imscapitafix && preg_match('{<roletype>(.+?)</roletype>}is', $mmatch[1], $matches)){
-                // The XML that comes out of Capita Student Records seems to contain a misinterpretation of the IMS specification!
-                $member->roletype = trim($matches[1]); // 01 means Student, 02 means Instructor, 3 means ContentDeveloper, and there are more besides
-            }
-            if(preg_match('{<role\b.*?<status>(.+?)</status>.*?</role>}is', $mmatch[1], $matches)){
-                $member->status = trim($matches[1]); // 1 means active, 0 means inactive - treat this as enrol vs unenrol
-            }
+        // Get plugin configs.
+        $truncatecoursecodes = $this->get_config('truncatecoursecodes');
+        $imscapitafix = $this->get_config('imscapitafix');
 
-            $recstatus = ($this->get_recstatus($mmatch[1], 'role'));
-            if($recstatus==3){
-              $member->status = 0; // See above - recstatus of 3 (==delete) is treated the same as status of 0
-              //echo "<p>process_membership_tag: unenrolling member due to recstatus of 3</p>";
-            }
+        $memberstally = 0;
+        $membersuntally = 0;
 
-            $timeframe = new stdClass();
-            $timeframe->begin = 0;
-            $timeframe->end = 0;
-            if(preg_match('{<role\b.*?<timeframe>(.+?)</timeframe>.*?</role>}is', $mmatch[1], $matches)){
-                $timeframe = $this->decode_timeframe($matches[1]);
-            }
-            if(preg_match('{<role\b.*?<extension>.*?<cohort>(.+?)</cohort>.*?</extension>.*?</role>}is', $mmatch[1], $matches)){
-                $member->groupname = trim($matches[1]);
-                // The actual processing (ensuring a group record exists, etc) occurs below, in the enrol-a-student clause
-            }
+        // In order to reduce the number of db queries required, group name/id associations are cached in this array.
+        $groupids = array();
+
+        $ship = new stdClass();
+
+        if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
+            $ship->coursecode = ($truncatecoursecodes > 0)
+                ? substr(trim($matches[1]), 0, intval($truncatecoursecodes))
+                : trim($matches[1]);
+            $ship->courseid = $DB->get_field('course', 'id', array('idnumber' => $ship->coursecode));
+        }
+        if ($ship->courseid && preg_match_all('{<member>(.*?)</member>}is', $tagcontents, $membermatches, PREG_SET_ORDER)) {
+            $courseobj = new stdClass();
+            $courseobj->id = $ship->courseid;
+
+            foreach ($membermatches as $mmatch) {
+                $member = new stdClass();
+                $memberstoreobj = new stdClass();
+                if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $mmatch[1], $matches)) {
+                    $member->idnumber = trim($matches[1]);
+                }
+                if (preg_match('{<role\s+roletype=["\'](.+?)["\'].*?>}is', $mmatch[1], $matches)) {
+                    // 01 means Student, 02 means Instructor, 3 means ContentDeveloper, and there are more besides.
+                    $member->roletype = trim($matches[1]);
+                } else if ($imscapitafix && preg_match('{<roletype>(.+?)</roletype>}is', $mmatch[1], $matches)) {
+                    // The XML that comes out of Capita Student Records seems to contain a misinterpretation of
+                    // the IMS specification! 01 means Student, 02 means Instructor, 3 means ContentDeveloper,
+                    // and there are more besides.
+                    $member->roletype = trim($matches[1]);
+                }
+                if (preg_match('{<role\b.*?<status>(.+?)</status>.*?</role>}is', $mmatch[1], $matches)) {
+                    // 1 means active, 0 means inactive - treat this as enrol vs unenrol.
+                    $member->status = trim($matches[1]);
+                }
 
-            $rolecontext = context_course::instance($ship->courseid);
-            $rolecontext = $rolecontext->id; // All we really want is the ID
-//$this->log_line("Context instance for course $ship->courseid is...");
-//print_r($rolecontext);
-
-            // Add or remove this student or teacher to the course...
-            $memberstoreobj->userid = $DB->get_field('user', 'id', array('idnumber'=>$member->idnumber));
-            $memberstoreobj->enrol = 'imsenterprise';
-            $memberstoreobj->course = $ship->courseid;
-            $memberstoreobj->time = time();
-            $memberstoreobj->timemodified = time();
-            if($memberstoreobj->userid){
-
-                // Decide the "real" role (i.e. the Moodle role) that this user should be assigned to.
-                // Zero means this roletype is supposed to be skipped.
-                $moodleroleid = $this->rolemappings[$member->roletype];
-                if(!$moodleroleid) {
-                    $this->log_line("SKIPPING role $member->roletype for $memberstoreobj->userid ($member->idnumber) in course $memberstoreobj->course");
-                    continue;
+                $recstatus = ($this->get_recstatus($mmatch[1], 'role'));
+                if ($recstatus == 3) {
+                    // See above - recstatus of 3 (==delete) is treated the same as status of 0.
+                    $member->status = 0;
                 }
 
-                if(intval($member->status) == 1) {
-                    // Enrol the member
+                $timeframe = new stdClass();
+                $timeframe->begin = 0;
+                $timeframe->end = 0;
+                if (preg_match('{<role\b.*?<timeframe>(.+?)</timeframe>.*?</role>}is', $mmatch[1], $matches)) {
+                    $timeframe = $this->decode_timeframe($matches[1]);
+                }
+                if (preg_match('{<role\b.*?<extension>.*?<cohort>(.+?)</cohort>.*?</extension>.*?</role>}is',
+                        $mmatch[1], $matches)) {
+                    $member->groupname = trim($matches[1]);
+                    // The actual processing (ensuring a group record exists, etc) occurs below, in the enrol-a-student clause.
+                }
 
-                    $einstance = $DB->get_record('enrol',
-                                    array('courseid' => $courseobj->id, 'enrol' => $memberstoreobj->enrol));
-                    if (empty($einstance)) {
-                        // Only add an enrol instance to the course if non-existent
-                        $enrolid = $this->add_instance($courseobj);
-                        $einstance = $DB->get_record('enrol', array('id' => $enrolid));
+                // Add or remove this student or teacher to the course...
+                $memberstoreobj->userid = $DB->get_field('user', 'id', array('idnumber' => $member->idnumber));
+                $memberstoreobj->enrol = 'imsenterprise';
+                $memberstoreobj->course = $ship->courseid;
+                $memberstoreobj->time = time();
+                $memberstoreobj->timemodified = time();
+                if ($memberstoreobj->userid) {
+
+                    // Decide the "real" role (i.e. the Moodle role) that this user should be assigned to.
+                    // Zero means this roletype is supposed to be skipped.
+                    $moodleroleid = $this->rolemappings[$member->roletype];
+                    if (!$moodleroleid) {
+                        $this->log_line("SKIPPING role $member->roletype for $memberstoreobj->userid "
+                            ."($member->idnumber) in course $memberstoreobj->course");
+                        continue;
                     }
 
-                    $this->enrol_user($einstance, $memberstoreobj->userid, $moodleroleid, $timeframe->begin, $timeframe->end);
+                    if (intval($member->status) == 1) {
+                        // Enrol the member.
 
-                    $this->log_line("Enrolled user #$memberstoreobj->userid ($member->idnumber) to role $member->roletype in course $memberstoreobj->course");
-                    $memberstally++;
+                        $einstance = $DB->get_record('enrol',
+                            array('courseid' => $courseobj->id, 'enrol' => $memberstoreobj->enrol));
+                        if (empty($einstance)) {
+                            // Only add an enrol instance to the course if non-existent.
+                            $enrolid = $this->add_instance($courseobj);
+                            $einstance = $DB->get_record('enrol', array('id' => $enrolid));
+                        }
 
-                    // At this point we can also ensure the group membership is recorded if present
-                    if(isset($member->groupname)){
-                        // Create the group if it doesn't exist - either way, make sure we know the group ID
-                        if(isset($groupids[$member->groupname])) {
-                            $member->groupid = $groupids[$member->groupname]; // Recall the group ID from cache if available
-                        } else {
-                            if($groupid = $DB->get_field('groups', 'id', array('courseid'=>$ship->courseid, 'name'=>$member->groupname))){
-                                $member->groupid = $groupid;
-                                $groupids[$member->groupname] = $groupid; // Store ID in cache
+                        $this->enrol_user($einstance, $memberstoreobj->userid, $moodleroleid, $timeframe->begin, $timeframe->end);
+
+                        $this->log_line("Enrolled user #$memberstoreobj->userid ($member->idnumber) "
+                            ."to role $member->roletype in course $memberstoreobj->course");
+                        $memberstally++;
+
+                        // At this point we can also ensure the group membership is recorded if present.
+                        if (isset($member->groupname)) {
+                            // Create the group if it doesn't exist - either way, make sure we know the group ID.
+                            if (isset($groupids[$member->groupname])) {
+                                $member->groupid = $groupids[$member->groupname]; // Recall the group ID from cache if available.
                             } else {
-                                // Attempt to create the group
-                                $group = new stdClass();
-                                $group->name = $member->groupname;
-                                $group->courseid = $ship->courseid;
-                                $group->timecreated = time();
-                                $group->timemodified = time();
-                                $groupid = $DB->insert_record('groups', $group);
-                                $this->log_line('Added a new group for this course: '.$group->name);
-                                $groupids[$member->groupname] = $groupid; // Store ID in cache
-                                $member->groupid = $groupid;
-                                // Invalidate the course group data cache just in case.
-                                cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($ship->courseid));
+                                $params = array('courseid' => $ship->courseid, 'name' => $member->groupname);
+                                if ($groupid = $DB->get_field('groups', 'id', $params)) {
+                                    $member->groupid = $groupid;
+                                    $groupids[$member->groupname] = $groupid; // Store ID in cache.
+                                } else {
+                                    // Attempt to create the group.
+                                    $group = new stdClass();
+                                    $group->name = $member->groupname;
+                                    $group->courseid = $ship->courseid;
+                                    $group->timecreated = time();
+                                    $group->timemodified = time();
+                                    $groupid = $DB->insert_record('groups', $group);
+                                    $this->log_line('Added a new group for this course: '.$group->name);
+                                    $groupids[$member->groupname] = $groupid; // Store ID in cache.
+                                    $member->groupid = $groupid;
+                                    // Invalidate the course group data cache just in case.
+                                    cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($ship->courseid));
+                                }
                             }
-                        }
-                        // Add the user-to-group association if it doesn't already exist
-                        if($member->groupid) {
-                            groups_add_member($member->groupid, $memberstoreobj->userid,
+                            // Add the user-to-group association if it doesn't already exist.
+                            if ($member->groupid) {
+                                groups_add_member($member->groupid, $memberstoreobj->userid,
                                     'enrol_imsenterprise', $einstance->id);
+                            }
                         }
-                    } // End of group-enrolment (from member.role.extension.cohort tag)
 
-                } elseif ($this->get_config('imsunenrol')) {
-                    // Unenrol member
+                    } else if ($this->get_config('imsunenrol')) {
+                        // Unenrol member.
+
+                        $einstances = $DB->get_records('enrol',
+                            array('enrol' => $memberstoreobj->enrol, 'courseid' => $courseobj->id));
+                        foreach ($einstances as $einstance) {
+                            // Unenrol the user from all imsenterprise enrolment instances.
+                            $this->unenrol_user($einstance, $memberstoreobj->userid);
+                        }
 
-                    $einstances = $DB->get_records('enrol',
-                                    array('enrol' => $memberstoreobj->enrol, 'courseid' => $courseobj->id));
-                    foreach ($einstances as $einstance) {
-                        // Unenrol the user from all imsenterprise enrolment instances
-                        $this->unenrol_user($einstance, $memberstoreobj->userid);
+                        $membersuntally++;
+                        $this->log_line("Unenrolled $member->idnumber from role $moodleroleid in course");
                     }
 
-                    $membersuntally++;
-                    $this->log_line("Unenrolled $member->idnumber from role $moodleroleid in course");
                 }
-
+            }
+            $this->log_line("Added $memberstally users to course $ship->coursecode");
+            if ($membersuntally > 0) {
+                $this->log_line("Removed $membersuntally users from course $ship->coursecode");
             }
         }
-        $this->log_line("Added $memberstally users to course $ship->coursecode");
-        if($membersuntally > 0){
-            $this->log_line("Removed $membersuntally users from course $ship->coursecode");
+    } // End process_membership_tag().
+
+    /**
+     * Process the properties tag. The only data from this element
+     * that is relevant is whether a <target> is specified.
+     *
+     * @param string $tagcontents The raw contents of the XML element
+     */
+    protected function process_properties_tag($tagcontents) {
+        $imsrestricttarget = $this->get_config('imsrestricttarget');
+
+        if ($imsrestricttarget) {
+            if (!(preg_match('{<target>'.preg_quote($imsrestricttarget).'</target>}is', $tagcontents, $matches))) {
+                $this->log_line("Skipping processing: required target \"$imsrestricttarget\" not specified in this data.");
+                $this->continueprocessing = false;
+            }
         }
     }
-} // End process_membership_tag()
 
-/**
-* Process the properties tag. The only data from this element
-* that is relevant is whether a <target> is specified.
-* @param string $tagconents The raw contents of the XML element
-*/
-function process_properties_tag($tagcontents){
-    $imsrestricttarget = $this->get_config('imsrestricttarget');
-
-    if ($imsrestricttarget) {
-        if(!(preg_match('{<target>'.preg_quote($imsrestricttarget).'</target>}is', $tagcontents, $matches))){
-            $this->log_line("Skipping processing: required target \"$imsrestricttarget\" not specified in this data.");
-            $this->continueprocessing = false;
+    /**
+     * Store logging information. This does two things: uses the {@link mtrace()}
+     * function to print info to screen/STDOUT, and also writes log to a text file
+     * if a path has been specified.
+     * @param string $string Text to write (newline will be added automatically)
+     */
+    protected function log_line($string) {
+
+        if (!PHPUNIT_TEST) {
+            mtrace($string);
+        }
+        if ($this->logfp) {
+            fwrite($this->logfp, $string . "\n");
         }
     }
-}
 
-/**
-* Store logging information. This does two things: uses the {@link mtrace()}
-* function to print info to screen/STDOUT, and also writes log to a text file
-* if a path has been specified.
-* @param string $string Text to write (newline will be added automatically)
-*/
-function log_line($string){
-
-    if (!PHPUNIT_TEST) {
-        mtrace($string);
-    }
-    if($this->logfp) {
-        fwrite($this->logfp, $string . "\n");
+    /**
+     * Process the INNER contents of a <timeframe> tag, to return beginning/ending dates.
+     *
+     * @param string $string tag to decode.
+     * @return stdClass beginning and/or ending is returned, in unix time, zero indicating not specified.
+     */
+    protected static function decode_timeframe($string) {
+        $ret = new stdClass();
+        $ret->begin = $ret->end = 0;
+        // Explanatory note: The matching will ONLY match if the attribute restrict="1"
+        // because otherwise the time markers should be ignored (participation should be
+        // allowed outside the period).
+        if (preg_match('{<begin\s+restrict="1">(\d\d\d\d)-(\d\d)-(\d\d)</begin>}is', $string, $matches)) {
+            $ret->begin = mktime(0, 0, 0, $matches[2], $matches[3], $matches[1]);
+        }
+        if (preg_match('{<end\s+restrict="1">(\d\d\d\d)-(\d\d)-(\d\d)</end>}is', $string, $matches)) {
+            $ret->end = mktime(0, 0, 0, $matches[2], $matches[3], $matches[1]);
+        }
+        return $ret;
     }
-}
 
-/**
-* Process the INNER contents of a <timeframe> tag, to return beginning/ending dates.
-*/
-function decode_timeframe($string){ // Pass me the INNER CONTENTS of a <timeframe> tag - beginning and/or ending is returned, in unix time, zero indicating not specified
-    $ret = new stdClass();
-    $ret->begin = $ret->end = 0;
-    // Explanatory note: The matching will ONLY match if the attribute restrict="1"
-    // because otherwise the time markers should be ignored (participation should be
-    // allowed outside the period)
-    if(preg_match('{<begin\s+restrict="1">(\d\d\d\d)-(\d\d)-(\d\d)</begin>}is', $string, $matches)){
-        $ret->begin = mktime(0,0,0, $matches[2], $matches[3], $matches[1]);
-    }
-    if(preg_match('{<end\s+restrict="1">(\d\d\d\d)-(\d\d)-(\d\d)</end>}is', $string, $matches)){
-        $ret->end = mktime(0,0,0, $matches[2], $matches[3], $matches[1]);
-    }
-    return $ret;
-} // End decode_timeframe
+    /**
+     * Load the role mappings (from the config), so we can easily refer to
+     * how an IMS-E role corresponds to a Moodle role
+     */
+    protected function load_role_mappings() {
+        require_once('locallib.php');
 
-/**
-* Load the role mappings (from the config), so we can easily refer to
-* how an IMS-E role corresponds to a Moodle role
-*/
-function load_role_mappings() {
-    require_once('locallib.php');
-    global $DB;
-
-    $imsroles = new imsenterprise_roles();
-    $imsroles = $imsroles->get_imsroles();
-
-    $this->rolemappings = array();
-    foreach($imsroles as $imsrolenum=>$imsrolename) {
-        $this->rolemappings[$imsrolenum] = $this->rolemappings[$imsrolename] = $this->get_config('imsrolemap' . $imsrolenum);
+        $imsroles = new imsenterprise_roles();
+        $imsroles = $imsroles->get_imsroles();
+
+        $this->rolemappings = array();
+        foreach ($imsroles as $imsrolenum => $imsrolename) {
+            $this->rolemappings[$imsrolenum] = $this->rolemappings[$imsrolename] = $this->get_config('imsrolemap' . $imsrolenum);
+        }
     }
-}
 
     /**
      * Load the name mappings (from the config), so we can easily refer to
      * how an IMS-E course properties corresponds to a Moodle course properties
      */
-    function load_course_mappings() {
+    protected function load_course_mappings() {
         require_once('locallib.php');
 
         $imsnames = new imsenterprise_courses();
         $courseattrs = $imsnames->get_courseattrs();
 
         $this->coursemappings = array();
-        foreach($courseattrs as $courseattr) {
+        foreach ($courseattrs as $courseattr) {
             $this->coursemappings[$courseattr] = $this->get_config('imscoursemap' . $courseattr);
         }
     }
@@ -828,7 +777,7 @@ function load_role_mappings() {
      * @param int $userid User ID being removed from group
      * @return bool True if the remove is permitted, false to give an error
      */
-    function enrol_imsenterprise_allow_group_member_remove($itemid, $groupid, $userid) {
+    public function enrol_imsenterprise_allow_group_member_remove($itemid, $groupid, $userid) {
         return false;
     }
 
@@ -852,8 +801,4 @@ function load_role_mappings() {
 
         return $defaultcategoryid;
     }
-
-
-} // end of class
-
-
+}
index f203dc8..acaaa12 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
+/**
+ * Class for dealing with role mappings in IMS Enterprise.
+ *
+ * @copyright  2010 Eugene Venter
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 class imsenterprise_roles {
+    /** @var imscode => ims role name. Role name mapping. */
     private $imsroles;
 
-    function __construct() {
+    /**
+     * Constructor.
+     */
+    public function __construct() {
         $this->imsroles = array(
-        '01'=>'Learner',
-        '02'=>'Instructor',
-        '03'=>'Content Developer',
-        '04'=>'Member',
-        '05'=>'Manager',
-        '06'=>'Mentor',
-        '07'=>'Administrator',
-        '08'=>'TeachingAssistant',
+            '01' => 'Learner',
+            '02' => 'Instructor',
+            '03' => 'Content Developer',
+            '04' => 'Member',
+            '05' => 'Manager',
+            '06' => 'Mentor',
+            '07' => 'Administrator',
+            '08' => 'TeachingAssistant',
         );
         // PLEASE NOTE: It may seem odd that "Content Developer" has a space in it
         // but "TeachingAssistant" doesn't. That's what the spec says though!!!
     }
 
-    function get_imsroles() {
+    /**
+     * Returns the mapped roles
+     *
+     * @return array of IMS roles indexed by IMS code.
+     */
+    public function get_imsroles() {
         return $this->imsroles;
     }
 
     /**
-    * This function is only used when first setting up the plugin, to
-    * decide which role assignments to recommend by default.
-    * For example, IMS role '01' is 'Learner', so may map to 'student' in Moodle.
-    */
-    function determine_default_rolemapping($imscode) {
+     * This function is only used when first setting up the plugin, to
+     * decide which role assignments to recommend by default.
+     * For example, IMS role '01' is 'Learner', so may map to 'student' in Moodle.
+     *
+     * @param string $imscode
+     */
+    public function determine_default_rolemapping($imscode) {
         global $DB;
 
         switch($imscode) {
@@ -73,13 +90,13 @@ class imsenterprise_roles {
                 $shortname = 'admin';
                 break;
             default:
-                return 0; // Zero for no match
+                return 0; // Zero for no match.
         }
-        return (string)$DB->get_field('role', 'id', array('shortname'=>$shortname));
+        return (string)$DB->get_field('role', 'id', array('shortname' => $shortname));
     }
 
 
-}  // class
+}
 
 
 /**
@@ -90,14 +107,15 @@ class imsenterprise_roles {
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class imsenterprise_courses {
-
+    /** @var array IMS group description names */
     private $imsnames;
+    /** @var array moodle course field names */
     private $courseattrs;
 
     /**
      * Loads default
      */
-    function __construct() {
+    public function __construct() {
         $this->imsnames = array(
             'short' => 'short',
             'long' => 'long',
@@ -111,7 +129,7 @@ class imsenterprise_courses {
      * @param string $courseattr The course attribute (shortname, fullname...)
      * @return array Array of assignable values
      */
-    function get_imsnames($courseattr) {
+    public function get_imsnames($courseattr) {
 
         $values = $this->imsnames;
         if ($courseattr == 'summary') {
@@ -124,7 +142,7 @@ class imsenterprise_courses {
      * courseattrs getter
      * @return array
      */
-    function get_courseattrs() {
+    public function get_courseattrs() {
         return $this->courseattrs;
     }
 
@@ -132,10 +150,10 @@ class imsenterprise_courses {
      * This function is only used when first setting up the plugin, to
      * decide which name assignments to recommend by default.
      *
-     * @param string $coursename
+     * @param string $courseattr
      * @return string
      */
-    function determine_default_coursemapping($courseattr) {
+    public function determine_default_coursemapping($courseattr) {
         switch($courseattr) {
             case 'fullname':
                 $imsname = 'short';
@@ -149,5 +167,4 @@ class imsenterprise_courses {
 
         return $imsname;
     }
-
-}  // class
+}
index 5b7fbb6..2ecf575 100644 (file)
@@ -27,31 +27,43 @@ defined('MOODLE_INTERNAL') || die();
 if ($ADMIN->fulltree) {
     require_once($CFG->dirroot.'/enrol/imsenterprise/locallib.php');
 
-    $settings->add(new admin_setting_heading('enrol_imsenterprise_settings', '', get_string('pluginname_desc', 'enrol_imsenterprise')));
+    $settings->add(new admin_setting_heading('enrol_imsenterprise_settings', '',
+        get_string('pluginname_desc', 'enrol_imsenterprise')));
 
-    //--- general settings -----------------------------------------------------------------------------------
-    $settings->add(new admin_setting_heading('enrol_imsenterprise_basicsettings', get_string('basicsettings', 'enrol_imsenterprise'), ''));
+    // General settings.
+    $settings->add(new admin_setting_heading('enrol_imsenterprise_basicsettings',
+        get_string('basicsettings', 'enrol_imsenterprise'), ''));
 
-    $settings->add(new admin_setting_configtext('enrol_imsenterprise/imsfilelocation', get_string('location', 'enrol_imsenterprise'), '', ''));
+    $settings->add(new admin_setting_configtext('enrol_imsenterprise/imsfilelocation',
+        get_string('location', 'enrol_imsenterprise'), '', ''));
 
-    $settings->add(new admin_setting_configtext('enrol_imsenterprise/logtolocation', get_string('logtolocation', 'enrol_imsenterprise'), '', ''));
+    $settings->add(new admin_setting_configtext('enrol_imsenterprise/logtolocation',
+        get_string('logtolocation', 'enrol_imsenterprise'), '', ''));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/mailadmins', get_string('mailadmins', 'enrol_imsenterprise'), '', 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/mailadmins',
+        get_string('mailadmins', 'enrol_imsenterprise'), '', 0));
 
-    //--- user data options ---------------------------------------------------------------------------------
-    $settings->add(new admin_setting_heading('enrol_imsenterprise_usersettings', get_string('usersettings', 'enrol_imsenterprise'), ''));
+    // User data options.
+    $settings->add(new admin_setting_heading('enrol_imsenterprise_usersettings',
+        get_string('usersettings', 'enrol_imsenterprise'), ''));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/createnewusers', get_string('createnewusers', 'enrol_imsenterprise'), get_string('createnewusers_desc', 'enrol_imsenterprise'), 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/createnewusers',
+        get_string('createnewusers', 'enrol_imsenterprise'), get_string('createnewusers_desc', 'enrol_imsenterprise'), 0));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsdeleteusers', get_string('deleteusers', 'enrol_imsenterprise'), get_string('deleteusers_desc', 'enrol_imsenterprise'), 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsdeleteusers',
+        get_string('deleteusers', 'enrol_imsenterprise'), get_string('deleteusers_desc', 'enrol_imsenterprise'), 0));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/fixcaseusernames', get_string('fixcaseusernames', 'enrol_imsenterprise'), '', 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/fixcaseusernames',
+        get_string('fixcaseusernames', 'enrol_imsenterprise'), '', 0));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/fixcasepersonalnames', get_string('fixcasepersonalnames', 'enrol_imsenterprise'), '', 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/fixcasepersonalnames',
+        get_string('fixcasepersonalnames', 'enrol_imsenterprise'), '', 0));
 
-    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imssourcedidfallback', get_string('sourcedidfallback', 'enrol_imsenterprise'), get_string('sourcedidfallback_desc', 'enrol_imsenterprise'), 0));
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imssourcedidfallback',
+        get_string('sourcedidfallback', 'enrol_imsenterprise'), get_string('sourcedidfallback_desc', 'enrol_imsenterprise'), 0));
 
-    $settings->add(ne