Merge branch 'MDL-41229-master' of git://github.com/mouneyrac/moodle
authorSam Hemelryk <sam@moodle.com>
Sun, 6 Oct 2013 23:50:29 +0000 (12:50 +1300)
committerSam Hemelryk <sam@moodle.com>
Sun, 6 Oct 2013 23:50:29 +0000 (12:50 +1300)
Conflicts:
theme/bootstrapbase/style/moodle.css

577 files changed:
admin/settings/courses.php
admin/tool/generator/tests/maketestcourse_test.php
admin/webservice/service.php
admin/webservice/service_users.php
backup/moodle2/backup_activity_task.class.php
backup/moodle2/backup_course_task.class.php
backup/moodle2/backup_root_task.class.php
backup/util/dbops/backup_controller_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
badges/assertion.php
badges/badge.php
badges/classes/assertion.php [new file with mode: 0644]
badges/lib/bakerlib.php
badges/renderer.php
badges/tests/badgeslib_test.php
blocks/course_list/block_course_list.php
blocks/course_overview/renderer.php
cache/classes/definition.php
cache/classes/dummystore.php
cache/tests/cache_test.php
calendar/export.php
calendar/view.php
course/format/lib.php
course/format/renderer.php
course/format/singleactivity/lib.php
course/format/singleactivity/settingslib.php
course/format/singleactivity/styles.css
course/lib.php
course/modedit.php
course/moodleform_mod.php
course/renderer.php
course/tests/behat/paged_course_navigation.feature
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js
course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js
course/yui/src/categoryexpander/js/categoryexpander.js
course/yui/toolboxes/toolboxes.js
enrol/category/version.php
enrol/cohort/db/events.php
enrol/cohort/locallib.php
enrol/cohort/version.php
enrol/imsenterprise/lib.php
enrol/meta/version.php
files/renderer.php
grade/edit/tree/lib.php
grade/tests/edittreelib_test.php
index.php
install/lang/bg/install.php
install/lang/ms/install.php
install/lang/pt/error.php
install/lang/pt/install.php
lang/en/backup.php
lang/en/repository.php
lang/en/webservice.php
lib/badgeslib.php
lib/behat/behat_base.php
lib/classes/event/base.php
lib/classes/event/webservice_function_called.php [new file with mode: 0644]
lib/classes/event/webservice_login_failed.php [new file with mode: 0644]
lib/classes/event/webservice_service_created.php [new file with mode: 0644]
lib/classes/event/webservice_service_deleted.php [new file with mode: 0644]
lib/classes/event/webservice_service_updated.php [new file with mode: 0644]
lib/classes/event/webservice_service_user_added.php [new file with mode: 0644]
lib/classes/event/webservice_service_user_removed.php [new file with mode: 0644]
lib/classes/event/webservice_token_created.php [new file with mode: 0644]
lib/classes/event/webservice_token_sent.php [new file with mode: 0644]
lib/classes/user.php
lib/componentlib.class.php
lib/coursecatlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/atto/plugins/link/lang/en/atto_link.php
lib/editor/atto/plugins/link/lib.php
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js
lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js
lib/editor/atto/plugins/link/yui/src/button/js/button.js
lib/editor/atto/styles.css
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-debug.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor-min.js
lib/editor/atto/yui/build/moodle-editor_atto-editor/moodle-editor_atto-editor.js
lib/editor/atto/yui/src/editor/js/editor.js
lib/editor/tinymce/lib.php
lib/editor/tinymce/plugins/dragmath/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/dragmath/tinymce/img/dragmath.gif [deleted file]
lib/editor/tinymce/plugins/dragmath/tinymce/img/dragmath.png [new file with mode: 0644]
lib/editor/tinymce/plugins/managefiles/tinymce/img/managefiles.png
lib/editor/tinymce/plugins/moodleemoticon/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/moodleemoticon/tinymce/img/moodleemoticon.gif [deleted file]
lib/editor/tinymce/plugins/moodleemoticon/tinymce/img/moodleemoticon.png [new file with mode: 0644]
lib/editor/tinymce/plugins/moodlemedia/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.gif [deleted file]
lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.png [new file with mode: 0644]
lib/editor/tinymce/plugins/moodlenolink/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/moodlenolink/tinymce/img/ed_nolink.gif [deleted file]
lib/editor/tinymce/plugins/moodlenolink/tinymce/img/prevent_autolink.png [new file with mode: 0644]
lib/editor/tinymce/plugins/pdw/pix/icon.gif [deleted file]
lib/editor/tinymce/plugins/pdw/pix/icon.png [new file with mode: 0644]
lib/editor/tinymce/plugins/pdw/tinymce/editor_plugin.js
lib/editor/tinymce/plugins/pdw/tinymce/img/toolbars.gif [deleted file]
lib/editor/tinymce/plugins/pdw/tinymce/img/toolbars.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/content.css [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/dialog.css [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/absolute.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/absolute.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/advance_hr.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/advance_hr.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/anchor.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/anchor.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/backward.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/backward.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bold.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bold.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bullet_list.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bullet_list.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/button_bg.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cell_props.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cell_props.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cite.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cite.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cleanup_messy_code.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cleanup_messy_code.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/clear_formatting.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/clear_formatting.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/copy.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/copy.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cut.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cut.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/decrease_indent.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/decrease_indent.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_col.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_col.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_row.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_row.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_table.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete_table.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/document_properties.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/document_properties.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/dragmath.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/dragmath.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/emoticons.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/emoticons.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/find_replace.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/find_replace.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/forward.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/forward.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/fullpage.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/fullpage.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/fullscreen.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/fullscreen.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/help.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/help.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/increase_indent.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/increase_indent.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_col_after.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_col_after.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_col_before.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_col_before.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_date.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_date.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_image.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_image.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_link.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_link.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_video.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_video.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_file.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_file.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_horizontal_ruler.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_horizontal_ruler.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_nonbreaking_space.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_nonbreaking_space.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_row_after.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_row_after.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_row_before.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_row_before.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_time.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_time.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/italic.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/italic.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/justify.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/justify.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_over.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_over.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_under.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_under.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/left_to_right.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/left_to_right.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/manage_files.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/manage_files.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/merge_cells.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/merge_cells.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/new_document.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/new_document.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/numbered_list.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/numbered_list.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/page_break.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/page_break.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste_text.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste_text.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste_word.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste_word.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/prevent_autolink.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/prevent_autolink.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/preview.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/preview.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/print.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/print.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/question.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/question.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/redo.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/redo.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/remove_link.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/remove_link.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/resize.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/resize.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/restore_draft.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/restore_draft.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/restore_last_draft.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/restore_last_draft.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/right_to_left.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/right_to_left.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/row_props.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/row_props.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/save.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/save.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/search.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/search.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/select_all.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/select_all.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/show_invisible_characters.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/show_invisible_characters.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/source_code.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/source_code.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/special_character.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/special_character.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/spellcheck.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/spellcheck.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/split_cells.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/split_cells.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/strikethrough.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/strikethrough.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/styleprops.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/styleprops.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/subscript.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/subscript.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/superscript.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/superscript.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/table.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/table.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/table_props.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/table_props.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/template.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/template.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/tick.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/tick.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/toggle_blockquote.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/toggle_blockquote.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/underline.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/underline.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/undo.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/undo.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/visual_aid.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/visual_aid.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/visual_blocks.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/visual_blocks.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/ui.css [new file with mode: 0644]
lib/enrollib.php
lib/eventslib.php
lib/filestorage/file_storage.php
lib/filestorage/stored_file.php
lib/filestorage/tests/file_storage_test.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrequirementslib.php
lib/phpunit/classes/advanced_testcase.php
lib/phpunit/classes/util.php
lib/phpunit/tests/advanced_test.php
lib/pluginlib.php
lib/questionlib.php
lib/tests/componentlib_test.php
lib/tests/event_test.php
lib/tests/eventslib_test.php
lib/tests/filelib_test.php
lib/tests/fixtures/event_fixtures.php
lib/tests/rsslib_test.php
lib/tests/weblib_test.php
lib/thirdpartylibs.xml
lib/upgrade.txt
login/token.php
message/lib.php
mod/assign/db/services.php
mod/assign/externallib.php
mod/assign/feedback/editpdf/ajax.php [new file with mode: 0644]
mod/assign/feedback/editpdf/backup/moodle2/backup_assignfeedback_editpdf_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/editpdf/backup/moodle2/restore_assignfeedback_editpdf_subplugin.class.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/annotation.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/comment.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/comments_quick_list.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/document_services.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/page_editor.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/pdf.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/renderer.php [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/widget.php [new file with mode: 0644]
mod/assign/feedback/editpdf/db/install.xml [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/ASL20.txt [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/filters/FilterASCII85.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/filters/FilterASCII85_FPDI.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/filters/FilterLZW.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/filters/FilterLZW_FPDI.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/fpdf_tpl.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/fpdi.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/fpdi2tcpdf_bridge.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/fpdi_pdf_parser.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/pdf_context.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/pdf_parser.php [new file with mode: 0644]
mod/assign/feedback/editpdf/fpdi/readme_moodle.txt [new file with mode: 0644]
mod/assign/feedback/editpdf/lang/en/assignfeedback_editpdf.php [new file with mode: 0644]
mod/assign/feedback/editpdf/lib.php [new file with mode: 0644]
mod/assign/feedback/editpdf/locallib.php [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_blue.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_blue.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_clear.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_clear.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_green.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_green.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_red.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_red.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_white.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_white.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_yellow.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/background_colour_yellow.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_black.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_black.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_blue.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_blue.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_green.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_green.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_red.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_red.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_white.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_white.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_yellow.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/colour_yellow.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/comment.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/comment.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/comment_search.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/comment_search.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/highlight.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/highlight.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/line.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/line.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/nav_next.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/nav_next.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/nav_prev.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/nav_prev.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/oval.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/oval.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/pen.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/pen.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/rectangle.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/rectangle.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/select.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/select.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/stamp.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/stamp.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/trash.png [new file with mode: 0644]
mod/assign/feedback/editpdf/pix/trash.svg [new file with mode: 0644]
mod/assign/feedback/editpdf/settings.php [new file with mode: 0644]
mod/assign/feedback/editpdf/styles.css [new file with mode: 0644]
mod/assign/feedback/editpdf/testgs.php [new file with mode: 0644]
mod/assign/feedback/editpdf/tests/editpdf_test.php [new file with mode: 0644]
mod/assign/feedback/editpdf/tests/fixtures/submission.pdf [new file with mode: 0644]
mod/assign/feedback/editpdf/tests/fixtures/testgs.pdf [new file with mode: 0644]
mod/assign/feedback/editpdf/version.php [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/build.json [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotation.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationhighlight.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationline.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationoval.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationpen.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationrectangle.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/colourpicker.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/comment.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/commentmenu.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/commentsearch.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/drawable.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/dropdown.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/edit.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/globals.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/point.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/quickcomment.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/quickcommentlist.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/rect.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js [new file with mode: 0644]
mod/assign/feedback/editpdf/yui/src/editor/meta/editor.json [new file with mode: 0644]
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/assign/tests/externallib_test.php
mod/chat/db/events.php
mod/chat/gui_header_js/jsupdate.php
mod/chat/gui_header_js/jsupdated.php
mod/chat/gui_header_js/users.php
mod/chat/lib.php
mod/chat/tests/generator_test.php
mod/chat/version.php
mod/choice/classes/event/answer_submitted.php [new file with mode: 0644]
mod/choice/classes/event/answer_updated.php [new file with mode: 0644]
mod/choice/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/choice/classes/event/instances_list_viewed.php [new file with mode: 0644]
mod/choice/classes/event/report_viewed.php [new file with mode: 0644]
mod/choice/index.php
mod/choice/lang/en/choice.php
mod/choice/lib.php
mod/choice/report.php
mod/choice/tests/events_test.php [new file with mode: 0644]
mod/choice/tests/generator/lib.php [new file with mode: 0644]
mod/choice/tests/generator_test.php [new file with mode: 0644]
mod/choice/view.php
mod/feedback/classes/event/course_module_viewed.php [new file with mode: 0644]
mod/feedback/classes/event/instances_list_viewed.php [new file with mode: 0644]
mod/feedback/classes/event/response_deleted.php [new file with mode: 0644]
mod/feedback/classes/event/response_submitted.php [new file with mode: 0644]
mod/feedback/complete.php
mod/feedback/complete_guest.php
mod/feedback/delete_completed.php
mod/feedback/index.php
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/feedback/show_entries.php
mod/feedback/tests/events_test.php [new file with mode: 0644]
mod/feedback/tests/generator/lib.php [new file with mode: 0644]
mod/feedback/tests/generator_test.php [new file with mode: 0644]
mod/feedback/view.php
mod/folder/mod_form.php
mod/forum/classes/observer.php
mod/forum/db/events.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/mod_form.php
mod/forum/settings.php
mod/forum/version.php
mod/imscp/index.php
mod/imscp/view.php
mod/quiz/attemptlib.php
mod/quiz/classes/event/attempt_abandoned.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_becameoverdue.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_started.php [new file with mode: 0644]
mod/quiz/classes/event/attempt_submitted.php [new file with mode: 0644]
mod/quiz/db/events.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/quiz/renderer.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/statistics/classes/calculated.php [new file with mode: 0644]
mod/quiz/report/statistics/classes/calculator.php [new file with mode: 0644]
mod/quiz/report/statistics/db/install.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/report/statistics/lib.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/statistics_form.php
mod/quiz/report/statistics/statistics_graph.php
mod/quiz/report/statistics/statistics_question_table.php
mod/quiz/report/statistics/statistics_table.php
mod/quiz/report/statistics/tests/statistics_test.php
mod/quiz/report/statistics/tests/stats_from_steps_walkthrough_test.php
mod/quiz/tests/events_test.php [new file with mode: 0644]
mod/quiz/version.php
mod/quiz/view.php
mod/scorm/lib.php
mod/upgrade.txt
mod/url/mod_form.php
mod/workshop/allocation/scheduled/version.php
mod/workshop/eval/best/lib.php
mod/workshop/eval/best/tests/lib_test.php
phpunit.xml.dist
question/behaviour/behaviourbase.php
question/classes/statistics/questions/calculated.php [new file with mode: 0644]
question/classes/statistics/questions/calculated_for_subquestion.php [new file with mode: 0644]
question/classes/statistics/questions/calculator.php [moved from question/engine/statistics.php with 50% similarity]
question/classes/statistics/responses/analyser.php [new file with mode: 0644]
question/classes/statistics/responses/analysis_for_actual_response.php [new file with mode: 0644]
question/classes/statistics/responses/analysis_for_class.php [new file with mode: 0644]
question/classes/statistics/responses/analysis_for_question.php [new file with mode: 0644]
question/classes/statistics/responses/analysis_for_subpart.php [new file with mode: 0644]
question/engine/questionattempt.php
question/engine/responseanalysis.php [deleted file]
question/engine/statisticslib.php
question/type/edit_question_form.php
question/type/multianswer/edit_multianswer_form.php
question/type/multianswer/questiontype.php
question/type/multichoice/backup/moodle2/backup_qtype_multichoice_plugin.class.php
question/type/multichoice/backup/moodle2/restore_qtype_multichoice_plugin.class.php
question/type/multichoice/db/install.xml
question/type/multichoice/db/upgrade.php
question/type/multichoice/questiontype.php
question/type/multichoice/tests/questiontype_test.php
question/type/multichoice/version.php
question/upgrade.txt
repository/upgrade.txt
tag/index.php
theme/afterburner/layout/default.php
theme/afterburner/style/afterburner_responsive.css
theme/afterburner/style/afterburner_rtl.css
theme/afterburner/style/afterburner_styles.css
theme/anomaly/style/base.css
theme/anomaly/style/general.css
theme/arialist/style/core.css
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/filemanager.css
theme/bootstrapbase/config.php
theme/bootstrapbase/layout/maintenance.php [new file with mode: 0644]
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/course.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/style/moodle.css
theme/bootstrapbase/upgrade.txt
theme/boxxie/style/core.css
theme/brick/style/colors.css
theme/brick/style/core.css
theme/canvas/style/core.css
theme/canvas/style/text.css
theme/clean/layout/maintenance.php [new file with mode: 0644]
theme/formal_white/style/core.css
theme/formal_white/style/course.css
theme/fusion/style/core.css
theme/leatherbound/style/core.css
theme/magazine/style/colors.css
theme/magazine/style/core.css
theme/mymobile/style/core.css
theme/nimble/style/core.css
theme/serenity/style/core.css
theme/sky_high/style/core.css
theme/splash/style/core.css
theme/splash/style/pagelayout.css
theme/standard/style/core.css
theme/standard/style/course.css
theme/standard/style/css3.css
theme/upgrade.txt
theme/yui_combo.php
theme/yui_image.php
user/edit.php
user/editadvanced.php
version.php
webservice/lib.php
webservice/tests/events_test.php [new file with mode: 0644]

index 377034b..4005c7c 100644 (file)
@@ -162,6 +162,7 @@ if ($hassiteconfig
     $temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_userscompletion', new lang_string('generaluserscompletion','backup'), new lang_string('configgeneraluserscompletion','backup'), array('value'=>1, 'locked'=>0)));
     $temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_logs', new lang_string('generallogs','backup'), new lang_string('configgenerallogs','backup'), array('value'=>0, 'locked'=>0)));
     $temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_histories', new lang_string('generalhistories','backup'), new lang_string('configgeneralhistories','backup'), array('value'=>0, 'locked'=>0)));
+    $temp->add(new admin_setting_configcheckbox_with_lock('backup/backup_general_questionbank', new lang_string('generalquestionbank','backup'), new lang_string('configgeneralquestionbank','backup'), array('value'=>1, 'locked'=>0)));
     $ADMIN->add('backups', $temp);
 
     // Create a page for general import configuration and defaults.
@@ -236,6 +237,7 @@ if ($hassiteconfig
     $temp->add(new admin_setting_configcheckbox('backup/backup_auto_userscompletion', new lang_string('generaluserscompletion','backup'), new lang_string('configgeneraluserscompletion','backup'), 1));
     $temp->add(new admin_setting_configcheckbox('backup/backup_auto_logs', new lang_string('generallogs', 'backup'), new lang_string('configgenerallogs', 'backup'), 0));
     $temp->add(new admin_setting_configcheckbox('backup/backup_auto_histories', new lang_string('generalhistories','backup'), new lang_string('configgeneralhistories','backup'), 0));
+    $temp->add(new admin_setting_configcheckbox('backup/backup_auto_questionbank', new lang_string('generalquestionbank','backup'), new lang_string('configgeneralquestionbank','backup'), 1));
 
 
     //$temp->add(new admin_setting_configcheckbox('backup/backup_auto_messages', new lang_string('messages', 'message'), new lang_string('backupmessageshelp','message'), 0));
index b5afea0..a5150b1 100644 (file)
@@ -112,7 +112,6 @@ class tool_generator_maketestcourse_testcase extends advanced_testcase {
      * Creates an small test course with fixed data set and checks the used sections and users.
      */
     public function test_fixed_data_set() {
-        global $DB;
 
         $this->resetAfterTest();
         $this->setAdminUser();
@@ -127,14 +126,12 @@ class tool_generator_maketestcourse_testcase extends advanced_testcase {
 
         // Check module instances belongs to section 1.
         $instances = $modinfo->get_instances_of('page');
-        $npageinstances = count($instances);
         foreach ($instances as $instance) {
             $this->assertEquals(1, $instance->sectionnum);
         }
 
         // Users that started discussions are the same.
         $forums = $modinfo->get_instances_of('forum');
-        $nforuminstances = count($forums);
         $discussions = forum_get_discussions(reset($forums), 'd.timemodified ASC');
         $lastusernumber = 0;
         $discussionstarters = array();
index a933ec4..0abf9d0 100644 (file)
@@ -58,7 +58,12 @@ if ($action == 'delete' and confirm_sesskey() and $service and empty($service->c
     }
     //The user has confirmed the deletion, delete and redirect
     $webservicemanager->delete_service($service->id);
-    add_to_log(SITEID, 'webservice', 'delete', $returnurl, get_string('deleteservice', 'webservice', $service));
+    $params = array(
+        'objectid' => $service->id
+    );
+    $event = \core\event\webservice_service_deleted::create($params);
+    $event->add_record_snapshot('external_services', $service);
+    $event->trigger();
     redirect($returnurl);
 }
 
@@ -75,7 +80,12 @@ if ($mform->is_cancelled()) {
     //create operation
     if (empty($servicedata->id)) {
         $servicedata->id = $webservicemanager->add_external_service($servicedata);
-        add_to_log(SITEID, 'webservice', 'add', $returnurl, get_string('addservice', 'webservice', $servicedata));
+        $params = array(
+            'objectid' => $servicedata->id
+        );
+        $event = \core\event\webservice_service_created::create($params);
+        $event->add_record_snapshot('external_services', $servicedata);
+        $event->trigger();
 
         //redirect to the 'add functions to service' page
         $addfunctionpage = new moodle_url(
@@ -85,7 +95,12 @@ if ($mform->is_cancelled()) {
     } else {
         //update operation
         $webservicemanager->update_external_service($servicedata);
-        add_to_log(SITEID, 'webservice', 'edit', $returnurl, get_string('editservice', 'webservice', $servicedata));
+        $params = array(
+            'objectid' => $servicedata->id
+        );
+        $event = \core\event\webservice_service_updated::create($params);
+        $event->add_record_snapshot('external_services', $servicedata);
+        $event->trigger();
     }
 
     redirect($returnurl);
index ad67c66..907a4cc 100644 (file)
@@ -57,8 +57,13 @@ if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
             $serviceuser->externalserviceid = $id;
             $serviceuser->userid = $adduser->id;
             $webservicemanager->add_ws_authorised_user($serviceuser);
-            add_to_log(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id='
-                    . $id, 'add', '', $adduser->id);
+
+            $params = array(
+                'objectid' => $serviceuser->externalserviceid,
+                'relateduserid' => $serviceuser->userid
+            );
+            $event = \core\event\webservice_service_user_added::create($params);
+            $event->trigger();
         }
         $potentialuserselector->invalidate_selected_users();
         $alloweduserselector->invalidate_selected_users();
@@ -71,8 +76,13 @@ if (optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) {
     if (!empty($userstoremove)) {
         foreach ($userstoremove as $removeuser) {
             $webservicemanager->remove_ws_authorised_user($removeuser, $id);
-            add_to_log(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id='
-                    . $id, 'remove', '', $removeuser->id);
+
+            $params = array(
+                'objectid' => $id,
+                'relateduserid' => $removeuser->id
+            );
+            $event = \core\event\webservice_service_user_removed::create($params);
+            $event->trigger();
         }
         $potentialuserselector->invalidate_selected_users();
         $alloweduserselector->invalidate_selected_users();
index a1ada38..2dc9353 100644 (file)
@@ -246,6 +246,8 @@ abstract class backup_activity_task extends backup_task {
      * Defines the common setting that any backup activity will have
      */
     protected function define_settings() {
+        global $CFG;
+        require_once($CFG->libdir.'/questionlib.php');
 
         // All the settings related to this activity will include this prefix
         $settingprefix = $this->modulename . '_' . $this->moduleid . '_';
@@ -264,6 +266,12 @@ abstract class backup_activity_task extends backup_task {
         // Look for "activities" root setting
         $activities = $this->plan->get_setting('activities');
         $activities->add_dependency($activity_included);
+
+        if (question_module_uses_questions($this->modulename)) {
+            $questionbank = $this->plan->get_setting('questionbank');
+            $questionbank->add_dependency($activity_included);
+        }
+
         // Look for "section_included" section setting (if exists)
         $settingname = 'section_' . $this->sectionid . '_included';
         if ($this->plan->setting_exists($settingname)) {
index 5d4cd9b..7e37f99 100644 (file)
@@ -90,8 +90,10 @@ class backup_course_task extends backup_task {
         // course->defaultgroupingid
         $this->add_step(new backup_annotate_groups_from_groupings('annotate_groups_from_groupings'));
 
-        // Annotate the question_categories belonging to the course context
-        $this->add_step(new backup_calculate_question_categories('course_question_categories'));
+        // Annotate the question_categories belonging to the course context (conditionally).
+        if ($this->get_setting_value('questionbank')) {
+            $this->add_step(new backup_calculate_question_categories('course_question_categories'));
+        }
 
         // Generate the roles file (optionally role assignments and always role overrides)
         $this->add_step(new backup_roles_structure_step('course_roles', 'roles.xml'));
index a4d1782..3e97f68 100644 (file)
@@ -152,5 +152,10 @@ class backup_root_task extends backup_task {
         $gradehistories->set_ui(new backup_setting_ui_checkbox($gradehistories, get_string('rootsettinggradehistories', 'backup')));
         $this->add_setting($gradehistories);
         $users->add_dependency($gradehistories);
+
+        // Define question bank inclusion setting.
+        $questionbank = new backup_generic_setting('questionbank', base_setting::IS_BOOLEAN, true);
+        $questionbank->set_ui(new backup_setting_ui_checkbox($questionbank, get_string('rootsettingquestionbank', 'backup')));
+        $this->add_setting($questionbank);
     }
 }
index 409be32..9fd95ac 100644 (file)
@@ -544,11 +544,18 @@ abstract class backup_controller_dbops extends backup_dbops {
             'backup_general_badges'             => 'badges',
             'backup_general_userscompletion'    => 'userscompletion',
             'backup_general_logs'               => 'logs',
-            'backup_general_histories'          => 'grade_histories'
+            'backup_general_histories'          => 'grade_histories',
+            'backup_general_questionbank'       => 'questionbank'
         );
         $plan = $controller->get_plan();
         foreach ($settings as $config=>$settingname) {
             $value = get_config('backup', $config);
+            if ($value === false) {
+                // Ignore this because the config has not been set. get_config
+                // returns false if a setting doesn't exist, '0' is returned when
+                // the configuration is set to false.
+                continue;
+            }
             $locked = (get_config('backup', $config.'_locked') == true);
             if ($plan->setting_exists($settingname)) {
                 $setting = $plan->get_setting($settingname);
index 5913324..97d3334 100644 (file)
@@ -386,11 +386,14 @@ abstract class backup_cron_automated_helper {
                 'badges' => 'backup_auto_badges',
                 'completion_information' => 'backup_auto_userscompletion',
                 'logs' => 'backup_auto_logs',
-                'histories' => 'backup_auto_histories'
+                'histories' => 'backup_auto_histories',
+                'questionbank' => 'backup_auto_questionbank'
             );
             foreach ($settings as $setting => $configsetting) {
                 if ($bc->get_plan()->setting_exists($setting)) {
-                    $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting});
+                    if (isset($config->{$configsetting})) {
+                        $bc->get_plan()->get_setting($setting)->set_value($config->{$configsetting});
+                    }
                 }
             }
 
index 24b8e60..0bd944e 100644 (file)
  */
 
 require_once(dirname(dirname(__FILE__)) . '/config.php');
-require_once($CFG->libdir . '/badgeslib.php');
 
 if (empty($CFG->enablebadges)) {
     print_error('badgesdisabled', 'badges');
 }
 
-$hash = required_param('b', PARAM_ALPHANUM);
+$hash = required_param('b', PARAM_ALPHANUM); // Issued badge unique hash for badge assertion.
+$action = optional_param('action', null, PARAM_BOOL); // Generates badge class if true.
 
-$badge = badges_get_issued_badge_info($hash);
+$assertion = new core_badges_assertion($hash);
+
+if (!is_null($action)) {
+    // Get badge class or issuer information depending on $action.
+    $json = ($action) ? $assertion->get_badge_class() : $assertion->get_issuer();
+} else {
+    // Otherwise, get badge assertion.
+    $json = $assertion->get_badge_assertion();
+}
 
 header('Content-type: application/json; charset=utf-8');
 
-echo json_encode($badge);
\ No newline at end of file
+echo json_encode($json);
index 339b147..c2f7a63 100644 (file)
@@ -35,8 +35,8 @@ $output = $PAGE->get_renderer('core', 'badges');
 
 $badge = new issued_badge($id);
 
-if ($bake && ($badge->recipient == $USER->id)) {
-    $name = str_replace(' ', '_', $badge->issued['badge']['name']) . '.png';
+if ($bake && ($badge->recipient->id == $USER->id)) {
+    $name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
     ob_start();
     $file = badges_bake($id, $badge->badgeid);
     header('Content-Type: image/png');
@@ -50,8 +50,8 @@ $PAGE->set_pagelayout('base');
 $PAGE->set_title(get_string('issuedbadge', 'badges'));
 
 if (isloggedin()) {
-    $PAGE->set_heading($badge->issued['badge']['name']);
-    $PAGE->navbar->add($badge->issued['badge']['name']);
+    $PAGE->set_heading($badge->badgeclass['name']);
+    $PAGE->navbar->add($badge->badgeclass['name']);
     $url = new moodle_url('/badges/mybadges.php');
     navigation_node::override_active_url($url);
 }
diff --git a/badges/classes/assertion.php b/badges/classes/assertion.php
new file mode 100644 (file)
index 0000000..ba0efb5
--- /dev/null
@@ -0,0 +1,158 @@
+<?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/>.
+
+/**
+ * Badge assertion library.
+ *
+ * @package    core
+ * @subpackage badges
+ * @copyright  2012 onwards Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @author     Yuliya Bozhko <yuliya.bozhko@totaralms.com>
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Open Badges Assertions specification 1.0 {@link https://github.com/mozilla/openbadges/wiki/Assertions}
+ *
+ * Badge asserion is defined by three parts:
+ * - Badge Assertion (information regarding a specific badge that was awarded to a badge earner)
+ * - Badge Class (general information about a badge and what it is intended to represent)
+ * - Issuer Class (general information of an issuing organisation)
+ */
+
+/**
+ * Class that represents badge assertion.
+ *
+ */
+class core_badges_assertion {
+    /** @var object Issued badge information from database */
+    private $_data;
+
+    /** @var moodle_url Issued badge url */
+    private $_url;
+
+    /**
+     * Constructs with issued badge unique hash.
+     *
+     * @param string $hash Badge unique hash from badge_issued table.
+     */
+    public function __construct($hash) {
+        global $DB;
+
+        $this->_data = $DB->get_record_sql('
+            SELECT
+                bi.dateissued,
+                bi.dateexpire,
+                bi.uniquehash,
+                u.email,
+                b.*,
+                bb.email as backpackemail
+            FROM
+                {badge} b
+                JOIN {badge_issued} bi
+                    ON b.id = bi.badgeid
+                JOIN {user} u
+                    ON u.id = bi.userid
+                LEFT JOIN {badge_backpack} bb
+                    ON bb.userid = bi.userid
+            WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
+            array('hash' => $hash), IGNORE_MISSING);
+
+        $this->_url = new moodle_url('/badges/badge.php', array('hash' => $this->_data->uniquehash));
+    }
+
+    /**
+     * Get badge assertion.
+     *
+     * @return array Badge assertion.
+     */
+    public function get_badge_assertion() {
+        global $CFG;
+        $assertion = array();
+        if ($this->_data) {
+            $hash = $this->_data->uniquehash;
+            $email = empty($this->_data->backpackemail) ? $this->_data->email : $this->_data->backpackemail;
+            $assertionurl = new moodle_url('/badges/assertion.php', array('b' => $hash));
+            $classurl = new moodle_url('/badges/assertion.php', array('b' => $hash, 'action' => 1));
+
+            // Required.
+            $assertion['uid'] = $hash;
+            $assertion['recipient'] = array();
+            $assertion['recipient']['identity'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
+            $assertion['recipient']['type'] = 'email'; // Currently the only supported type.
+            $assertion['recipient']['hashed'] = true; // We are always hashing recipient.
+            $assertion['recipient']['salt'] = $CFG->badges_badgesalt;
+            $assertion['badge'] = $classurl->out(false);
+            $assertion['verify'] = array();
+            $assertion['verify']['type'] = 'hosted'; // 'Signed' is not implemented yet.
+            $assertion['verify']['url'] = $assertionurl->out(false);
+            $assertion['issuedOn'] = $this->_data->dateissued;
+            // Optional.
+            $assertion['evidence'] = $this->_url->out(false); // Currently issued badge URL.
+            if (!empty($this->_data->dateexpire)) {
+                $assertion['expires'] = $this->_data->dateexpire;
+            }
+        }
+        return $assertion;
+    }
+
+    /**
+     * Get badge class information.
+     *
+     * @return array Badge Class information.
+     */
+    public function get_badge_class() {
+        $class = array();
+        if ($this->_data) {
+            if (empty($this->_data->courseid)) {
+                $context = context_system::instance();
+            } else {
+                $context = context_course::instance($this->_data->courseid);
+            }
+            $issuerurl = new moodle_url('/badges/assertion.php', array('b' => $this->_data->uniquehash, 'action' => 0));
+
+            // Required.
+            $class['name'] = $this->_data->name;
+            $class['description'] = $this->_data->description;
+            $class['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->_data->id, '/', 'f1')->out(false);
+            $class['criteria'] = $this->_url->out(false); // Currently issued badge URL.
+            $class['issuer'] = $issuerurl->out(false);
+        }
+        return $class;
+    }
+
+    /**
+     * Get badge issuer information.
+     *
+     * @return array Issuer information.
+     */
+    public function get_issuer() {
+        $issuer = array();
+        if ($this->_data) {
+            // Required.
+            $issuer['name'] = $this->_data->issuername;
+            $issuer['url'] = $this->_data->issuerurl;
+            // Optional.
+            if (!empty($this->_data->issuercontact)) {
+                $issuer['email'] = $this->_data->issuercontact;
+            }
+        }
+        return $issuer;
+    }
+
+}
index 8a0c45e..9507bfa 100644 (file)
@@ -120,25 +120,11 @@ class PNG_MetaDataHandler
             debugging('Key is too big');
         }
 
-        if ($type == 'iTXt') {
-            // iTXt International textual data.
-            // Keyword:             1-79 bytes (character string)
-            // Null separator:      1 byte
-            // Compression flag:    1 byte
-            // Compression method:  1 byte
-            // Language tag:        0 or more bytes (character string)
-            // Null separator:      1 byte
-            // Translated keyword:  0 or more bytes
-            // Null separator:      1 byte
-            // Text:                0 or more bytes
-            $data = $key . "\000'json'\0''\0\"{'method': 'hosted', 'assertionUrl': '" . $value . "'}\"";
-        } else {
-            // tEXt Textual data.
-            // Keyword:        1-79 bytes (character string)
-            // Null separator: 1 byte
-            // Text:           n bytes (character string)
-            $data = $key . "\0" . $value;
-        }
+        // tEXt Textual data.
+        // Keyword:        1-79 bytes (character string)
+        // Null separator: 1 byte
+        // Text:           n bytes (character string)
+        $data = $key . "\0" . $value;
         $crc = pack("N", crc32($type . $data));
         $len = pack("N", strlen($data));
 
index de4894e..b3fa735 100644 (file)
@@ -282,24 +282,24 @@ class core_badges_renderer extends plugin_renderer_base {
         global $USER, $CFG, $DB;
         $issued = $ibadge->issued;
         $userinfo = $ibadge->recipient;
+        $badgeclass = $ibadge->badgeclass;
         $badge = new badge($ibadge->badgeid);
-        $today_date = date('Y-m-d');
-        $today = strtotime($today_date);
+        $now = time();
 
         $table = new html_table();
         $table->id = 'issued-badge-table';
 
         $imagetable = new html_table();
         $imagetable->attributes = array('class' => 'clearfix badgeissuedimage');
-        $imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $issued['badge']['image'])));
+        $imagetable->data[] = array(html_writer::empty_tag('img', array('src' => $badgeclass['image'])));
         if ($USER->id == $userinfo->id && !empty($CFG->enablebadges)) {
             $imagetable->data[] = array($this->output->single_button(
-                        new moodle_url('/badges/badge.php', array('hash' => $ibadge->hash, 'bake' => true)),
+                        new moodle_url('/badges/badge.php', array('hash' => $issued['uid'], 'bake' => true)),
                         get_string('download'),
                         'POST'));
-            $expiration = isset($issued['expires']) ? strtotime($issued['expires']) : $today + 1;
-            if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $today) && badges_user_has_backpack($USER->id)) {
-                $assertion = new moodle_url('/badges/assertion.php', array('b' => $ibadge->hash));
+            $expiration = isset($issued['expires']) ? $issued['expires'] : $now + 86400;
+            if (!empty($CFG->badges_allowexternalbackpack) && ($expiration > $now) && badges_user_has_backpack($USER->id)) {
+                $assertion = new moodle_url('/badges/assertion.php', array('b' => $issued['uid']));
                 $action = new component_action('click', 'addtobackpack', array('assertion' => $assertion->out(false)));
                 $attributes = array(
                         'type'  => 'button',
@@ -339,24 +339,23 @@ class core_badges_renderer extends plugin_renderer_base {
 
         $datatable->data[] = array(get_string('bcriteria', 'badges'), self::print_badge_criteria($badge));
         $datatable->data[] = array($this->output->heading(get_string('issuancedetails', 'badges'), 3), '');
-        $datatable->data[] = array(get_string('dateawarded', 'badges'), $issued['issued_on']);
+        $datatable->data[] = array(get_string('dateawarded', 'badges'), userdate($issued['issuedOn']));
         if (isset($issued['expires'])) {
-            $expiration = strtotime($issued['expires']);
-            if ($expiration < $today) {
-                $cell = new html_table_cell($issued['expires'] . get_string('warnexpired', 'badges'));
+            if ($issued['expires'] < $now) {
+                $cell = new html_table_cell(userdate($issued['expires']) . get_string('warnexpired', 'badges'));
                 $cell->attributes = array('class' => 'notifyproblem warning');
                 $datatable->data[] = array(get_string('expirydate', 'badges'), $cell);
 
                 $image = html_writer::start_tag('div', array('class' => 'badge'));
-                $image .= html_writer::empty_tag('img', array('src' => $issued['badge']['image']));
+                $image .= html_writer::empty_tag('img', array('src' => $badgeclass['image']));
                 $image .= $this->output->pix_icon('i/expired',
-                                get_string('expireddate', 'badges', $issued['expires']),
+                                get_string('expireddate', 'badges', userdate($issued['expires'])),
                                 'moodle',
                                 array('class' => 'expireimage'));
                 $image .= html_writer::end_tag('div');
                 $imagetable->data[0] = array($image);
             } else {
-                $datatable->data[] = array(get_string('expirydate', 'badges'), $issued['expires']);
+                $datatable->data[] = array(get_string('expirydate', 'badges'), userdate($issued['expires']));
             }
         }
 
@@ -909,15 +908,15 @@ class issued_badge implements renderable {
     /** @var badge recipient */
     public $recipient;
 
+    /** @var badge class */
+    public $badgeclass;
+
     /** @var badge visibility to others */
     public $visible = 0;
 
     /** @var badge class */
     public $badgeid = 0;
 
-    /** @var issued badge unique hash */
-    public $hash = "";
-
     /**
      * Initializes the badge to display
      *
@@ -925,8 +924,10 @@ class issued_badge implements renderable {
      */
     public function __construct($hash) {
         global $DB;
-        $this->issued = badges_get_issued_badge_info($hash);
-        $this->hash = $hash;
+
+        $assertion = new core_badges_assertion($hash);
+        $this->issued = $assertion->get_badge_assertion();
+        $this->badgeclass = $assertion->get_badge_class();
 
         $rec = $DB->get_record_sql('SELECT userid, visible, badgeid
                 FROM {badge_issued}
index a3b2878..a571569 100644 (file)
@@ -35,6 +35,7 @@ class core_badgeslib_testcase extends advanced_testcase {
     protected $user;
     protected $module;
     protected $coursebadge;
+    protected $assertion;
 
     protected function setUp() {
         global $DB, $CFG;
@@ -52,6 +53,7 @@ class core_badgeslib_testcase extends advanced_testcase {
         $fordb->usermodified = $user->id;
         $fordb->issuername = "Test issuer";
         $fordb->issuerurl = "http://issuer-url.domain.co.nz";
+        $fordb->issuercontact = "issuer@example.com";
         $fordb->expiredate = null;
         $fordb->expireperiod = null;
         $fordb->type = BADGE_TYPE_SITE;
@@ -86,6 +88,10 @@ class core_badgeslib_testcase extends advanced_testcase {
         $fordb->status = BADGE_STATUS_ACTIVE;
 
         $this->coursebadge = $DB->insert_record('badge', $fordb, true);
+        $this->assertion = new stdClass();
+        $this->assertion->badge = '{"uid":"%s","recipient":{"identity":"%s","type":"email","hashed":true,"salt":"%s"},"badge":"%s","verify":{"type":"hosted","url":"%s"},"issuedOn":"%d","evidence":"%s"}';
+        $this->assertion->class = '{"name":"%s","description":"%s","image":"%s","criteria":"%s","issuer":"%s"}';
+        $this->assertion->issuer = '{"name":"%s","url":"%s","email":"%s"}';
     }
 
     public function test_create_badge() {
@@ -103,6 +109,7 @@ class core_badgeslib_testcase extends advanced_testcase {
         $this->assertEquals($badge->description, $cloned_badge->description);
         $this->assertEquals($badge->issuercontact, $cloned_badge->issuercontact);
         $this->assertEquals($badge->issuername, $cloned_badge->issuername);
+        $this->assertEquals($badge->issuercontact, $cloned_badge->issuercontact);
         $this->assertEquals($badge->issuerurl, $cloned_badge->issuerurl);
         $this->assertEquals($badge->expiredate, $cloned_badge->expiredate);
         $this->assertEquals($badge->expireperiod, $cloned_badge->expireperiod);
@@ -279,4 +286,34 @@ class core_badgeslib_testcase extends advanced_testcase {
         $this->assertDebuggingCalled('Error baking badge image!');
         $this->assertTrue($badge->is_issued($this->user->id));
     }
+
+    /**
+     * Test badges assertion generated when a badge is issued.
+     */
+    public function test_badges_assertion() {
+        $badge = new badge($this->coursebadge);
+        $this->assertFalse($badge->is_issued($this->user->id));
+
+        $criteria_overall = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_OVERALL, 'badgeid' => $badge->id));
+        $criteria_overall->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ANY));
+        $criteria_overall1 = award_criteria::build(array('criteriatype' => BADGE_CRITERIA_TYPE_PROFILE, 'badgeid' => $badge->id));
+        $criteria_overall1->save(array('agg' => BADGE_CRITERIA_AGGREGATION_ALL, 'field_address' => 'address'));
+
+        $this->user->address = 'Test address';
+        user_update_user($this->user, false);
+        // Check if badge is awarded.
+        $this->assertDebuggingCalled('Error baking badge image!');
+        $awards = $badge->get_awards();
+        $this->assertCount(1, $awards);
+
+        // Get assertion.
+        $award = reset($awards);
+        $assertion = new core_badges_assertion($award->uniquehash);
+        $testassertion = $this->assertion;
+
+        // Make sure JSON strings have the same structure.
+        $this->assertStringMatchesFormat($testassertion->badge, json_encode($assertion->get_badge_assertion()));
+        $this->assertStringMatchesFormat($testassertion->class, json_encode($assertion->get_badge_class()));
+        $this->assertStringMatchesFormat($testassertion->issuer, json_encode($assertion->get_issuer()));
+    }
 }
index de35644..aaf6570 100644 (file)
@@ -40,7 +40,7 @@ class block_course_list extends block_list {
                     $coursecontext = context_course::instance($course->id);
                     $linkcss = $course->visible ? "" : " class=\"dimmed\" ";
                     $this->content->items[]="<a $linkcss title=\"" . format_string($course->shortname, true, array('context' => $coursecontext)) . "\" ".
-                               "href=\"$CFG->wwwroot/course/view.php?id=$course->id\">".$icon.format_string($course->fullname). "</a>";
+                               "href=\"$CFG->wwwroot/course/view.php?id=$course->id\">".$icon.format_string(get_course_display_name_for_list($course)). "</a>";
                 }
                 $this->title = get_string('mycourses');
             /// If we can update any course of the view all isn't hidden, show the view all courses link
@@ -79,7 +79,7 @@ class block_course_list extends block_list {
                         $this->content->items[]="<a $linkcss title=\""
                                    . format_string($course->shortname, true, array('context' => $coursecontext))."\" ".
                                    "href=\"$CFG->wwwroot/course/view.php?id=$course->id\">"
-                                   .$icon. format_string($course->fullname, true, array('context' => context_course::instance($course->id))) . "</a>";
+                                   .$icon. format_string(get_course_display_name_for_list($course), true, array('context' => context_course::instance($course->id))) . "</a>";
                     }
                 /// If we can update any course of the view all isn't hidden, show the view all courses link
                     if (has_capability('moodle/course:update', context_system::instance()) || empty($CFG->block_course_list_hideallcourseslink)) {
@@ -124,7 +124,7 @@ class block_course_list extends block_list {
                 $coursecontext = context_course::instance($course->id);
                 $this->content->items[]="<a title=\"" . format_string($course->shortname, true, array('context' => $coursecontext)) . "\" ".
                     "href=\"{$CFG->wwwroot}/auth/mnet/jump.php?hostid={$course->hostid}&amp;wantsurl=/course/view.php?id={$course->remoteid}\">"
-                    .$icon. format_string($course->fullname) . "</a>";
+                    .$icon. format_string(get_course_display_name_for_list($course)) . "</a>";
             }
             // if we listed courses, we are done
             return true;
index 6f33899..8db1e34 100644 (file)
@@ -108,7 +108,7 @@ class block_course_overview_renderer extends plugin_renderer_base {
                     $attributes['class'] = 'dimmed';
                 }
                 $courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
-                $coursefullname = format_string($course->fullname, true, $course->id);
+                $coursefullname = format_string(get_course_display_name_for_list($course), true, $course->id);
                 $link = html_writer::link($courseurl, $coursefullname, $attributes);
                 $html .= $this->output->heading($link, 2, 'title');
             } else {
index 302bc17..281fa30 100644 (file)
@@ -824,7 +824,7 @@ class cache_definition {
             // Request caches should never use static acceleration - it just doesn't make sense.
             return false;
         }
-        return $this->staticacceleration || $this->mode === cache_store::MODE_SESSION;
+        return $this->staticacceleration;
     }
 
     /**
index 13c3017..55ee3ad 100644 (file)
@@ -113,7 +113,12 @@ class cachestore_dummy extends cache_store {
         //     store things in its static array.
         //   - If the definition is not using static acceleration then the cache loader won't try to store anything
         //     and we will need to store it here in order to make sure it is accessible.
-        $this->persist = !$definition->use_static_acceleration();
+        if ($definition->get_mode() !== self::MODE_APPLICATION) {
+            // Neither the request cache nor the session cache provide static acceleration.
+            $this->persist = true;
+        } else {
+            $this->persist = !$definition->use_static_acceleration();
+        }
     }
 
     /**
index 6295f55..2267afa 100644 (file)
@@ -1058,28 +1058,63 @@ class core_cache_testcase extends advanced_testcase {
      */
     public function test_disable_stores() {
         $instance = cache_config_phpunittest::instance();
-        $instance->phpunit_add_definition('phpunit/disabletest', array(
+        $instance->phpunit_add_definition('phpunit/disabletest1', array(
             'mode' => cache_store::MODE_APPLICATION,
             'component' => 'phpunit',
-            'area' => 'disabletest'
+            'area' => 'disabletest1'
+        ));
+        $instance->phpunit_add_definition('phpunit/disabletest2', array(
+            'mode' => cache_store::MODE_SESSION,
+            'component' => 'phpunit',
+            'area' => 'disabletest2'
+        ));
+        $instance->phpunit_add_definition('phpunit/disabletest3', array(
+            'mode' => cache_store::MODE_REQUEST,
+            'component' => 'phpunit',
+            'area' => 'disabletest3'
         ));
-        $cache = cache::make('phpunit', 'disabletest');
-        $this->assertInstanceOf('cache_phpunit_application', $cache);
-        $this->assertEquals('cachestore_file', $cache->phpunit_get_store_class());
 
-        $this->assertFalse($cache->get('test'));
-        $this->assertTrue($cache->set('test', 'test'));
-        $this->assertEquals('test', $cache->get('test'));
+        $caches = array(
+            'disabletest1' => cache::make('phpunit', 'disabletest1'),
+            'disabletest2' => cache::make('phpunit', 'disabletest2'),
+            'disabletest3' => cache::make('phpunit', 'disabletest3')
+        );
+
+        $this->assertInstanceOf('cache_phpunit_application', $caches['disabletest1']);
+        $this->assertInstanceOf('cache_phpunit_session', $caches['disabletest2']);
+        $this->assertInstanceOf('cache_phpunit_request', $caches['disabletest3']);
+
+        $this->assertEquals('cachestore_file', $caches['disabletest1']->phpunit_get_store_class());
+        $this->assertEquals('cachestore_session', $caches['disabletest2']->phpunit_get_store_class());
+        $this->assertEquals('cachestore_static', $caches['disabletest3']->phpunit_get_store_class());
+
+        foreach ($caches as $cache) {
+            $this->assertFalse($cache->get('test'));
+            $this->assertTrue($cache->set('test', 'test'));
+            $this->assertEquals('test', $cache->get('test'));
+        }
 
         cache_factory::disable_stores();
 
-        $cache = cache::make('phpunit', 'disabletest');
-        $this->assertInstanceOf('cache_phpunit_application', $cache);
-        $this->assertEquals('cachestore_dummy', $cache->phpunit_get_store_class());
+        $caches = array(
+            'disabletest1' => cache::make('phpunit', 'disabletest1'),
+            'disabletest2' => cache::make('phpunit', 'disabletest2'),
+            'disabletest3' => cache::make('phpunit', 'disabletest3')
+        );
 
-        $this->assertFalse($cache->get('test'));
-        $this->assertTrue($cache->set('test', 'test'));
-        $this->assertEquals('test', $cache->get('test'));
+        $this->assertInstanceOf('cache_phpunit_application', $caches['disabletest1']);
+        $this->assertInstanceOf('cache_phpunit_session', $caches['disabletest2']);
+        $this->assertInstanceOf('cache_phpunit_request', $caches['disabletest3']);
+
+        $this->assertEquals('cachestore_dummy', $caches['disabletest1']->phpunit_get_store_class());
+        $this->assertEquals('cachestore_dummy', $caches['disabletest2']->phpunit_get_store_class());
+        $this->assertEquals('cachestore_dummy', $caches['disabletest3']->phpunit_get_store_class());
+
+        foreach ($caches as $cache) {
+            $this->assertFalse($cache->get('test'));
+            $this->assertTrue($cache->set('test', 'test'));
+            $this->assertEquals('test', $cache->get('test'));
+        }
     }
 
     /**
@@ -1108,6 +1143,7 @@ class core_cache_testcase extends advanced_testcase {
         $cache = cache::make('phpunit', 'disable');
         $this->assertInstanceOf('cache_disabled', $cache);
 
+        // Test an application cache.
         $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable');
         $this->assertInstanceOf('cache_disabled', $cache);
 
@@ -1118,6 +1154,28 @@ class core_cache_testcase extends advanced_testcase {
         $this->assertFalse($cache->delete('test'));
         $this->assertTrue($cache->purge());
 
+        // Test a session cache.
+        $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'disable');
+        $this->assertInstanceOf('cache_disabled', $cache);
+
+        $this->assertFalse(file_exists($configfile));
+
+        $this->assertFalse($cache->get('test'));
+        $this->assertFalse($cache->set('test', 'test'));
+        $this->assertFalse($cache->delete('test'));
+        $this->assertTrue($cache->purge());
+
+        // Finally test a request cache.
+        $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'disable');
+        $this->assertInstanceOf('cache_disabled', $cache);
+
+        $this->assertFalse(file_exists($configfile));
+
+        $this->assertFalse($cache->get('test'));
+        $this->assertFalse($cache->set('test', 'test'));
+        $this->assertFalse($cache->delete('test'));
+        $this->assertTrue($cache->purge());
+
         cache_factory::reset();
 
         $factory = cache_factory::instance(true);
index 1426271..d6a7981 100644 (file)
@@ -106,8 +106,8 @@ $PAGE->navbar->add($pagetitle);
 
 $PAGE->set_title($course->shortname.': '.get_string('calendar', 'calendar').': '.$pagetitle);
 $PAGE->set_heading($course->fullname);
+$PAGE->set_pagelayout('standard');
 $PAGE->set_button(calendar_preferences_button($course));
-$PAGE->set_pagelayout('base');
 
 $renderer = $PAGE->get_renderer('core_calendar');
 $calendar->add_sidecalendar_blocks($renderer);
@@ -125,7 +125,7 @@ switch($action) {
             $weekend = intval($CFG->calendar_weekend);
         }
 
-        $authtoken = sha1($USER->id . $USER->password . $CFG->calendar_exportsalt);
+        $authtoken = sha1($USER->id . $DB->get_field('user', 'password', array('id'=>$USER->id)). $CFG->calendar_exportsalt);
         // Let's populate some vars to let "common tasks" be somewhat smart...
         // If today it's weekend, give the "next week" option
         $allownextweek  = $weekend & (1 << $now['wday']);
index 40a2d82..73e5924 100644 (file)
@@ -153,7 +153,7 @@ if (!empty($CFG->enablecalendarexport)) {
         echo $OUTPUT->single_button(new moodle_url('/calendar/managesubscriptions.php', array('course'=>$courseid)), get_string('managesubscriptions', 'calendar'));
     }
     if (isloggedin()) {
-        $authtoken = sha1($USER->id . $USER->password . $CFG->calendar_exportsalt);
+        $authtoken = sha1($USER->id . $DB->get_field('user', 'password', array('id'=>$USER->id)) . $CFG->calendar_exportsalt);
         $link = new moodle_url('/calendar/export_execute.php', array('preset_what'=>'all', 'preset_time'=>'recentupcoming', 'userid' => $USER->id, 'authtoken'=>$authtoken));
         $icon = html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('i/ical'), 'height'=>'14', 'width'=>'36', 'alt'=>get_string('ical', 'calendar'), 'title'=>get_string('quickdownloadcalendar', 'calendar')));
         echo html_writer::tag('a', $icon, array('href'=>$link));
index 84017f7..1980441 100644 (file)
@@ -250,6 +250,24 @@ abstract class format_base {
         return $this->course;
     }
 
+    /**
+     * Returns true if the course has a front page.
+     *
+     * This function is called to determine if the course has a view page, whether or not
+     * it contains a listing of activities. It can be useful to set this to false when the course
+     * format has only one activity and ignores the course page. Or if there are multiple
+     * activities but no page to see the centralised information.
+     *
+     * Initially this was created to know if forms should add a button to return to the course page.
+     * So if 'Return to course' does not make sense in your format your should probably return false.
+     *
+     * @return boolean
+     * @since 2.6
+     */
+    public function has_view_page() {
+        return true;
+    }
+
     /**
      * Returns true if this course format uses sections
      *
index ac921dc..5edf309 100644 (file)
@@ -651,7 +651,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
         // Title with section navigation links.
         $sectionnavlinks = $this->get_nav_links($course, $modinfo->get_section_info_all(), $displaysection);
         $sectiontitle = '';
-        $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation header headingblock'));
+        $sectiontitle .= html_writer::start_tag('div', array('class' => 'section-navigation navigationtitle'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['previous'], array('class' => 'mdl-left'));
         $sectiontitle .= html_writer::tag('span', $sectionnavlinks['next'], array('class' => 'mdl-right'));
         // Title attributes
index 02795d1..396e4d6 100644 (file)
@@ -154,7 +154,7 @@ class format_singleactivity extends format_base {
             );
         }
         if ($foreditform && !isset($courseformatoptions['activitytype']['label'])) {
-            $availabletypes = get_module_types_names();
+            $availabletypes = $this->get_supported_activities();
             $courseformatoptionsedit = array(
                 'activitytype' => array(
                     'label' => new lang_string('activitytype', 'format_singleactivity'),
@@ -270,7 +270,7 @@ class format_singleactivity extends format_base {
      */
     protected function get_activitytype() {
         $options = $this->get_format_options();
-        $availabletypes = get_module_types_names();
+        $availabletypes = $this->get_supported_activities();
         if (!empty($options['activitytype']) &&
                 array_key_exists($options['activitytype'], $availabletypes)) {
             return $options['activitytype'];
@@ -291,6 +291,23 @@ class format_singleactivity extends format_base {
         return $this->activity;
     }
 
+    /**
+     * Get the activities supported by the format.
+     *
+     * Here we ignore the modules that do not have a page of their own, like the label.
+     *
+     * @return array array($module => $name of the module).
+     */
+    public static function get_supported_activities() {
+        $availabletypes = get_module_types_names();
+        foreach ($availabletypes as $module => $name) {
+            if (plugin_supports('mod', $module, FEATURE_NO_VIEW_LINK, false)) {
+                unset($availabletypes[$module]);
+            }
+        }
+        return $availabletypes;
+    }
+
     /**
      * Checks if the current user can add the activity of the specified type to this course.
      *
@@ -440,4 +457,14 @@ class format_singleactivity extends format_base {
             $activitynode->remove();
         }
     }
+
+    /**
+     * Returns true if the course has a front page.
+     *
+     * @return boolean false
+     */
+    public function has_view_page() {
+        return false;
+    }
+
 }
index 75494fc..4846c58 100644 (file)
@@ -42,11 +42,11 @@ class format_singleactivity_admin_setting_activitytype extends admin_setting_con
      */
     public function load_choices() {
         global $CFG;
-        require_once($CFG->dirroot. '/course/lib.php');
+        require_once($CFG->dirroot. '/course/format/singleactivity/lib.php');
         if (is_array($this->choices)) {
             return true;
         }
-        $this->choices = get_module_types_names();
+        $this->choices = format_singleactivity::get_supported_activities();
         return true;
     }
 }
index 65f7875..f717984 100644 (file)
@@ -1,10 +1 @@
-/* Hide confusing form elements "Display description on course page" and
-"Save and return to course" from module edit form because they
-are not applicable in single activity course format */
-body.format-singleactivity.path-mod.pagelayout-admin form.mform #fitem_id_showdescription,
-body.format-singleactivity.path-mod.pagelayout-admin form.mform .fitem_actionbuttons#fgroup_id_buttonar #id_submitbutton {display:none;}
-
-/* In mod_quiz hide "Back to course" button */
-body.format-singleactivity.path-mod-quiz .quizattempt .continuebutton {display:none;}
-
-body.format-singleactivity .tree_item.orphaned a {color:red;}
+.format-singleactivity .tree_item.orphaned a {color:red;}
index 19988e2..e331e2f 100644 (file)
@@ -1289,8 +1289,11 @@ function get_module_metadata($course, $modnames, $sectionreturn = null) {
 
         // NOTE: this is legacy stuff, module subtypes are very strongly discouraged!!
         $gettypesfunc =  $modname.'_get_types';
+        $types = MOD_SUBTYPE_NO_CHILDREN;
         if (function_exists($gettypesfunc)) {
             $types = $gettypesfunc();
+        }
+        if ($types !== MOD_SUBTYPE_NO_CHILDREN) {
             if (is_array($types) && count($types) > 0) {
                 $group = new stdClass();
                 $group->name = $modname;
@@ -1314,7 +1317,9 @@ function get_module_metadata($course, $modnames, $sectionreturn = null) {
                     // should have the same archetype
                     $group->archetype = $subtype->archetype;
 
-                    if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
+                    if (!empty($type->help)) {
+                        $subtype->help = $type->help;
+                    } else if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
                         $subtype->help = get_string('help' . $subtype->name, $modname);
                     }
                     $subtype->link = new moodle_url($urlbase, array('add' => $modname, 'type' => $subtype->name));
index db70a9f..bf088d8 100644 (file)
@@ -56,6 +56,11 @@ if (!empty($add)) {
     $course = $DB->get_record('course', array('id'=>$course), '*', MUST_EXIST);
     require_login($course);
 
+    // There is no page for this in the navigation. The closest we'll have is the course section.
+    // If the course section isn't displayed on the navigation this will fall back to the course which
+    // will be the closest match we have.
+    navigation_node::override_active_url(course_get_url($course, $section));
+
     list($module, $context, $cw) = can_add_moduleinfo($course, $add, $section);
 
     $cm = null;
@@ -114,6 +119,7 @@ if (!empty($add)) {
     } else {
         $pageheading = get_string('addinganew', 'moodle', $fullmodulename);
     }
+    $navbaraddition = $pageheading;
 
 } else if (!empty($update)) {
 
@@ -220,6 +226,7 @@ if (!empty($add)) {
     } else {
         $pageheading = get_string('updatinga', 'moodle', $fullmodulename);
     }
+    $navbaraddition = null;
 
 } else {
     require_login();
@@ -290,6 +297,11 @@ if ($mform->is_cancelled()) {
     $PAGE->set_heading($course->fullname);
     $PAGE->set_title($streditinga);
     $PAGE->set_cacheable(false);
+
+    if (isset($navbaraddition)) {
+        $PAGE->navbar->add($navbaraddition);
+    }
+
     echo $OUTPUT->header();
 
     if (get_string_manager()->string_exists('modulename_help', $module->name)) {
index 5b489bf..b398be4 100644 (file)
@@ -55,7 +55,12 @@ abstract class moodleform_mod extends moodleform {
      */
     protected $applyadminlockedflags = false;
 
+    /** @var object The course format of the current course. */
+    protected $courseformat;
+
     function moodleform_mod($current, $section, $cm, $course) {
+        global $CFG;
+
         $this->current   = $current;
         $this->_instance = $current->instance;
         $this->_section  = $section;
@@ -66,6 +71,10 @@ abstract class moodleform_mod extends moodleform {
             $this->context = context_course::instance($course->id);
         }
 
+        // Set the course format.
+        require_once($CFG->dirroot . '/course/format/lib.php');
+        $this->courseformat = course_get_format($course);
+
         // Guess module name
         $matches = array();
         if (!preg_match('/^mod_([^_]+)_mod_form$/', get_class($this), $matches)) {
@@ -831,9 +840,9 @@ abstract class moodleform_mod extends moodleform {
             $mform->addRule('introeditor', get_string('required'), 'required', null, 'client');
         }
 
-        // If the 'show description' feature is enabled, this checkbox appears
-        // below the intro.
-        if ($this->_features->showdescription) {
+        // If the 'show description' feature is enabled, this checkbox appears below the intro.
+        // We want to hide that when using the singleactivity course format because it is confusing.
+        if ($this->_features->showdescription  && $this->courseformat->has_view_page()) {
             $mform->addElement('checkbox', 'showdescription', get_string('showdescription'));
             $mform->addHelpButton('showdescription', 'showdescription');
         }
@@ -861,7 +870,9 @@ abstract class moodleform_mod extends moodleform {
         // elements in a row need a group
         $buttonarray = array();
 
-        if ($submit2label !== false) {
+        // Label for the submit button to return to the course.
+        // Ignore this button in single activity format because it is confusing.
+        if ($submit2label !== false && $this->courseformat->has_view_page()) {
             $buttonarray[] = &$mform->createElement('submit', 'submitbutton2', $submit2label);
         }
 
index 659383a..7f7d789 100644 (file)
@@ -674,7 +674,8 @@ class core_course_renderer extends plugin_renderer_base {
             $conditionalhidden = $mod->availablefrom > time() ||
                 ($mod->availableuntil && $mod->availableuntil < time()) ||
                 count($mod->conditionsgrade) > 0 ||
-                count($mod->conditionscompletion) > 0;
+                count($mod->conditionscompletion) > 0 ||
+                count($mod->conditionsfield);
         }
         return $conditionalhidden;
     }
@@ -725,25 +726,27 @@ class core_course_renderer extends plugin_renderer_base {
         // viewhiddenactivities, so that teachers see 'items which might not
         // be available to some students' dimmed but students do not see 'item
         // which is actually available to current student' dimmed.
-        $conditionalhidden = $this->is_cm_conditionally_hidden($mod);
-        $accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
-                (!$mod->uservisible || has_capability('moodle/course:viewhiddenactivities',
-                        context_course::instance($mod->course)));
-
         $linkclasses = '';
         $accesstext = '';
         $textclasses = '';
-        if ($accessiblebutdim) {
-            $linkclasses .= ' dimmed';
-            $textclasses .= ' dimmed_text';
-            if ($conditionalhidden) {
-                $linkclasses .= ' conditionalhidden';
-                $textclasses .= ' conditionalhidden';
-            }
-            if ($mod->uservisible) {
-                // show accessibility note only if user can access the module himself
+        if ($mod->uservisible) {
+            $conditionalhidden = $this->is_cm_conditionally_hidden($mod);
+            $accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
+                has_capability('moodle/course:viewhiddenactivities',
+                        context_course::instance($mod->course));
+            if ($accessiblebutdim) {
+                $linkclasses .= ' dimmed';
+                $textclasses .= ' dimmed_text';
+                if ($conditionalhidden) {
+                    $linkclasses .= ' conditionalhidden';
+                    $textclasses .= ' conditionalhidden';
+                }
+                // Show accessibility note only if user can access the module himself.
                 $accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
             }
+        } else {
+            $linkclasses .= ' dimmed';
+            $textclasses .= ' dimmed_text';
         }
 
         // Get on-click attribute value if specified and decode the onclick - it
@@ -788,28 +791,23 @@ class core_course_renderer extends plugin_renderer_base {
             return $output;
         }
         $content = $mod->get_formatted_content(array('overflowdiv' => true, 'noclean' => true));
-        if ($this->page->user_is_editing()) {
-            // In editing mode, when an item is conditionally hidden from some users
-            // we show it as greyed out.
+        $accesstext = '';
+        $textclasses = '';
+        if ($mod->uservisible) {
             $conditionalhidden = $this->is_cm_conditionally_hidden($mod);
-            $dim = !$mod->visible || $conditionalhidden;
+            $accessiblebutdim = (!$mod->visible || $conditionalhidden) &&
+                has_capability('moodle/course:viewhiddenactivities',
+                        context_course::instance($mod->course));
+            if ($accessiblebutdim) {
+                $textclasses .= ' dimmed_text';
+                if ($conditionalhidden) {
+                    $textclasses .= ' conditionalhidden';
+                }
+                // Show accessibility note only if user can access the module himself.
+                $accesstext = get_accesshide(get_string('hiddenfromstudents').':'. $mod->modfullname);
+            }
         } else {
-            // When not in editing mode, we only show item as hidden if it is
-            // actually not available to the user
-            $conditionalhidden = false;
-            $dim = !$mod->uservisible;
-        }
-        $textclasses = '';
-        $accesstext = '';
-        if ($dim) {
             $textclasses .= ' dimmed_text';
-            if ($conditionalhidden) {
-                $textclasses .= ' conditionalhidden';
-            }
-            if ($mod->uservisible) {
-                // show accessibility note only if user can access the module himself
-                $accesstext = get_accesshide(get_string('hiddenfromstudents').': ');
-            }
         }
         if ($mod->get_url()) {
             if ($content) {
@@ -1149,9 +1147,8 @@ class core_course_renderer extends plugin_renderer_base {
         // course name
         $coursename = $chelper->get_course_formatted_name($course);
         $coursenamelink = html_writer::link(new moodle_url('/course/view.php', array('id' => $course->id)),
-                $coursename, array('class' => $course->visible ? '' : 'dimmed'));
-        $content .= html_writer::tag($nametag, $coursenamelink, array('class' => 'name'));
-
+                                            $coursename, array('class' => $course->visible ? '' : 'dimmed'));
+        $content .= html_writer::tag($nametag, $coursenamelink, array('class' => 'coursename'));
         // If we display course in collapsed form but the course has summary or course contacts, display the link to the info page.
         $content .= html_writer::start_tag('div', array('class' => 'moreinfo'));
         if ($chelper->get_show_courses() < self::COURSECAT_SHOW_COURSES_EXPANDED) {
@@ -1544,7 +1541,8 @@ class core_course_renderer extends plugin_renderer_base {
                     array('title' => get_string('numberofcourses'), 'class' => 'numberofcourse'));
         }
         $content .= html_writer::start_tag('div', array('class' => 'info'));
-        $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'name'));
+
+        $content .= html_writer::tag(($depth > 1) ? 'h4' : 'h3', $categoryname, array('class' => 'categoryname'));
         $content .= html_writer::end_tag('div'); // .info
 
         // add category content to the output
index 8c75e44..41ed09d 100644 (file)
@@ -16,18 +16,18 @@ Feature: Course paged mode
     And I click on <section3> "link" in the "#section-3" "css_element"
     And I follow "C1"
     And I click on <section1> "link" in the "#section-1" "css_element"
-    And I should see <section1> in the "div.single-section div.header" "css_element"
+    And I should see <section1> in the "div.single-section" "css_element"
     And I should see <section2> in the ".single-section span.mdl-right" "css_element"
     And I should not see <prevunexistingsection> in the ".single-section" "css_element"
     And I click on <section2> "link" in the ".single-section" "css_element"
-    And I should see <section2> in the "div.single-section div.header" "css_element"
+    And I should see <section2> in the "div.single-section" "css_element"
     And I should see <section1> in the ".single-section span.mdl-left" "css_element"
     And I should see <section3> in the ".single-section span.mdl-right" "css_element"
     And I click on <section1> "link" in the ".single-section" "css_element"
-    And I should see <section1> in the "div.single-section div.header" "css_element"
+    And I should see <section1> in the "div.single-section" "css_element"
     And I click on <section2> "link" in the ".single-section" "css_element"
     And I click on <section3> "link" in the ".single-section" "css_element"
-    And I should see <section3> in the "div.single-section div.header" "css_element"
+    And I should see <section3> in the "div.single-section" "css_element"
     And I should see <section2> in the ".single-section span.mdl-left" "css_element"
     And I should not see <section1> in the ".single-section .section-navigation" "css_element"
     And I should not see <prevunexistingsection> in the ".single-section" "css_element"
@@ -49,18 +49,18 @@ Feature: Course paged mode
     And I click on <section3> "link" in the "#section-3" "css_element"
     And I follow "C1"
     And I click on <section1> "link" in the "#section-1" "css_element"
-    And I should see <section1> in the "div.single-section div.header" "css_element"
+    And I should see <section1> in the "div.single-section" "css_element"
     And I should see <section2> in the ".single-section span.mdl-right" "css_element"
     And I should not see <prevunexistingsection> in the ".single-section" "css_element"
     And I click on <section2> "link" in the ".single-section" "css_element"
-    And I should see <section2> in the "div.single-section div.header" "css_element"
+    And I should see <section2> in the "div.single-section" "css_element"
     And I should see <section1> in the ".single-section span.mdl-left" "css_element"
     And I should see <section3> in the ".single-section span.mdl-right" "css_element"
     And I click on <section1> "link" in the ".single-section" "css_element"
-    And I should see <section1> in the "div.single-section div.header" "css_element"
+    And I should see <section1> in the "div.single-section" "css_element"
     And I click on <section2> "link" in the ".single-section" "css_element"
     And I click on <section3> "link" in the ".single-section" "css_element"
-    And I should see <section3> in the "div.single-section div.header" "css_element"
+    And I should see <section3> in the "div.single-section" "css_element"
     And I should see <section2> in the ".single-section span.mdl-left" "css_element"
     And I should not see <section1> in the ".single-section .section-navigation" "css_element"
     And I should not see <prevunexistingsection> in the ".single-section" "css_element"
index e8425e0..c6b9726 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-debug.js differ
index 98eb15b..e85d702 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander-min.js differ
index dd1d5b4..e58f308 100644 (file)
Binary files a/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js and b/course/yui/build/moodle-course-categoryexpander/moodle-course-categoryexpander.js differ
index 92833ac..b3c76f4 100644 (file)
@@ -25,14 +25,14 @@ var CSS = {
     SELECTORS = {
         LOADEDTREES: '.with_children.loaded',
         CONTENTNODE: '.content',
-        CATEGORYLISTENLINK: '.category .info .name',
-        CATEGORYSPINNERLOCATION: '.name',
+        CATEGORYLISTENLINK: '.category .info .categoryname',
+        CATEGORYSPINNERLOCATION: '.categoryname',
         CATEGORYWITHCOLLAPSEDLOADEDCHILDREN: '.category.with_children.loaded.collapsed',
         CATEGORYWITHMAXIMISEDLOADEDCHILDREN: '.category.with_children.loaded:not(.collapsed)',
         COLLAPSEEXPAND: '.collapseexpand',
         COURSEBOX: '.coursebox',
         COURSEBOXLISTENLINK: '.coursebox .moreinfo',
-        COURSEBOXSPINNERLOCATION: '.name a',
+        COURSEBOXSPINNERLOCATION: '.coursename a',
         COURSECATEGORYTREE: '.course_category_tree',
         PARENTWITHCHILDREN: '.category'
     },
index 2dad9f0..372652a 100644 (file)
@@ -432,24 +432,24 @@ YUI.add('moodle-course-toolboxes', function(Y) {
          * @method handle_resource_dim
          * @param {Node} button The button that triggered the action.
          * @param {Node} activity The activity node that this action will be performed on.
-         * @param {String} status Whether the activity was shown or hidden.
-         * @returns {number} 1 if we were changing to visible, 0 if we were hiding.
+         * @param {String} action 'show' or 'hide'.
+         * @returns {number} 1 if we changed to visible, 0 if we were hiding.
          */
-        handle_resource_dim : function(button, activity, status) {
+        handle_resource_dim : function(button, activity, action) {
             var toggleclass = CSS.DIMCLASS,
                 dimarea = activity.one('a'),
                 availabilityinfo = activity.one(CSS.AVAILABILITYINFODIV),
-                newstatus = (status === 'hide') ? 'show' : 'hide',
-                newstring = M.util.get_string(newstatus, 'moodle');
+                nextaction = (action === 'hide') ? 'show' : 'hide';
+                newstring = M.util.get_string(nextaction, 'moodle');
 
             // Update button info.
             button.one('img').setAttrs({
                 'alt' : newstring,
-                'src'   : M.util.image_url('t/' + newstatus)
+                'src'   : M.util.image_url('t/' + nextaction)
             });
             button.set('title', newstring);
-            button.replaceClass('editing_'+status, 'editing_'+newstatus)
-            button.setData('action', newstatus);
+            button.replaceClass('editing_'+action, 'editing_'+nextaction);
+            button.setData('action', nextaction);
 
             // If activity is conditionally hidden, then don't toggle.
             if (Y.Moodle.core_course.util.cm.getName(activity) == null) {
@@ -466,7 +466,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
             if (availabilityinfo) {
                 availabilityinfo.toggleClass(CSS.HIDE);
             }
-            return (status === 'hide') ? 0 : 1;
+            return (action === 'hide') ? 0 : 1;
         },
 
         /**
@@ -683,13 +683,13 @@ YUI.add('moodle-course-toolboxes', function(Y) {
                 shouldbevisible = args.visible,
                 buttonnode = element.one(SELECTOR.SHOW),
                 visible = (buttonnode === null),
-                status = 'hide';
+                action = 'show';
             if (visible) {
                 buttonnode = element.one(SELECTOR.HIDE);
-                status = 'show'
+                action = 'hide';
             }
             if (visible != shouldbevisible) {
-                this.handle_resource_dim(buttonnode, buttonnode.getData('activity'), status);
+                this.handle_resource_dim(buttonnode, buttonnode.getData('activity'), action);
             }
         }
     }, {
@@ -752,26 +752,26 @@ YUI.add('moodle-course-toolboxes', function(Y) {
 
             // The value to submit
             var value;
-            // The status text for strings and images
-            var status,
-                oldstatus;
+            // The text for strings and images. Also determines the icon to display.
+            var action,
+                nextaction;
 
             if (!section.hasClass(CSS.SECTIONHIDDENCLASS)) {
                 section.addClass(CSS.SECTIONHIDDENCLASS);
                 value = 0;
-                status = 'show';
-                oldstatus = 'hide';
+                action = 'hide';
+                nextaction = 'show';
             } else {
                 section.removeClass(CSS.SECTIONHIDDENCLASS);
                 value = 1;
-                status = 'hide';
-                oldstatus = 'show';
+                action = 'show';
+                nextaction = 'hide';
             }
 
-            var newstring = M.util.get_string(status + 'fromothers', 'format_' + this.get('format'));
+            var newstring = M.util.get_string(nextaction + 'fromothers', 'format_' + this.get('format'));
             hideicon.setAttrs({
                 'alt' : newstring,
-                'src'   : M.util.image_url('i/' + status)
+                'src'   : M.util.image_url('i/' + nextaction)
             });
             button.set('title', newstring);
 
@@ -800,7 +800,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, oldstatus);
+                    node.getData('toolbox').handle_resource_dim(button, node, action);
                 }
             }, this);
         },
index e655c7c..b8d1173 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013092600;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013050100;        // Requires this Moodle version
 $plugin->component = 'enrol_category';  // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60;
index 9de7dcf..7b902fd 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-/* List of handlers. */
-$handlers = array (
-    'cohort_member_added' => array (
-        'handlerfile'      => '/enrol/cohort/locallib.php',
-        'handlerfunction'  => array('enrol_cohort_handler', 'member_added'),
-        'schedule'         => 'instant',
-        'internal'         => 1,
+$observers = array(
+
+    array(
+        'eventname' => '\core\event\cohort_member_added',
+        'callback' => 'enrol_cohort_handler::member_added',
+        'includefile' => '/enrol/cohort/locallib.php'
     ),
 
-    'cohort_member_removed' => array (
-        'handlerfile'      => '/enrol/cohort/locallib.php',
-        'handlerfunction'  => array('enrol_cohort_handler', 'member_removed'),
-        'schedule'         => 'instant',
-        'internal'         => 1,
+    array(
+        'eventname' => '\core\event\cohort_member_removed',
+        'callback' => 'enrol_cohort_handler::member_removed',
+        'includefile' => '/enrol/cohort/locallib.php'
     ),
 
-    'cohort_deleted' => array (
-        'handlerfile'      => '/enrol/cohort/locallib.php',
-        'handlerfunction'  => array('enrol_cohort_handler', 'deleted'),
-        'schedule'         => 'instant',
-        'internal'         => 1,
+    array(
+        'eventname' => '\core\event\cohort_deleted',
+        'callback' => 'enrol_cohort_handler::deleted',
+        'includefile' => '/enrol/cohort/locallib.php'
     ),
 );
index 377584e..e07c20b 100644 (file)
@@ -36,10 +36,10 @@ require_once($CFG->dirroot . '/enrol/locallib.php');
 class enrol_cohort_handler {
     /**
      * Event processor - cohort member added.
-     * @param stdClass $ca
+     * @param \core\event\cohort_member_added $event
      * @return bool
      */
-    public static function member_added($ca) {
+    public static function member_added(\core\event\cohort_member_added $event) {
         global $DB, $CFG;
         require_once("$CFG->dirroot/group/lib.php");
 
@@ -53,7 +53,7 @@ class enrol_cohort_handler {
              LEFT JOIN {role} r ON (r.id = e.roleid)
                  WHERE e.customint1 = :cohortid AND e.enrol = 'cohort'
               ORDER BY e.id ASC";
-        if (!$instances = $DB->get_records_sql($sql, array('cohortid'=>$ca->cohortid))) {
+        if (!$instances = $DB->get_records_sql($sql, array('cohortid'=>$event->objectid))) {
             return true;
         }
 
@@ -68,13 +68,13 @@ class enrol_cohort_handler {
             }
             unset($instance->roleexists);
             // No problem if already enrolled.
-            $plugin->enrol_user($instance, $ca->userid, $instance->roleid, 0, 0, ENROL_USER_ACTIVE);
+            $plugin->enrol_user($instance, $event->relateduserid, $instance->roleid, 0, 0, ENROL_USER_ACTIVE);
 
             // Sync groups.
             if ($instance->customint2) {
-                if (!groups_is_member($instance->customint2, $ca->userid)) {
+                if (!groups_is_member($instance->customint2, $event->relateduserid)) {
                     if ($group = $DB->get_record('groups', array('id'=>$instance->customint2, 'courseid'=>$instance->courseid))) {
-                        groups_add_member($group->id, $ca->userid, 'enrol_cohort', $instance->id);
+                        groups_add_member($group->id, $event->relateduserid, 'enrol_cohort', $instance->id);
                     }
                 }
             }
@@ -85,14 +85,14 @@ class enrol_cohort_handler {
 
     /**
      * Event processor - cohort member removed.
-     * @param stdClass $ca
+     * @param \core\event\cohort_member_removed $event
      * @return bool
      */
-    public static function member_removed($ca) {
+    public static function member_removed(\core\event\cohort_member_removed $event) {
         global $DB;
 
         // Does anything want to sync with this cohort?
-        if (!$instances = $DB->get_records('enrol', array('customint1'=>$ca->cohortid, 'enrol'=>'cohort'), 'id ASC')) {
+        if (!$instances = $DB->get_records('enrol', array('customint1'=>$event->objectid, 'enrol'=>'cohort'), 'id ASC')) {
             return true;
         }
 
@@ -100,11 +100,11 @@ class enrol_cohort_handler {
         $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
 
         foreach ($instances as $instance) {
-            if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$ca->userid))) {
+            if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$event->relateduserid))) {
                 continue;
             }
             if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
-                $plugin->unenrol_user($instance, $ca->userid);
+                $plugin->unenrol_user($instance, $event->relateduserid);
 
             } else {
                 if ($ue->status != ENROL_USER_SUSPENDED) {
@@ -120,14 +120,14 @@ class enrol_cohort_handler {
 
     /**
      * Event processor - cohort deleted.
-     * @param stdClass $cohort
+     * @param \core\event\cohort_deleted $event
      * @return bool
      */
-    public static function deleted($cohort) {
+    public static function deleted(\core\event\cohort_deleted $event) {
         global $DB;
 
         // Does anything want to sync with this cohort?
-        if (!$instances = $DB->get_records('enrol', array('customint1'=>$cohort->id, 'enrol'=>'cohort'), 'id ASC')) {
+        if (!$instances = $DB->get_records('enrol', array('customint1'=>$event->objectid, 'enrol'=>'cohort'), 'id ASC')) {
             return true;
         }
 
index 2024bbd..e5df70d 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013092600;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013050100;        // Requires this Moodle version
 $plugin->component = 'enrol_cohort';    // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60*60;             // run cron every hour by default, it is not out-of-sync often
index f9ac4ca..f3dcdc0 100644 (file)
@@ -85,6 +85,7 @@ function cron() {
         $this->logfp = fopen($logtolocation, 'a');
     }
 
+    $fileisnew = false;
     if ( file_exists($filename) ) {
         @set_time_limit(0);
         $starttime = time();
@@ -107,10 +108,8 @@ function cron() {
         if(empty($prev_path)  || ($filename != $prev_path)) {
             $fileisnew = true;
         } elseif(isset($prev_time) && ($filemtime <= $prev_time)) {
-            $fileisnew = false;
             $this->log_line('File modification time is not more recent than last update - skipping processing.');
         } elseif(isset($prev_md5) && ($md5 == $prev_md5)) {
-            $fileisnew = false;
             $this->log_line('File MD5 hash is same as on last update - skipping processing.');
         } else {
             $fileisnew = true; // Let's process it!
@@ -217,7 +216,7 @@ function cron() {
         $this->log_line('File not found: '.$filename);
     }
 
-    if (!empty($mailadmins)) {
+    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){
@@ -234,7 +233,7 @@ function cron() {
 
         $eventdata = new stdClass();
         $eventdata->modulename        = 'moodle';
-        $eventdata->component         = 'imsenterprise';
+        $eventdata->component         = 'enrol_imsenterprise';
         $eventdata->name              = 'imsenterprise_enrolment';
         $eventdata->userfrom          = get_admin();
         $eventdata->userto            = get_admin();
@@ -400,7 +399,7 @@ function process_group_tag($tagcontents) {
                     // Insert default names for teachers/students, from the current language
 
                     // Handle course categorisation (taken from the group.org.orgunit field if present)
-                    if (strlen($group->category)>0) {
+                    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;
index 841022b..5ee34c3 100644 (file)
@@ -24,7 +24,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013092600;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013050100;        // Requires this Moodle version
 $plugin->component = 'enrol_meta';      // Full name of the plugin (used for diagnostics)
 $plugin->cron      = 60*60;             // run cron every hour by default, it is not out-of-sync often
\ No newline at end of file
index 64353f0..4017ade 100644 (file)
@@ -367,6 +367,7 @@ class core_files_renderer extends plugin_renderer_base {
      * @return string
      */
     private function fm_js_template_fileselectlayout() {
+        global $OUTPUT;
         $strloading  = get_string('loading', 'repository');
         $icon_progress = $this->pix_icon('i/loading_small', $strloading).'';
         $rv = '
@@ -377,7 +378,7 @@ class core_files_renderer extends plugin_renderer_base {
     <form>
         <button class="{!}fp-file-download">'.get_string('download').'</button>
         <button class="{!}fp-file-delete">'.get_string('delete').'</button>
-        <button class="{!}fp-file-setmain">'.get_string('setmainfile', 'repository').'</button>
+        <button class="{!}fp-file-setmain">'.get_string('setmainfile', 'repository').'</button><span class="fp-file-setmain-help">'.$OUTPUT->help_icon('setmainfile', 'repository').'</span>
         <button class="{!}fp-file-zip">'.get_string('zip', 'editor').'</button>
         <button class="{!}fp-file-unzip">'.get_string('unzip').'</button>
         <div class="fp-hr"></div>
index e782303..c05eb92 100644 (file)
@@ -793,9 +793,12 @@ class grade_edit_tree_column_range extends grade_edit_tree_column {
     public function get_item_cell($item, $params) {
         global $DB, $OUTPUT;
 
-        // If the parent aggregation is Sum of Grades, this cannot be changed
+        // If the parent aggregation is Sum of Grades, we should show the number, even for scales, as that value is used...
+        // ...in the computation. For text grades, the grademax is not used, so we can still show the no value string.
         $parent_cat = $item->get_parent_category();
-        if ($parent_cat->aggregation == GRADE_AGGREGATE_SUM) {
+        if ($item->gradetype == GRADE_TYPE_TEXT) {
+            $grademax = ' - ';
+        } else if ($parent_cat->aggregation == GRADE_AGGREGATE_SUM) {
             $grademax = format_float($item->grademax, $item->get_decimals());
         } elseif ($item->gradetype == GRADE_TYPE_SCALE) {
             $scale = $DB->get_record('scale', array('id' => $item->scaleid));
index 59e4a23..07c59e7 100644 (file)
@@ -32,21 +32,86 @@ require_once($CFG->dirroot.'/grade/edit/tree/lib.php');
 /**
  * Tests grade_edit_tree (deals with the data on the categories and items page in the gradebook)
  */
-class core_grade_edittreelib_testcase extends basic_testcase {
-    var $courseid = 1;
-    var $context = null;
-    var $grade_edit_tree = null;
-
+class core_grade_edittreelib_testcase extends advanced_testcase {
     public function test_format_number() {
-        $numinput = array( 0,   1,   1.01, '1.010', 1.2345);
+        $numinput = array(0,   1,   1.01, '1.010', 1.2345);
         $numoutput = array(0.0, 1.0, 1.01,  1.01,   1.2345);
 
-        for ($i=0; $i<sizeof($numinput); $i++) {
+        for ($i = 0; $i < count($numinput); $i++) {
             $msg = 'format_number() testing '.$numinput[$i].' %s';
-            $this->assertEquals(grade_edit_tree::format_number($numinput[$i]),$numoutput[$i],$msg);
+            $this->assertEquals(grade_edit_tree::format_number($numinput[$i]), $numoutput[$i], $msg);
         }
     }
 
+    public function test_grade_edit_tree_column_range_get_item_cell() {
+        global $DB, $CFG;
+
+        $this->resetAfterTest(true);
+
+        // Make some things we need.
+        $scale = $this->getDataGenerator()->create_scale();
+        $course = $this->getDataGenerator()->create_course();
+        $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
+        $modulecontext = context_module::instance($assign->id);
+        // The generator returns a dummy object, lets get the real assign object.
+        $assign = new assign($modulecontext, false, false);
+        $cm = $assign->get_course_module();
+
+        // Get range column.
+        $column = grade_edit_tree_column::factory('range');
+
+        $gradeitemparams = array(
+            'itemtype'     => 'mod',
+            'itemmodule'   => $cm->modname,
+            'iteminstance' => $cm->instance,
+            'courseid'     => $cm->course,
+            'itemnumber'   => 0
+        );
+
+        // Lets set the grade to something we know.
+        $instance = $assign->get_instance();
+        $instance->grade = 70;
+        $instance->instance = $instance->id;
+        $assign->update_instance($instance);
+
+        $gradeitem = grade_item::fetch($gradeitemparams);
+        $cell = $column->get_item_cell($gradeitem, array());
+
+        $this->assertEquals(GRADE_TYPE_VALUE, $gradeitem->gradetype);
+        $this->assertEquals(null, $gradeitem->scaleid);
+        $this->assertEquals(70.0, (float) $cell->text, "Grade text is 70", 0.01);
+
+        // Now change it to a scale.
+        $instance = $assign->get_instance();
+        $instance->grade = -($scale->id);
+        $instance->instance = $instance->id;
+        $assign->update_instance($instance);
+
+        $gradeitem = grade_item::fetch($gradeitemparams);
+        $cell = $column->get_item_cell($gradeitem, array());
+
+        // Make the expected scale text.
+        $scaleitems = null;
+        $scaleitems = explode(',', $scale->scale);
+        $scalestring = end($scaleitems) . ' (' . count($scaleitems) . ')';
+
+        $this->assertEquals(GRADE_TYPE_SCALE, $gradeitem->gradetype);
+        $this->assertEquals($scale->id, $gradeitem->scaleid);
+        $this->assertEquals($scalestring, $cell->text, "Grade text matches scale");
+
+        // Now change it to no grade.
+        $instance = $assign->get_instance();
+        $instance->grade = 0;
+        $instance->instance = $instance->id;
+        $assign->update_instance($instance);
+
+        $gradeitem = grade_item::fetch($gradeitemparams);
+        $cell = $column->get_item_cell($gradeitem, array());
+
+        $this->assertEquals(GRADE_TYPE_TEXT, $gradeitem->gradetype);
+        $this->assertEquals(null, $gradeitem->scaleid);
+        $this->assertEquals(' - ', $cell->text, 'Grade text matches empty value of " - "');
+    }
 }
 
 
index 54defb4..5c5f659 100644 (file)
--- a/index.php
+++ b/index.php
                         } else {
                             $subtext = get_string('subscribe', 'forum');
                         }
-                        echo $OUTPUT->heading($forumname, 2, 'headingblock header');
+                        echo $OUTPUT->heading($forumname);
                         $suburl = new moodle_url('/mod/forum/subscribe.php', array('id' => $newsforum->id, 'sesskey' => sesskey()));
                         echo html_writer::tag('div', html_writer::link($suburl, $subtext), array('class' => 'subscribelink'));
                     } else {
-                        echo $OUTPUT->heading($forumname, 2, 'headingblock header');
+                        echo $OUTPUT->heading($forumname);
                     }
 
                     forum_print_latest_discussions($SITE, $newsforum, $SITE->newsitems, 'plain', 'p.modified DESC');
                     //wrap frontpage course list in div container
                     echo html_writer::start_tag('div', array('id'=>'frontpage-course-list'));
 
-                    echo $OUTPUT->heading(get_string('mycourses'), 2, 'headingblock header');
+                    echo $OUTPUT->heading(get_string('mycourses'));
                     echo $mycourseshtml;
 
                     //end frontpage course list div container
                     //wrap frontpage course list in div container
                     echo html_writer::start_tag('div', array('id'=>'frontpage-course-list'));
 
-                    echo $OUTPUT->heading(get_string('availablecourses'), 2, 'headingblock header');
+                    echo $OUTPUT->heading(get_string('availablecourses'));
                     echo $availablecourseshtml;
 
                     //end frontpage course list div container
                 //wrap frontpage category names in div container
                 echo html_writer::start_tag('div', array('id'=>'frontpage-category-names'));
 
-                echo $OUTPUT->heading(get_string('categories'), 2, 'headingblock header');
+                echo $OUTPUT->heading(get_string('categories'));
                 echo $courserenderer->frontpage_categories_list();
 
                 //end frontpage category names div container
                 //wrap frontpage category combo in div container
                 echo html_writer::start_tag('div', array('id'=>'frontpage-category-combo'));
 
-                echo $OUTPUT->heading(get_string('courses'), 2, 'headingblock header');
+                echo $OUTPUT->heading(get_string('courses'));
                 echo $courserenderer->frontpage_combo_list();
 
                 //end frontpage category combo div container
index ff283b1..9da66ca 100644 (file)
@@ -30,7 +30,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['availablelangs'] = 'СпиÑ\81Ñ\8aк Ð½Ð° Ð½Ð°Ð»Ð¸Ñ\87ниÑ\82е ÐµÐ·Ð¸Ñ\86и';
+$string['availablelangs'] = 'Ð\9dалиÑ\87ни ÐµÐ·Ð¸ÐºÐ¾Ð²Ð¸ Ð¿Ð°ÐºÐµÑ\82и';
 $string['chooselanguagehead'] = 'Изберете език';
 $string['chooselanguagesub'] = 'Моля, изберете език за инсталацията. Този език ще бъде, също така, език по подразбиране на сайта, но може да бъде променен и по-късно след инсталирането.';
 $string['databasehost'] = 'Хост на базата данни';
index a9240fc..adbbb6c 100644 (file)
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
 $string['admindirname'] = 'Direktori admin';
 $string['availablelangs'] = 'Senarai bahasa yang tersedia';
 $string['chooselanguagehead'] = 'Pilih bahasa';
-$string['chooselanguagesub'] = 'Sila pilih bahasa untuk pemasangan. Bahasa ini juga akan digunakan sebagai bahasa lalai untuk laman, ia boleh diubah kemudian.';
+$string['chooselanguagesub'] = 'Sila pilih bahasa untuk pemasangan. Bahasa dipilih akan digunakan sebagai bahasa lalai untuk laman, walau bagaimanapun ia boleh diubah kemudian.';
 $string['clialreadyinstalled'] = 'config.php fail sudah wujud, sila gunakan admin/cli/upgrade.php jika anda mahu menaik taraf laman web anda.';
 $string['cliinstallheader'] = 'Baris arahan pemasangan program Moodle {$a}';
 $string['databasehost'] = 'Hos pangkalan data';
@@ -46,7 +46,7 @@ $string['environmenthead'] = 'Memeriksa persekitaran anda ...';
 $string['environmentsub2'] = 'Setiap pelepasan Moodle mempunyai keperluan minimum versi PHP dan beberapa sambungan mandatori PHP. Persekitaran penuh disemak sebelum setiap pemasangan dan naik taraf. Sila hubungi pentadbir pelayan jika anda tidak tahu bagaimana untuk memasang versi baru atau membolehkan sambungan PHP.';
 $string['errorsinenvironment'] = 'Semakan persekitaran gagal!';
 $string['installation'] = 'Pemasangan';
-$string['langdownloaderror'] = 'Malangnya bahasa "{$a}" tidak boleh dimuat turun. Proses pemasangan akan diteruskan dalam bahasa Bahasa Inggeris.';
+$string['langdownloaderror'] = 'Malangnya fail bahasa "{$a}" tidak dapat dimuat turun. Proses pemasangan akan diteruskan dalam Bahasa Inggeris.';
 $string['memorylimithelp'] = '<p> Had memori PHP untuk pelayan anda  ditetapkan kepada {$a}. </p>
 
 <p> Hal ini akan menyebabkan Moodle untuk mendapat masalah memori di kemudian hari, terutama jika anda mempunyai banyak modul yang diaktifkan dan/atau ramai pengguna. </p>
@@ -63,9 +63,9 @@ $string['pathsrodataroot'] = 'Direktori dataroot tidak dapat ditulis.';
 $string['pathsroparentdataroot'] = 'Direktori induk ({$a->parent}) tidak boleh tulis. Direktori data ({$a->dataroot}) tidak boleh diwujudkan oleh pemasang.';
 $string['pathssubadmindir'] = 'Beberapa webhos menggunakan /admin sebagai URL khas untuk anda akses panel kawalan atau sesuatu yang lain. Malangnya, ini bertentangan dengan lokasi standard untuk halaman pentadbiran Moodle. Anda boleh memperbaiki ini dengan menamakan semula direktori admin dalam pemasangan anda, dan meletakkan nama baru di sini. Sebagai contoh: <br /> <br /> <b> moodleadmin </ b> <br /> <br />
 Ini akan memulihkan pautan admin dalam Moodle.';
-$string['pathssubdataroot'] = 'Anda perlukan lokasi di mana Moodle boleh menyimpan fail yang dimuat naik. Direktori ini sepatutnya boleh dibaca DAN BOLEH DITULIS oleh pengguna web server (biasanya \'nobody\' atau \'apache\'), tetapi ia mestilah tidak boleh diakses secara langsung melalui laman web. Pemasang akan cuba untuk mewujudkannya jika belum ada.';
-$string['pathssubdirroot'] = 'Direktori laluan penuh untuk pemasangan Moodle.';
-$string['pathssubwwwroot'] = 'Alamat penuh web di mana Moodle akan dicapai. Adalah tidak mungkin untuk mengakses Moodle menggunakan pelbagai alamat. Jika laman anda mempunyai beberapa alamat awam, anda mesti menubuhkan pelencongan kekal kepada semua mereka kecuali yang satu ini. Jika laman web anda boleh diakses kedua-duanya dari Intranet dan Internet menggunakan alamat awam di sini dan menubuhkan DNS supaya pengguna Intranet boleh menggunakan alamat awam juga. Jika alamat itu tidak betul, sila ubah URL di pelayar anda untuk memulakan semula pemasangan dengan nilai yang berbeza.';
+$string['pathssubdataroot'] = 'Anda perlukan lokasi di mana Moodle boleh menyimpan fail yang dimuat naik. Direktori ini sepatutnya boleh dibaca DAN DITULIS oleh pengguna web server (biasanya \'nobody\' atau \'apache\'), tetapi ia mestilah tidak boleh diakses secara langsung melalui laman web. Pemasang akan cuba untuk wujudkannya jika belum ada.';
+$string['pathssubdirroot'] = 'Laluan penuh direktori untuk pemasangan Moodle.';
+$string['pathssubwwwroot'] = 'Alamat penuh web di mana Moodle akan dicapai. Ia tidak mungkin untuk mengakses Moodle menggunakan berbilang alamat. Jika laman anda mempunyai pelbagai alamat awam, anda mesti menubuhkan pelencongan kekal kepada yang lain kecuali yang satu ini. Jika laman web anda boleh diakses kedua-dua Intranet dan Internet, gunakan alamat awam di sini dan tubuhkan DNS supaya pengguna Intranet boleh gunakan alamat awam juga. Jika alamat sekarang tidak betul, sila ubah URL di pelayar anda untuk memulakan semula pemasangan dengan nilai yang berbeza.';
 $string['pathsunsecuredataroot'] = 'Lokasi dataroot tidak selamat';
 $string['pathswrongadmindir'] = 'Direktori admin tidak wujud';
 $string['phpextension'] = 'Sambungan PHP {$a}';
index e400a6c..5784b0c 100644 (file)
@@ -30,6 +30,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['cannotcreatedboninstall'] = '<p> Não é possível criar a base de dados. </p><p> A base de dados especificada não existe e o utilizador em questão não tem permissão para criar a base de dados. </p><p> O administrador do site deve verificar a configuração da base de dados. </p>';
 $string['cannotcreatelangdir'] = 'Não é possível criar a pasta de pacotes linguísticos';
 $string['cannotcreatetempdir'] = 'Não é possível criar a pasta de ficheiros temporários';
 $string['cannotdownloadcomponents'] = 'Não é possível descarregar os componentes.';
@@ -39,6 +40,7 @@ $string['cannotsavemd5file'] = 'Não é possível gravar o ficheiro md5';
 $string['cannotsavezipfile'] = 'Não é possível gravar o ficheiro ZIP';
 $string['cannotunzipfile'] = 'Não é possível descompactar o ficheiro ZIP';
 $string['componentisuptodate'] = 'O componente está atualizado.';
+$string['dmlexceptiononinstall'] = '<p>Ocorreu um erro de base de dados [{$a->errorcode}].<br />{$a->debuginfo}</p>';
 $string['downloadedfilecheckfailed'] = 'A verificação do ficheiro descarregado falhou.';
 $string['invalidmd5'] = 'A variável de verificação está errada - tente novamente.';
 $string['missingrequiredfield'] = 'Um dos campos obrigatórios está em falta';
index 7abb930..afa42aa 100644 (file)
@@ -31,7 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Pasta de administração';
-$string['availablelangs'] = 'Idiomas disponíveis';
+$string['availablelangs'] = 'Pacotes de idiomas disponíveis';
 $string['chooselanguagehead'] = 'Selecione um idioma';
 $string['chooselanguagesub'] = 'Selecione o idioma a utilizar durante a instalação. Poderá depois selecionar um outro idioma para o site e para os utilizadores.';
 $string['clialreadyconfigured'] = 'O ficheiro config.php já existe, use admin/cli/install_database.php se desejar instalar este site.';
index 1a092c9..54a7297 100644 (file)
@@ -86,6 +86,7 @@ $string['configgeneralcomments'] = 'Sets the default for including comments in a
 $string['configgeneralfilters'] = 'Sets the default for including filters in a backup.';
 $string['configgeneralhistories'] = 'Sets the default for including user history within a backup.';
 $string['configgenerallogs'] = 'If enabled logs will be included in backups by default.';
+$string['configgeneralquestionbank'] = 'If enabled the question bank will be included in backups by default. PLEASE NOTE: Disabling this setting with disable the backup of activities which use the question bank, such as the quiz.';
 $string['configgeneralroleassignments'] = 'If enabled by default roles assignments will also be backed up.';
 $string['configgeneraluserscompletion'] = 'If enabled user completion information will be included in backups by default.';
 $string['configgeneralusers'] = 'Sets the default for whether to include users in backups.';
@@ -136,7 +137,9 @@ $string['generalfilters'] = 'Include filters';
 $string['generalhistories'] = 'Include histories';
 $string['generalgradehistories'] = 'Include histories';
 $string['generallogs'] = 'Include logs';
+$string['generalquestionbank'] = 'Include question bank';
 $string['generalroleassignments'] = 'Include role assignments';
+$string['generalquestionbank'] = 'Include question bank';
 $string['generalsettings'] = 'General backup settings';
 $string['generaluserscompletion'] = 'Include user completion information';
 $string['generalusers'] = 'Include users';
@@ -229,6 +232,7 @@ $string['rootsettingfilters'] = 'Include filters';
 $string['rootsettingcomments'] = 'Include comments';
 $string['rootsettingcalendarevents'] = 'Include calendar events';
 $string['rootsettinguserscompletion'] = 'Include user completion details';
+$string['rootsettingquestionbank'] = 'Include question bank';
 $string['rootsettinglogs'] = 'Include course logs';
 $string['rootsettinggradehistories'] = 'Include grade history';
 $string['rootsettingimscc1'] = 'Convert to IMS Common Cartridge 1.0';
index a6734bf..db2c118 100644 (file)
@@ -205,6 +205,7 @@ $string['select'] = 'Select';
 $string['settings'] = 'Settings';
 $string['setupdefaultplugins'] = 'Setting up default repository plugins';
 $string['setmainfile'] = 'Set main file';
+$string['setmainfile_help'] = 'If there are multiple files in the folder, the main file is the one that appears on the view page. Other files such as images or videos may be embedded in it. In filemanager the main file is indicated with a title in bold.';
 $string['siteinstances'] = 'Repositories instances of the site';
 $string['size'] = 'Size';
 $string['submit'] = 'Submit';
index 83cdb58..52966b2 100644 (file)
@@ -82,6 +82,14 @@ $string['errorcoursecontextnotvalid'] = 'You cannot execute functions in the cou
 $string['errorinvalidparam'] = 'The param "{$a}" is invalid.';
 $string['errornotemptydefaultparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. The default can only be empty array. Check web service description.';
 $string['erroroptionalparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. It can not be set as VALUE_OPTIONAL. Check web service description.';
+$string['event_webservice_function_called'] = 'Web service function called';
+$string['event_webservice_login_failed'] = 'Web service login failed';
+$string['event_webservice_service_created'] = 'Web service service created';
+$string['event_webservice_service_updated'] = 'Web service service updated';
+$string['event_webservice_service_user_added'] = 'Web service service user added';
+$string['event_webservice_service_user_removed'] = 'Web service service user removed';
+$string['event_webservice_token_created'] = 'Web service token created';
+$string['event_webservice_token_sent'] = 'Web service token sent';
 $string['execute'] = 'Execute';
 $string['executewarnign'] = 'WARNING: If you press execute your database will be modified and changes can not be reverted automatically!';
 $string['externalservice'] = 'External service';
index 1a300b9..815fb2a 100644 (file)
@@ -821,69 +821,6 @@ function badges_get_user_badges($userid, $courseid = 0, $page = 0, $perpage = 0,
     return $badges;
 }
 
-/**
- * Get issued badge details for assertion URL
- *
- * @param string $hash
- */
-function badges_get_issued_badge_info($hash) {
-    global $DB, $CFG;
-
-    $a = array();
-
-    $record = $DB->get_record_sql('
-            SELECT
-                bi.dateissued,
-                bi.dateexpire,
-                u.email,
-                b.*,
-                bb.email as backpackemail
-            FROM
-                {badge} b
-                JOIN {badge_issued} bi
-                    ON b.id = bi.badgeid
-                JOIN {user} u
-                    ON u.id = bi.userid
-                LEFT JOIN {badge_backpack} bb
-                    ON bb.userid = bi.userid
-            WHERE ' . $DB->sql_compare_text('bi.uniquehash', 40) . ' = ' . $DB->sql_compare_text(':hash', 40),
-            array('hash' => $hash), IGNORE_MISSING);
-
-    if ($record) {
-        if ($record->type == BADGE_TYPE_SITE) {
-            $context = context_system::instance();
-        } else {
-            $context = context_course::instance($record->courseid);
-        }
-
-        $url = new moodle_url('/badges/badge.php', array('hash' => $hash));
-        $email = empty($record->backpackemail) ? $record->email : $record->backpackemail;
-
-        // Recipient's email is hashed: <algorithm>$<hash(email + salt)>.
-        $a['recipient'] = 'sha256$' . hash('sha256', $email . $CFG->badges_badgesalt);
-        $a['salt'] = $CFG->badges_badgesalt;
-
-        if ($record->dateexpire) {
-            $a['expires'] = date('Y-m-d', $record->dateexpire);
-        }
-
-        $a['issued_on'] = date('Y-m-d', $record->dateissued);
-        $a['evidence'] = $url->out(); // Issued badge URL.
-        $a['badge'] = array();
-        $a['badge']['version'] = '0.5.0'; // Version of OBI specification, 0.5.0 - current beta.
-        $a['badge']['name'] = $record->name;
-        $a['badge']['image'] = moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $record->id, '/', 'f1')->out();
-        $a['badge']['description'] = $record->description;
-        $a['badge']['criteria'] = $url->out(); // Issued badge URL.
-        $a['badge']['issuer'] = array();
-        $a['badge']['issuer']['origin'] = $record->issuerurl;
-        $a['badge']['issuer']['name'] = $record->issuername;
-        $a['badge']['issuer']['contact'] = $record->issuercontact;
-    }
-
-    return $a;
-}
-
 /**
  * Extends the course administration navigation with the Badges page
  *
index 667273f..6278ff7 100644 (file)
@@ -241,48 +241,56 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
      * closure exception will be used, but you must provide an exception if the closure does not throws
      * an exception.
      *
-     * @throws Exception            If it timeouts without receiving something != false from the closure
-     * @param  Closure   $lambda    The function to execute.
-     * @param  mixed     $args      Arguments to pass to the closure
-     * @param  int       $timeout   Timeout
-     * @param  Exception $exception The exception to throw in case it time outs.
+     * @throws Exception If it timeouts without receiving something != false from the closure
+     * @param Function|array|string $lambda The function to execute or an array passed to call_user_func (maps to a class method)
+     * @param mixed $args Arguments to pass to the closure
+     * @param int $timeout Timeout in seconds
+     * @param Exception $exception The exception to throw in case it time outs.
+     * @param bool $microsleep If set to true it'll sleep micro seconds rather than seconds.
      * @return mixed The value returned by the closure
      */
-    protected function spin($lambda, $args = false, $timeout = false, $exception = false) {
+    protected function spin($lambda, $args = false, $timeout = false, $exception = false, $microsleep = false) {
 
         // Using default timeout which is pretty high.
         if (!$timeout) {
             $timeout = self::TIMEOUT;
         }
+        if ($microsleep) {
+            // Will sleep 1/10th of a second by default for self::TIMEOUT seconds.
+            $loops = $timeout * 10;
+        } else {
+            // Will sleep for self::TIMEOUT seconds.
+            $loops = $timeout;
+        }
 
-        for ($i = 0; $i < $timeout; $i++) {
-
+        for ($i = 0; $i < $loops; $i++) {
             // We catch the exception thrown by the step definition to execute it again.
             try {
-
                 // We don't check with !== because most of the time closures will return
                 // direct Behat methods returns and we are not sure it will be always (bool)false
                 // if it just runs the behat method without returning anything $return == null.
-                if ($return = $lambda($this, $args)) {
+                if ($return = call_user_func($lambda, $this, $args)) {
                     return $return;
                 }
             } catch (Exception $e) {
-
                 // We would use the first closure exception if no exception has been provided.
                 if (!$exception) {
                     $exception = $e;
                 }
-
                 // We wait until no exception is thrown or timeout expires.
                 continue;
             }
 
-            sleep(1);
+            if ($microsleep) {
+                usleep(100000);
+            } else {
+                sleep(1);
+            }
         }
 
         // Using coding_exception as is a development issue if no exception has been provided.
         if (!$exception) {
-            $exception = new coding_exception('spin method requires an exception if the closure doesn\'t throw an exception itself');
+            $exception = new coding_exception('spin method requires an exception if the callback does not throw an exception');
         }
 
         // Throwing exception to the user.
index 531eb59..6a303f2 100644 (file)
@@ -463,6 +463,9 @@ abstract class base implements \IteratorAggregate {
                 if (!$DB->get_manager()->table_exists($this->data['objecttable'])) {
                     debugging('Unknown table specified in objecttable field', DEBUG_DEVELOPER);
                 }
+                if (!isset($this->data['objectid'])) {
+                    debugging('Event property objectid must be set when objecttable is defined', DEBUG_DEVELOPER);
+                }
             }
         }
     }
diff --git a/lib/classes/event/webservice_function_called.php b/lib/classes/event/webservice_function_called.php
new file mode 100644 (file)
index 0000000..9914385
--- /dev/null
@@ -0,0 +1,101 @@
+<?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/>.
+
+/**
+ * core webservice function_called event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice function_called event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_function_called extends \core\event\base {
+
+    /**
+     * Legacy log data.
+     */
+    protected $legacylogdata;
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The web service function '{$this->other['function']}' has been called.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        return $this->legacylogdata;
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_function_called', 'webservice');
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->context = \context_system::instance();
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return void
+     */
+    public function set_legacy_logdata($legacydata) {
+        $this->legacylogdata = $legacydata;
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->other['function'])) {
+           throw new \coding_exception('The key \'function\' needs to be set in $other.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/webservice_login_failed.php b/lib/classes/event/webservice_login_failed.php
new file mode 100644 (file)
index 0000000..395e91b
--- /dev/null
@@ -0,0 +1,116 @@
+<?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/>.
+
+/**
+ * core web service login failed event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core web service login_failed event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_login_failed extends \core\event\base {
+
+    /**
+     * Legacy log data.
+     *
+     * @var null|array
+     */
+    protected $legacylogdata;
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "Web service authentication failed with code: {$this->other['reason']}.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        return $this->legacylogdata;
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_login_failed', 'webservice');
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->context = \context_system::instance();
+    }
+
+    /**
+     * Set the legacy event log data.
+     *
+     * @param array $logdata The log data.
+     * @return void
+     */
+    public function set_legacy_logdata($logdata) {
+        $this->legacylogdata = $logdata;
+    }
+
+    /**
+     * Custom validation.
+     *
+     * It is recommended to set the properties:
+     * - $other['tokenid']
+     * - $other['username']
+     *
+     * However they are not mandatory as they are not always known.
+     *
+     * Please note that the token CANNOT be specified, it is considered
+     * as a password and should never be displayed.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->other['reason'])) {
+           throw new \coding_exception('The key \'reason\' needs to be set in $other.');
+        } else if (!isset($this->other['method'])) {
+           throw new \coding_exception('The key \'method\' needs to be set in $other.');
+        } else if (isset($this->other['token'])) {
+           throw new \coding_exception('The token cannot be set in $other.');
+        }
+    }
+}
diff --git a/lib/classes/event/webservice_service_created.php b/lib/classes/event/webservice_service_created.php
new file mode 100644 (file)
index 0000000..436a236
--- /dev/null
@@ -0,0 +1,88 @@
+<?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/>.
+
+/**
+ * core webservice service created event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice service created event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_service_created extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The web service service $this->objectid has been created by user $this->userid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        global $CFG;
+        $service = $this->get_record_snapshot('external_services', $this->objectid);
+        return array(SITEID, 'webservice', 'add', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices",
+            get_string('addservice', 'webservice', $service));
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_service_created', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/settings.php', array('section' => 'externalservices'));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'c';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_services';
+    }
+
+}
diff --git a/lib/classes/event/webservice_service_deleted.php b/lib/classes/event/webservice_service_deleted.php
new file mode 100644 (file)
index 0000000..4c497d9
--- /dev/null
@@ -0,0 +1,88 @@
+<?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/>.
+
+/**
+ * core webservice service deleted event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice service deleted event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_service_deleted extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The web service service $this->objectid has been deleted by user $this->userid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        global $CFG;
+        $service = $this->get_record_snapshot('external_services', $this->objectid);
+        return array(SITEID, 'webservice', 'delete', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices",
+            get_string('deleteservice', 'webservice', $service));
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_service_deleted', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/settings.php', array('section' => 'externalservices'));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'd';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_services';
+    }
+
+}
diff --git a/lib/classes/event/webservice_service_updated.php b/lib/classes/event/webservice_service_updated.php
new file mode 100644 (file)
index 0000000..0b82da7
--- /dev/null
@@ -0,0 +1,97 @@
+<?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/>.
+
+/**
+ * core webservice service updated event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice service updated event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_service_updated extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The web service service $this->objectid has been updated by user $this->userid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        global $CFG;
+        $service = $this->get_record_snapshot('external_services', $this->objectid);
+        return array(SITEID, 'webservice', 'edit', $CFG->wwwroot . "/" . $CFG->admin . "/settings.php?section=externalservices",
+            get_string('editservice', 'webservice', $service));
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_service_updated', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/settings.php', array('section' => 'externalservices'));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'u';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_services';
+    }
+
+    /**
+     * Set the legacy event log data.
+     *
+     * @return void
+     */
+    public function set_legacy_logdata($legacylogdata) {
+        $this->legacylogdata = $legacylogdata;
+    }
+
+}
diff --git a/lib/classes/event/webservice_service_user_added.php b/lib/classes/event/webservice_service_user_added.php
new file mode 100644 (file)
index 0000000..38309fb
--- /dev/null
@@ -0,0 +1,98 @@
+<?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/>.
+
+/**
+ * core webservice service user added event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice service user added event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_service_user_added extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user $this->relateduserid has been added to the web service service $this->objectid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        global $CFG;
+        return array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $this->objectid, 'add', '',
+            $this->relateduserid);
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_service_user_added', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/webservice/service_users.php', array('id' => $this->objectid));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'c';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_services';
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->relateduserid)) {
+            throw new \coding_exception('The relateduserid must be set.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/webservice_service_user_removed.php b/lib/classes/event/webservice_service_user_removed.php
new file mode 100644 (file)
index 0000000..a0f7eec
--- /dev/null
@@ -0,0 +1,98 @@
+<?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/>.
+
+/**
+ * core webservice service user removed event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice service user removed event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_service_user_removed extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The user $this->relateduserid has been removed to the web service service $this->objectid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        global $CFG;
+        return array(SITEID, 'core', 'assign', $CFG->admin . '/webservice/service_users.php?id=' . $this->objectid, 'remove', '',
+            $this->relateduserid);
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_service_user_removed', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/webservice/service_users.php', array('id' => $this->objectid));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'd';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_services';
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->relateduserid)) {
+            throw new \coding_exception('The relateduserid must be set.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/webservice_token_created.php b/lib/classes/event/webservice_token_created.php
new file mode 100644 (file)
index 0000000..5f18336
--- /dev/null
@@ -0,0 +1,100 @@
+<?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/>.
+
+/**
+ * core webservice token_created event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice token_created event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_token_created extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "A web service token has been created for the user $this->relateduserid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        if (!empty($this->other['auto'])) {
+            // The token has been automatically created.
+            return array(SITEID, 'webservice', 'automatically create user token', '' , 'User ID: ' . $this->relateduserid);
+        }
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_token_created', 'webservice');
+    }
+
+    /**
+     * Get URL related to the action.
+     *
+     * @return \moodle_url
+     */
+    public function get_url() {
+        return new \moodle_url('/admin/settings.php', array('section' => 'webservicetokens'));
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'c';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_tokens';
+    }
+
+    /**
+     * Custom validation.
+     *
+     * @throws \coding_exception
+     * @return void
+     */
+    protected function validate_data() {
+        if (!isset($this->relateduserid)) {
+           throw new \coding_exception('The property \'relateduserid\' must be set.');
+        }
+    }
+
+}
diff --git a/lib/classes/event/webservice_token_sent.php b/lib/classes/event/webservice_token_sent.php
new file mode 100644 (file)
index 0000000..51775bd
--- /dev/null
@@ -0,0 +1,76 @@
+<?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/>.
+
+/**
+ * core webservice token_sent event.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\event;
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * core webservice token sent event class.
+ *
+ * @package    core
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_token_sent extends \core\event\base {
+
+    /**
+     * Returns description of what happened.
+     *
+     * @return string
+     */
+    public function get_description() {
+        return "The token $this->objectid has been sent to the user $this->userid.";
+    }
+
+    /**
+     * Return the legacy event log data.
+     *
+     * @return array|null
+     */
+    protected function get_legacy_logdata() {
+        return array(SITEID, 'webservice', 'sending requested user token', '' , 'User ID: ' . $this->userid);
+    }
+
+    /**
+     * Return localised event name.
+     *
+     * @return string
+     */
+    public static function get_name() {
+        return get_string('event_webservice_token_sent', 'webservice');
+    }
+
+    /**
+     * Init method.
+     *
+     * @return void
+     */
+    protected function init() {
+        $this->context = \context_system::instance();
+        $this->data['crud'] = 'r';
+        $this->data['level'] = self::LEVEL_OTHER;
+        $this->data['objecttable'] = 'external_tokens';
+    }
+
+}
index 4032884..badf854 100644 (file)
@@ -39,7 +39,7 @@ class core_user {
     const NOREPLY_USER = -10;
 
     /**
-     * Suppport user id.
+     * Support user id.
      */
     const SUPPORT_USER = -20;
 
@@ -160,9 +160,11 @@ class core_user {
             self::$supportuser = self::get_dummy_user_record();
             self::$supportuser->id = self::SUPPORT_USER;
             self::$supportuser->email = $CFG->supportemail;
-            self::$supportuser->firstname = $CFG->supportname ? $CFG->supportname : $supportuser->firstname;
+            if ($CFG->supportname) {
+                self::$supportuser->firstname = $CFG->supportname;
+            }
             self::$supportuser->username = 'support';
-            self::$supportuser->maildisplay = true;
+            self::$supportuser->maildisplay = '1'; // Show to all.
         }
 
         // Send support msg to admin user if nothing is set above.
@@ -191,7 +193,7 @@ class core_user {
 
     /**
      * Return true is user id is greater than self::NOREPLY_USER and
-     * alternetely check db.
+     * alternatively check db.
      *
      * @param int $userid user id.
      * @param bool $checkdb if true userid will be checked in db. By default it's false, and
@@ -199,6 +201,8 @@ class core_user {
      * @return bool true is real user else false.
      */
     public static function is_real_user($userid, $checkdb = false) {
+        global $DB;
+
         if ($userid < 0) {
             return false;
         }
index c10389e..55fc643 100644 (file)
@@ -220,12 +220,12 @@ class component_installer {
         $this->requisitesok = false;
 
     /// Check that everything we need is present
-        if (empty($this->sourcebase) || empty($this->zippath) || empty($this->zipfilename)) {
+        if (empty($this->sourcebase) || empty($this->zipfilename)) {
             $this->errorstring='missingrequiredfield';
             return false;
         }
     /// Check for correct sourcebase (this will be out in the future)
-        if ($this->sourcebase != 'http://download.moodle.org') {
+        if (!PHPUNIT_TEST and $this->sourcebase != 'http://download.moodle.org') {
             $this->errorstring='wrongsourcebase';
             return false;
         }
@@ -286,7 +286,12 @@ class component_installer {
              return COMPONENT_ERROR;
         }
     /// Download zip file and save it to temp
-        $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->zipfilename;
+        if ($this->zippath) {
+            $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->zipfilename;
+        } else {
+            $source = $this->sourcebase.'/'.$this->zipfilename;
+        }
+
         $zipfile= $CFG->tempdir.'/'.$this->zipfilename;
 
         if($contents = download_file_content($source)) {
@@ -474,7 +479,11 @@ class component_installer {
         $comp_arr = array();
 
     /// Define and retrieve the full md5 file
-        $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->md5filename;
+        if ($this->zippath) {
+            $source = $this->sourcebase.'/'.$this->zippath.'/'.$this->md5filename;
+        } else {
+            $source = $this->sourcebase.'/'.$this->md5filename;
+        }
 
     /// Check if we have downloaded the md5 file before (per request cache)
         if (!empty($this->cachedmd5components[$source])) {
index 5348112..520840e 100644 (file)
@@ -572,12 +572,6 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         if ($rv !== false) {
             return $rv;
         }
-        // We did not find the entry in cache but it also can mean that tree is not built.
-        // The keys 0 and 'countall' must always be present if tree is built.
-        if ($id !== 0 && $id !== 'countall' && $coursecattreecache->has('countall')) {
-            // Tree was built, it means the non-existing $id was requested.
-            return false;
-        }
         // Re-build the tree.
         $sql = "SELECT cc.id, cc.parent, cc.visible
                 FROM {course_categories} cc
@@ -619,7 +613,8 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         if (array_key_exists($id, $all)) {
             return $all[$id];
         }
-        return false;
+        // Requested non-existing category.
+        return array();
     }
 
     /**
@@ -1187,8 +1182,13 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         $coursecatcache = cache::make('core', 'coursecat');
         $cntcachekey = 'scnt-'. serialize($search);
         if (($cnt = $coursecatcache->get($cntcachekey)) === false) {
-            self::search_courses($search, $options);
-            $cnt = $coursecatcache->get($cntcachekey);
+            // Cached value not found. Retrieve ALL courses and return their count.
+            unset($options['offset']);
+            unset($options['limit']);
+            unset($options['summary']);
+            unset($options['coursecontacts']);
+            $courses = self::search_courses($search, $options);
+            $cnt = count($courses);
         }
         return $cnt;
     }
@@ -1311,8 +1311,13 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
         $cntcachekey = 'lcnt-'. $this->id. '-'. (!empty($options['recursive']) ? 'r' : '');
         $coursecatcache = cache::make('core', 'coursecat');
         if (($cnt = $coursecatcache->get($cntcachekey)) === false) {
-            $this->get_courses($options);
-            $cnt = $coursecatcache->get($cntcachekey);
+            // Cached value not found. Retrieve ALL courses and return their count.
+            unset($options['offset']);
+            unset($options['limit']);
+            unset($options['summary']);
+            unset($options['coursecontacts']);
+            $courses = $this->get_courses($options);
+            $cnt = count($courses);
         }
         return $cnt;
     }
index b0c04d8..f8f65cc 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20130921" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20130927" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
         <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="order of files"/>
         <FIELD NAME="referencefileid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Use to indicate file is a proxy for repository file"/>
-        <FIELD NAME="referencelastsync" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="Last time the proxy file was synced with repository, defined for performance reasons"/>
-        <FIELD NAME="referencelifetime" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false" COMMENT="How often do we have to sync proxy file with repository, defined for performance reasons"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index d68ce28..5409977 100644 (file)
@@ -864,11 +864,9 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012062000.01);
     }
 
-
     // Moodle v2.3.0 release upgrade line
     // Put any upgrade step following this
 
-
     if ($oldversion < 2012062500.02) {
         // Drop some old backup tables, not used anymore
 
@@ -1497,7 +1495,6 @@ function xmldb_main_upgrade($oldversion) {
     // Moodle v2.4.0 release upgrade line
     // Put any upgrade step following this
 
-
     if ($oldversion < 2012120300.01) {
         // Make sure site-course has format='site' //MDL-36840
 
@@ -2555,5 +2552,29 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2013092001.02);
     }
 
+    if ($oldversion < 2013092700.01) {
+
+        $table = new xmldb_table('files');
+
+        // Define field referencelastsync to be dropped from files.
+        $field = new xmldb_field('referencelastsync');
+
+        // Conditionally launch drop field referencelastsync.
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field referencelifetime to be dropped from files.
+        $field = new xmldb_field('referencelifetime');
+
+        // Conditionally launch drop field referencelifetime.
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013092700.01);
+    }
+
     return true;
 }
index 6ef7851..9709302 100644 (file)
@@ -4678,4 +4678,17 @@ function get_browser_version_classes() {
 function generate_email_supportuser() {
     debugging('generate_email_supportuser is deprecated, please use core_user::get_support_user');
     return core_user::get_support_user();
-}
\ No newline at end of file
+}
+
+/**
+ * Get issued badge details for assertion URL
+ *
+ * @deprecated since Moodle 2.6
+ * @param string $hash Unique hash of a badge
+ * @return array Information about issued badge.
+ */
+function badges_get_issued_badge_info($hash) {
+    debugging('Function badges_get_issued_badge_info() is deprecated. Please use core_badges_assertion class and methods to generate badge assertion.', DEBUG_DEVELOPER);
+    $assertion = new core_badges_assertion($hash);
+    return $assertion->get_badge_assertion();
+}
index 9735788..ec7a027 100644 (file)
@@ -27,4 +27,5 @@ $string['link'] = 'Link';
 $string['createlink'] = 'Create link';
 $string['enterurl'] = 'Enter a URL';
 $string['browserepositories'] = 'Browse repositories...';
+$string['openinnewwindow'] = 'Open in new window';
 $string['accessibilityhint'] = '<p>Web content accessibility guidelines (WCAG):<br/><ul><li><a href="http://www.w3.org/TR/WCAG20/#navigation-mechanisms-refs" target="_blank">2.4.4 Link Purpose (In Context)</a></li><li><a href="http://www.w3.org/TR/WCAG20/#navigation-mechanisms-link" target="_blank">2.4.9 Link Purpose (Link Only)</a></li></ul></p>';
index 90b953c..1276905 100644 (file)
@@ -39,6 +39,7 @@ function atto_link_init_editor($elementid) {
     $PAGE->requires->strings_for_js(array('createlink',
                                           'enterurl',
                                           'browserepositories',
+                                          'openinnewwindow',
                                           'accessibilityhint'),
                                     'atto_link');
 
index 69cc689..7f88586 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-debug.js differ
index 68806af..1434aed 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button-min.js differ
index 69cc689..7f88586 100644 (file)
Binary files a/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js and b/lib/editor/atto/plugins/link/yui/build/moodle-atto_link-button/moodle-atto_link-button.js differ
index 329e1f7..71d74b9 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 M.atto_link = M.atto_link || {
+    /**
+     * The window used to get the link details.
+     *
+     * @property dialogue
+     * @type M.core.dialogue
+     * @default null
+     */
     dialogue : null,
+
+    /**
+     * The selection object returned by the browser.
+     *
+     * @property selection
+     * @type Range
+     * @default null
+     */
     selection : null,
-    init : function(params) {
-        var display_chooser = function(e, elementid) {
-            e.preventDefault();
-            if (!M.editor_atto.is_active(elementid)) {
-                M.editor_atto.focus(elementid);
+
+    /**
+     * Display the chooser dialogue.
+     *
+     * @method init
+     * @param Event e
+     * @param string elementid
+     */
+    display_chooser : function(e, elementid) {
+        e.preventDefault();
+        if (!M.editor_atto.is_active(elementid)) {
+            M.editor_atto.focus(elementid);
+        }
+        M.atto_link.selection = M.editor_atto.get_selection();
+        if (M.atto_link.selection !== false && (!M.atto_link.selection.collapsed)) {
+            var dialogue;
+            if (!M.atto_link.dialogue) {
+                dialogue = new M.core.dialogue({
+                    visible: false,
+                    modal: true,
+                    close: true,
+                    draggable: true
+                });
+            } else {
+                dialogue = M.atto_link.dialogue;
             }
-            M.atto_link.selection = M.editor_atto.get_selection();
-            if (M.atto_link.selection !== false && (!M.atto_link.selection.collapsed)) {
-                var dialogue;
-                if (!M.atto_link.dialogue) {
-                    dialogue = new M.core.dialogue({
-                        visible: false,
-                        modal: true,
-                        close: true,
-                        draggable: true
-                    });
-                } else {
-                    dialogue = M.atto_link.dialogue;
-                }
 
-                dialogue.render();
-                dialogue.set('bodyContent', M.atto_link.get_form_content(elementid));
-                dialogue.set('headerContent', M.util.get_string('createlink', 'atto_link'));
+            dialogue.render();
+            dialogue.set('bodyContent', M.atto_link.get_form_content(elementid));
+            dialogue.set('headerContent', M.util.get_string('createlink', 'atto_link'));
 
-                M.atto_link.resolve_anchors();
+            M.atto_link.resolve_anchors();
 
-                dialogue.show();
-                M.atto_link.dialogue = dialogue;
-            }
-        };
+            dialogue.show();
+            M.atto_link.dialogue = dialogue;
+        }
+    },
 
-        M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, display_chooser, this);
+    /**
+     * Add this button to the form.
+     *
+     * @method init
+     * @param {Object} params
+     */
+    init : function(params) {
+        M.editor_atto.add_toolbar_button(params.elementid, 'link', params.icon, params.group, this.display_chooser, this);
     },
+
+    /**
+     * If there is selected text and it is part of an anchor link,
+     * extract the url (and target) from the link (and set them in the form).
+     *
+     * @method resolve_anchors
+     */
     resolve_anchors : function() {
         // Find the first anchor tag in the selection.
         var selectednode = M.editor_atto.get_selection_parent_node(),
+            anchornodes,
             anchornode,
             url;
 
@@ -67,22 +104,43 @@ M.atto_link = M.atto_link || {
             return;
         }
 
-        anchornode = Y.one(selectednode).ancestor('a');
+        anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
 
-        if (anchornode) {
+        if (anchornodes.length > 0) {
+            anchornode = anchornodes[0];
+            M.atto_link.selection = M.editor_atto.get_selection_from_node(anchornode);
             url = anchornode.getAttribute('href');
+            target = anchornode.getAttribute('target');
             if (url !== '') {
-                M.atto_link.selection = M.editor_atto.get_selection_from_node(anchornode);
                 Y.one('#atto_link_urlentry').set('value', url);
             }
+            if (target === '_blank') {
+                Y.one('#atto_link_openinnewwindow').set('checked', 'checked');
+            } else {
+                Y.one('#atto_link_openinnewwindow').set('checked', '');
+            }
         }
     },
+
+    /**
+     * Open the repository file picker.
+     *
+     * @method open_filepicker
+     * @param Event e
+     */
     open_filepicker : function(e) {
         var elementid = this.getAttribute('data-editor');
         e.preventDefault();
 
         M.editor_atto.show_filepicker(elementid, 'link', M.atto_link.filepicker_callback);
     },
+
+    /**
+     * Called by the file picker when a link has been chosen.
+     *
+     * @method filepicker_callback
+     * @param {Object} params - contains selected url.
+     */
     filepicker_callback : function(params) {
         M.atto_link.dialogue.hide();
         if (params.url !== '') {
@@ -91,25 +149,99 @@ M.atto_link = M.atto_link || {
             document.execCommand('createLink', false, params.url);
         }
     },
+
+    /**
+     * The OK button has been pressed - make the changes to the source.
+     *
+     * @method set_link
+     * @param Event e
+     */
     set_link : function(e) {
+        var input,
+            target,
+            selectednode,
+            anchornodes,
+            value;
+
         e.preventDefault();
         M.atto_link.dialogue.hide();
 
-        var input = e.currentTarget.get('parentNode').one('input');
+        input = e.currentTarget.get('parentNode').one('input[type=url]');
 
-        var value = input.get('value');
+        value = input.get('value');
         if (value !== '') {
             M.editor_atto.set_selection(M.atto_link.selection);
             document.execCommand('unlink', false, null);
             document.execCommand('createLink', false, value);
+
+            // Now set the target.
+            selectednode = M.editor_atto.get_selection_parent_node();
+
+            // Note this is a document fragment and YUI doesn't like them.
+            if (!selectednode) {
+                return;
+            }
+
+            anchornodes = M.atto_link.find_selected_anchors(Y.one(selectednode));
+            Y.Array.each(anchornodes, function(anchornode) {
+                target = e.currentTarget.get('parentNode').one('input[type=checkbox]');
+                if (target.get('checked')) {
+                    anchornode.setAttribute('target', '_blank');
+                } else {
+                    anchornode.removeAttribute('target');
+                }
+            });
+        }
+    },
+
+    /**
+     * Look up and down for the ne