Merge branch 'master_MDL-34644' of git://github.com/danmarsden/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 18 Sep 2012 01:27:09 +0000 (09:27 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 18 Sep 2012 01:27:09 +0000 (09:27 +0800)
353 files changed:
admin/settings/courses.php
auth/shibboleth/index.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_course_task.class.php
backup/moodle2/restore_qtype_plugin.class.php
backup/moodle2/restore_root_task.class.php
backup/moodle2/restore_stepslib.php
backup/util/plan/restore_structure_step.class.php
blocks/community/yui/comments/comments.js
blocks/dock.js
blocks/rss_client/block_rss_client.php
course/category.php
course/edit_form.php
course/format/weeks/format.js
course/lib.php
course/pending.php
course/request_form.php
course/tests/courselib_test.php
course/tests/courserequest_test.php [new file with mode: 0644]
course/tests/externallib_test.php
enrol/category/lib.php
enrol/cohort/addinstance.php
enrol/cohort/addinstance_form.php
enrol/cohort/ajax.php
enrol/cohort/cli/sync.php
enrol/cohort/db/access.php
enrol/cohort/db/events.php
enrol/cohort/db/uninstall.php
enrol/cohort/lang/en/enrol_cohort.php
enrol/cohort/lib.php
enrol/cohort/locallib.php
enrol/cohort/settings.php
enrol/cohort/version.php
enrol/cohort/yui/quickenrolment/assets/skins/sam/quickenrolment.css
enrol/guest/lib.php
enrol/manual/lib.php
enrol/manual/tests/externallib_test.php [new file with mode: 0644]
enrol/self/lib.php
enrol/tests/externallib_test.php [new file with mode: 0644]
enrol/upgrade.txt
files/renderer.php
group/tests/externallib_test.php [new file with mode: 0644]
index.php
install/lang/eu/admin.php
install/lang/ga/error.php [new file with mode: 0644]
install/lang/pl/install.php
install/lang/ru/install.php
lang/en/admin.php
lang/en/backup.php
lang/en/calendar.php
lang/en/moodle.php
lang/en/table.php
lib/adminlib.php
lib/blocklib.php
lib/datalib.php
lib/db/install.xml
lib/db/upgrade.php
lib/ddl/tests/ddl_test.php
lib/dml/moodle_database.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/dtl/database_exporter.php
lib/editor/tinymce/all_strings.php [moved from lib/editor/tinymce/extra/strings.php with 67% similarity]
lib/editor/tinymce/cli/update_lang_files.php [new file with mode: 0644]
lib/editor/tinymce/extra/tools/download_langs.sh [deleted file]
lib/editor/tinymce/extra/tools/update_lang_files.php [deleted file]
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/module.js
lib/editor/tinymce/plugins/moodleimage/lang/en/tinymce_moodleimage.php
lib/editor/tinymce/plugins/moodleimage/tinymce/image.htm
lib/editor/tinymce/plugins/moodlemedia/lang/en/tinymce_moodlemedia.php
lib/editor/tinymce/plugins/moodlemedia/tinymce/moodlemedia.htm
lib/editor/tinymce/readme_moodle.txt
lib/editor/tinymce/tiny_mce/3.6.0/jquery.tinymce.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/license.txt
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advhr/css/advhr.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advhr/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advhr/js/rule.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advhr/rule.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advimage/css/advimage.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advimage/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advimage/image.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advimage/js/image.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advlink/css/advlink.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advlink/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advlink/js/advlink.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advlink/link.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/advlist/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/autosave/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/autosave/langs/en.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/bbcode/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/contextmenu/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/directionality/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/emotions/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/emotions/emotions.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/emotions/js/emotions.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/example/dialog.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/example/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/example/js/dialog.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/example/langs/en.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/example/langs/en_dlg.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullpage/css/fullpage.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullpage/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullpage/fullpage.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullpage/js/fullpage.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullscreen/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/fullscreen/fullscreen.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/iespell/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/inlinepopups/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/inlinepopups/template.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/insertdatetime/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/layer/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/legacyoutput/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/media/css/media.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/media/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/media/js/embed.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/media/js/media.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/media/media.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/nonbreaking/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/noneditable/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/pagebreak/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/paste/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/paste/js/pastetext.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/paste/js/pasteword.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/paste/pastetext.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/paste/pasteword.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/preview/example.html
lib/editor/tinymce/tiny_mce/3.6.0/plugins/preview/jscripts/embed.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/preview/preview.html
lib/editor/tinymce/tiny_mce/3.6.0/plugins/print/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/save/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/searchreplace/css/searchreplace.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/searchreplace/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/searchreplace/js/searchreplace.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/searchreplace/searchreplace.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/spellchecker/css/content.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/spellchecker/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/style/css/props.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/style/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/style/js/props.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/style/props.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/cell.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/css/cell.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/css/row.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/css/table.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/js/cell.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/js/merge_cells.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/js/row.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/js/table.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/merge_cells.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/row.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/table/table.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/template/blank.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/template/css/template.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/template/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/template/js/template.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/template/template.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/css/visualblocks.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/address.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/article.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/aside.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/blockquote.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/div.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/figure.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h1.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h2.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h3.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h4.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h5.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/h6.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/hgroup.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/p.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/pre.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualblocks/img/section.gif [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/plugins/visualchars/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/wordcount/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/abbr.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/acronym.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/attributes.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/cite.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/css/attributes.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/css/popup.css
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/del.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/editor_plugin_src.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/ins.htm
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/abbr.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/acronym.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/attributes.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/cite.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/del.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/element_common.js
lib/editor/tinymce/tiny_mce/3.6.0/plugins/xhtmlxtras/js/ins.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/about.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/anchor.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/charmap.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/color_picker.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/editor_template_src.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/image.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/about.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/anchor.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/charmap.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/image.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/link.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/js/source_editor.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/link.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/shortcuts.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/default/content.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/default/dialog.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/default/ui.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/highcontrast/content.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/highcontrast/dialog.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/highcontrast/ui.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/o2k7/content.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/o2k7/dialog.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/o2k7/ui.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/o2k7/ui_black.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/skins/o2k7/ui_silver.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/advanced/source_editor.htm
lib/editor/tinymce/tiny_mce/3.6.0/themes/simple/editor_template_src.js
lib/editor/tinymce/tiny_mce/3.6.0/themes/simple/skins/default/content.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/simple/skins/default/ui.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/simple/skins/o2k7/content.css
lib/editor/tinymce/tiny_mce/3.6.0/themes/simple/skins/o2k7/ui.css
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce.js
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_dev.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_jquery.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_jquery_src.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_popup.js
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_popup_src.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_prototype.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_prototype_src.js [deleted file]
lib/editor/tinymce/tiny_mce/3.6.0/tiny_mce_src.js
lib/editor/tinymce/tiny_mce/3.6.0/utils/editable_selects.js
lib/editor/tinymce/tiny_mce/3.6.0/utils/form_utils.js
lib/editor/tinymce/tiny_mce/3.6.0/utils/mctabs.js
lib/editor/tinymce/tiny_mce/3.6.0/utils/validate.js
lib/editor/tinymce/upgrade.txt
lib/editor/tinymce/version.php
lib/enrollib.php
lib/filelib.php
lib/filestorage/file_storage.php
lib/filestorage/tests/zip_packer_test.php
lib/formslib.php
lib/javascript-static.js
lib/moodlelib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpunit/classes/advanced_testcase.php
lib/phpunit/classes/database_driver_testcase.php
lib/phpunit/classes/util.php
lib/phpunit/tests/advanced_test.php
lib/pluginlib.php
lib/tablelib.php
lib/tests/accesslib_test.php
lib/tests/moodlelib_test.php
lib/tests/outputlib_test.php
lib/tests/textlib_test.php
lib/upgrade.txt
lib/weblib.php
message/tests/externallib_test.php [new file with mode: 0644]
mod/assign/assignmentplugin.php
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/db/events.php
mod/assign/feedback/comments/locallib.php
mod/assign/feedback/file/batchuploadfilesform.php [new file with mode: 0644]
mod/assign/feedback/file/importzipform.php [new file with mode: 0644]
mod/assign/feedback/file/importziplib.php [new file with mode: 0644]
mod/assign/feedback/file/lang/en/assignfeedback_file.php
mod/assign/feedback/file/locallib.php
mod/assign/feedback/file/renderable.php [new file with mode: 0644]
mod/assign/feedback/file/renderer.php [new file with mode: 0644]
mod/assign/feedback/file/uploadzipform.php [new file with mode: 0644]
mod/assign/feedback/offline/db/access.php [new file with mode: 0644]
mod/assign/feedback/offline/importgradesform.php [new file with mode: 0644]
mod/assign/feedback/offline/importgradeslib.php [new file with mode: 0644]
mod/assign/feedback/offline/lang/en/assignfeedback_offline.php [new file with mode: 0644]
mod/assign/feedback/offline/locallib.php [new file with mode: 0644]
mod/assign/feedback/offline/settings.php [new file with mode: 0644]
mod/assign/feedback/offline/uploadgradesform.php [new file with mode: 0644]
mod/assign/feedback/offline/version.php [new file with mode: 0644]
mod/assign/feedbackplugin.php
mod/assign/gradingbatchoperationsform.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/module.js
mod/assign/renderable.php
mod/assign/renderer.php
mod/assign/submission/comments/locallib.php
mod/assign/submission/onlinetext/locallib.php
mod/book/backup/moodle2/restore_book_activity_task.class.php
mod/book/lib.php
mod/book/version.php
mod/book/view.php
mod/chat/chatd.php
mod/chat/gui_sockets/chatinput.php
mod/chat/gui_sockets/index.php
mod/choice/backup/moodle2/backup_choice_stepslib.php
mod/folder/renderer.php
mod/forum/lib.php
mod/lesson/format.php
mod/quiz/editlib.php
mod/quiz/lang/en/quiz.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/statistics/lang/en/quiz_statistics.php
mod/quiz/report/statistics/report.php
mod/scorm/locallib.php
mod/scorm/player.php
mod/scorm/styles.css
mod/scorm/view.js
mod/scorm/view.php
my/index.php
notes/tests/externallib_test.php [new file with mode: 0644]
question/behaviour/behaviourbase.php
question/engine/lib.php
question/engine/questionattempt.php
question/engine/tests/questionutils_test.php
question/format.php
question/format/aiken/format.php
question/format/blackboard/format.php
question/format/blackboard_six/formatpool.php
question/format/blackboard_six/formatqti.php
question/format/blackboard_six/lang/en/qformat_blackboard_six.php
question/format/examview/format.php
question/format/gift/format.php
question/format/learnwise/format.php
question/format/missingword/format.php
question/format/multianswer/format.php
question/format/multianswer/tests/multianswerformat_test.php
question/format/webct/format.php
question/format/xml/format.php
question/tests/importexport_test.php
question/type/calculatedmulti/styles.css
question/type/match/styles.css
question/type/multianswer/styles.css
question/type/multichoice/styles.css
repository/dropbox/lang/en/repository_dropbox.php
repository/dropbox/lib.php
repository/filepicker.js
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/course.css
theme/formal_white/layout/frontpage.php
theme/formal_white/layout/general.php
theme/formal_white/layout/report.php
theme/formal_white/settings.php
user/editadvanced_form.php
version.php
webservice/externallib.php
webservice/tests/externallib_test.php [new file with mode: 0644]

index f07107c..25614ab 100644 (file)
@@ -35,10 +35,11 @@ if ($hassiteconfig
     $temp->add(new admin_setting_configselect('moodlecourse/showgrades', new lang_string('showgrades'), new lang_string('coursehelpshowgrades'), 1,array(0 => new lang_string('no'), 1 => new lang_string('yes'))));
     $temp->add(new admin_setting_configselect('moodlecourse/showreports', new lang_string('showreports'), '', 0,array(0 => new lang_string('no'), 1 => new lang_string('yes'))));
 
+    $currentmaxbytes = get_config('moodlecourse', 'maxbytes');
     if (isset($CFG->maxbytes)) {
-        $choices = get_max_upload_sizes($CFG->maxbytes);
+        $choices = get_max_upload_sizes($CFG->maxbytes, 0, 0, $currentmaxbytes);
     } else {
-        $choices = get_max_upload_sizes();
+        $choices = get_max_upload_sizes(0, 0, 0, $currentmaxbytes);
     }
     $temp->add(new admin_setting_configselect('moodlecourse/maxbytes', new lang_string('maximumupload'), new lang_string('coursehelpmaximumupload'), key($choices), $choices));
 
@@ -85,6 +86,7 @@ if ($hassiteconfig
     $temp = new admin_settingpage('courserequest', new lang_string('courserequest'));
     $temp->add(new admin_setting_configcheckbox('enablecourserequests', new lang_string('enablecourserequests', 'admin'), new lang_string('configenablecourserequests', 'admin'), 0));
     $temp->add(new admin_settings_coursecat_select('defaultrequestcategory', new lang_string('defaultrequestcategory', 'admin'), new lang_string('configdefaultrequestcategory', 'admin'), 1));
+    $temp->add(new admin_setting_configcheckbox('requestcategoryselection', new lang_string('requestcategoryselection', 'admin'), new lang_string('configrequestcategoryselection', 'admin'), 0));
     $temp->add(new admin_setting_users_with_capability('courserequestnotify', new lang_string('courserequestnotify', 'admin'), new lang_string('configcourserequestnotify2', 'admin'), array(), 'moodle/site:approvecourse'));
     $ADMIN->add('courses', $temp);
 
index bd4eddb..051ac4f 100644 (file)
@@ -6,6 +6,9 @@
 
     $PAGE->set_url('/auth/shibboleth/index.php');
 
+    // Support for WAYFless URLs.
+    $SESSION->wantsurl = optional_param('target', $SESSION->wantsurl, PARAM_LOCALURL);
+
     if (isloggedin() && !isguestuser()) {      // Nothing to do
         if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0)) {
             $urltogo = $SESSION->wantsurl;    /// Because it's an address in this site
index 850bb2e..3578063 100644 (file)
@@ -520,7 +520,7 @@ class backup_enrolments_structure_step extends backup_structure_step {
         $enrols = new backup_nested_element('enrols');
 
         $enrol = new backup_nested_element('enrol', array('id'), array(
-            'enrol', 'status', 'sortorder', 'name', 'enrolperiod', 'enrolstartdate',
+            'enrol', 'status', 'name', 'enrolperiod', 'enrolstartdate',
             'enrolenddate', 'expirynotify', 'expirytreshold', 'notifyall',
             'password', 'cost', 'currency', 'roleid',
             'customint1', 'customint2', 'customint3', 'customint4', 'customint5', 'customint6', 'customint7', 'customint8',
@@ -541,9 +541,8 @@ class backup_enrolments_structure_step extends backup_structure_step {
         $enrol->add_child($userenrolments);
         $userenrolments->add_child($enrolment);
 
-        // Define sources
-
-        $enrol->set_source_table('enrol', array('courseid' => backup::VAR_COURSEID));
+        // Define sources - the instances are restored using the same sortorder, we do not need to store it in xml and deal with it afterwards.
+        $enrol->set_source_sql("SELECT * FROM {enrol} WHERE courseid = :courseid ORDER BY sortorder", array('courseid' => backup::VAR_COURSEID));
 
         // User enrolments only added only if users included
         if ($users) {
index f39e6c6..a43909b 100644 (file)
@@ -71,14 +71,15 @@ class restore_course_task extends restore_task {
             $this->add_step(new restore_course_structure_step('course_info', 'course.xml'));
         }
 
-        // Restore course role assignments and overrides (internally will observe the role_assignments setting)
-        $this->add_step(new restore_ras_and_caps_structure_step('course_ras_and_caps', 'roles.xml'));
-
         // Restore course enrolments (plugins and membership). Conditionally prevented for any IMPORT/HUB operation
         if ($this->plan->get_mode() != backup::MODE_IMPORT && $this->plan->get_mode() != backup::MODE_HUB) {
             $this->add_step(new restore_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
         }
 
+        // Restore course role assignments and overrides (internally will observe the role_assignments setting),
+        // this must be done after all users are enrolled.
+        $this->add_step(new restore_ras_and_caps_structure_step('course_ras_and_caps', 'roles.xml'));
+
         // Restore course filters (conditionally)
         if ($this->get_setting_value('filters')) {
             $this->add_step(new restore_filters_structure_step('course_filters', 'filters.xml'));
index 40b06ac..99365ba 100644 (file)
@@ -154,6 +154,22 @@ abstract class restore_qtype_plugin extends restore_plugin {
                        AND ' . $DB->sql_compare_text('answer', 255) . ' = ' . $DB->sql_compare_text('?', 255);
             $params = array($newquestionid, $data->answertext);
             $newitemid = $DB->get_field_sql($sql, $params);
+
+            // Not able to find the answer, let's try cleaning the answertext
+            // of all the question answers in DB as slower fallback. MDL-30018.
+            if (!$newitemid) {
+                $params = array('question' => $newquestionid);
+                $answers = $DB->get_records('question_answers', $params, '', 'id, answer');
+                foreach ($answers as $answer) {
+                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
+                    $clean = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is','', $answer->answer); // Clean CTRL chars.
+                    $clean = preg_replace("/\r\n|\r/", "\n", $clean); // Normalize line ending.
+                    if ($clean === $data->answertext) {
+                        $newitemid = $data->id;
+                    }
+                }
+            }
+
             // If we haven't found the newitemid, something has gone really wrong, question in DB
             // is missing answers, exception
             if (!$newitemid) {
index 6917f1c..f95a5b9 100644 (file)
@@ -112,6 +112,12 @@ class restore_root_task extends restore_task {
         $users->get_ui()->set_changeable($changeable);
         $this->add_setting($users);
 
+        $rootenrolmanual = new restore_users_setting('enrol_migratetomanual', base_setting::IS_BOOLEAN, false);
+        $rootenrolmanual->set_ui(new backup_setting_ui_checkbox($rootenrolmanual, get_string('rootenrolmanual', 'backup')));
+        $rootenrolmanual->get_ui()->set_changeable(enrol_is_enabled('manual'));
+        $this->add_setting($rootenrolmanual);
+        $users->add_dependency($rootenrolmanual);
+
         // Define role_assignments (dependent of users)
         $defaultvalue = false;                      // Safer default
         $changeable = false;
index 8742f0a..77f82ae 100644 (file)
@@ -1407,13 +1407,14 @@ class restore_course_structure_step extends restore_structure_step {
 
 /*
  * Structure step that will read the roles.xml file (at course/activity/block levels)
- * containig all the role_assignments and overrides for that context. If corresponding to
+ * containing all the role_assignments and overrides for that context. If corresponding to
  * one mapped role, they will be applied to target context. Will observe the role_assignments
  * setting to decide if ras are restored.
- * Note: only ras with component == null are restored as far as the any ra with component
- * is handled by one enrolment plugin, hence it will createt the ras later
+ *
+ * Note: this needs to be executed after all users are enrolled.
  */
 class restore_ras_and_caps_structure_step extends restore_structure_step {
+    protected $plugins = null;
 
     protected function define_structure() {
 
@@ -1462,15 +1463,15 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
             role_assign($newroleid, $newuserid, $contextid);
 
         } else if ((strpos($data->component, 'enrol_') === 0)) {
-            // Deal with enrolment roles
+            // Deal with enrolment roles - ignore the component and just find out the instance via new id,
+            // it is possible that enrolment was restored using different plugin type.
+            if (!isset($this->plugins)) {
+                $this->plugins = enrol_get_plugins(true);
+            }
             if ($enrolid = $this->get_mappingid('enrol', $data->itemid)) {
-                if ($component = $DB->get_field('enrol', 'component', array('id'=>$enrolid))) {
-                    //note: we have to verify component because it might have changed
-                    if ($component === 'enrol_manual') {
-                        // manual is a special case, we do not use components - this owudl happen when converting from other plugin
-                        role_assign($newroleid, $newuserid, $contextid); //TODO: do we need modifierid?
-                    } else {
-                        role_assign($newroleid, $newuserid, $contextid, $component, $enrolid); //TODO: do we need modifierid?
+                if ($instance = $DB->get_record('enrol', array('id'=>$enrolid))) {
+                    if (isset($this->plugins[$instance->enrol])) {
+                        $this->plugins[$instance->enrol]->restore_role_assignment($instance, $newroleid, $newuserid, $contextid);
                     }
                 }
             }
@@ -1496,6 +1497,9 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
  * enrolments, performing all the mappings and/or movements required
  */
 class restore_enrolments_structure_step extends restore_structure_step {
+    protected $enrolsynced = false;
+    protected $plugins = null;
+    protected $originalstatus = array();
 
     /**
      * Conditionally decide if this step should be executed.
@@ -1542,82 +1546,103 @@ class restore_enrolments_structure_step extends restore_structure_step {
         global $DB;
 
         $data = (object)$data;
-        $oldid = $data->id; // We'll need this later
+        $oldid = $data->id; // We'll need this later.
+        unset($data->id);
 
-        $restoretype = plugin_supports('enrol', $data->enrol, ENROL_RESTORE_TYPE, null);
+        $this->originalstatus[$oldid] = $data->status;
 
-        if ($restoretype !== ENROL_RESTORE_EXACT and $restoretype !== ENROL_RESTORE_NOUSERS) {
-            // TODO: add complex restore support via custom class
-            debugging("Skipping '{$data->enrol}' enrolment plugin. Will be implemented before 2.0 release", DEBUG_DEVELOPER);
+        if (!$courserec = $DB->get_record('course', array('id' => $this->get_courseid()))) {
             $this->set_mapping('enrol', $oldid, 0);
             return;
         }
 
-        // Perform various checks to decide what to do with the enrol plugin
-        if (!array_key_exists($data->enrol, enrol_get_plugins(false))) {
-            // TODO: decide if we want to switch to manual enrol - we need UI for this
-            debugging("Enrol plugin data can not be restored because it is not installed");
-            $this->set_mapping('enrol', $oldid, 0);
-            return;
-
+        if (!isset($this->plugins)) {
+            $this->plugins = enrol_get_plugins(true);
         }
-        if (!enrol_is_enabled($data->enrol)) {
-            // TODO: decide if we want to switch to manual enrol - we need UI for this
-            debugging("Enrol plugin data can not be restored because it is not enabled");
-            $this->set_mapping('enrol', $oldid, 0);
-            return;
+
+        if (!$this->enrolsynced) {
+            // Make sure that all plugin may create instances and enrolments automatically
+            // before the first instance restore - this is suitable especially for plugins
+            // that synchronise data automatically using course->idnumber or by course categories.
+            foreach ($this->plugins as $plugin) {
+                $plugin->restore_sync_course($courserec);
+            }
+            $this->enrolsynced = true;
         }
 
-        // map standard fields - plugin has to process custom fields from own restore class
-        $data->roleid = $this->get_mappingid('role', $data->roleid);
-        //TODO: should we move the enrol start and end date here?
+        // Map standard fields - plugin has to process custom fields manually.
+        $data->roleid   = $this->get_mappingid('role', $data->roleid);
+        $data->courseid = $courserec->id;
 
-        // always add instance, if the course does not support multiple instances it just returns NULL
-        $enrol = enrol_get_plugin($data->enrol);
-        $courserec = $DB->get_record('course', array('id' => $this->get_courseid())); // Requires object, uses only id!!
-        if ($newitemid = $enrol->add_instance($courserec, (array)$data)) {
-            // ok
-        } else {
-            if ($instances = $DB->get_records('enrol', array('courseid'=>$courserec->id, 'enrol'=>$data->enrol))) {
-                // most probably plugin that supports only one instance
-                $newitemid = key($instances);
+        if ($this->get_setting_value('enrol_migratetomanual')) {
+            unset($data->sortorder); // Remove useless sortorder from <2.4 backups.
+            if (!enrol_is_enabled('manual')) {
+                $this->set_mapping('enrol', $oldid, 0);
+                return;
+            }
+            if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) {
+                $instance = reset($instances);
+                $this->set_mapping('enrol', $oldid, $instance->id);
             } else {
-                debugging('Can not create new enrol instance or reuse existing');
-                $newitemid = 0;
+                if ($data->enrol === 'manual') {
+                    $instanceid = $this->plugins['manual']->add_instance($courserec, (array)$data);
+                } else {
+                    $instanceid = $this->plugins['manual']->add_default_instance($courserec);
+                }
+                $this->set_mapping('enrol', $oldid, $instanceid);
             }
-        }
 
-        if ($restoretype === ENROL_RESTORE_NOUSERS) {
-            // plugin requests to prevent restore of any users
-            $newitemid = 0;
+        } else {
+            if (!enrol_is_enabled($data->enrol) or !isset($this->plugins[$data->enrol])) {
+                debugging("Enrol plugin data can not be restored because it is not enabled, use migration to manual enrolments");
+                $this->set_mapping('enrol', $oldid, 0);
+                return;
+            }
+            if ($task = $this->get_task() and $task->get_target() == backup::TARGET_NEW_COURSE) {
+                // Let's keep the sortorder in old backups.
+            } else {
+                // Prevent problems with colliding sortorders in old backups,
+                // new 2.4 backups do not need sortorder because xml elements are ordered properly.
+                unset($data->sortorder);
+            }
+            // Note: plugin is responsible for setting up the mapping, it may also decide to migrate to different type.
+            $this->plugins[$data->enrol]->restore_instance($this, $data, $courserec, $oldid);
         }
-
-        $this->set_mapping('enrol', $oldid, $newitemid);
     }
 
     /**
-     * Create user enrolments
+     * Create user enrolments.
      *
      * This has to be called after creation of enrolment instances
      * and before adding of role assignments.
      *
+     * Roles are assigned in restore_ras_and_caps_structure_step::process_assignment() processing afterwards.
+     *
      * @param mixed $data
      * @return void
      */
     public function process_enrolment($data) {
         global $DB;
 
+        if (!isset($this->plugins)) {
+            $this->plugins = enrol_get_plugins(true);
+        }
+
         $data = (object)$data;
 
-        // Process only if parent instance have been mapped
+        // Process only if parent instance have been mapped.
         if ($enrolid = $this->get_new_parentid('enrol')) {
+            $oldinstancestatus = ENROL_INSTANCE_ENABLED;
+            $oldenrolid = $this->get_old_parentid('enrol');
+            if (isset($this->originalstatus[$oldenrolid])) {
+                $oldinstancestatus = $this->originalstatus[$oldenrolid];
+            }
             if ($instance = $DB->get_record('enrol', array('id'=>$enrolid))) {
-                // And only if user is a mapped one
+                // And only if user is a mapped one.
                 if ($userid = $this->get_mappingid('user', $data->userid)) {
-                    $enrol = enrol_get_plugin($instance->enrol);
-                    //TODO: do we need specify modifierid?
-                    $enrol->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
-                    //note: roles are assigned in restore_ras_and_caps_structure_step::process_assignment() processing above
+                    if (isset($this->plugins[$instance->enrol])) {
+                        $this->plugins[$instance->enrol]->restore_user_enrolment($this, $data, $instance, $userid, $oldinstancestatus);
+                    }
                 }
             }
         }
index 7a1cfa2..da34487 100644 (file)
@@ -278,6 +278,7 @@ abstract class restore_structure_step extends restore_step {
     /**
      * As far as restore structure steps are implementing restore_plugin stuff, they need to
      * have the parent task available for wrapping purposes (get course/context....)
+     * @return restore_task|null
      */
     public function get_task() {
         return $this->task;
index 817e3ac..c32b75e 100644 (file)
@@ -23,8 +23,7 @@ YUI.add('moodle-block_community-comments', function(Y) {
                     bodyContent:Y.one('#commentoverlay-'+commentid).get('innerHTML'),
                     visible: false, //by default it is not displayed
                     lightbox : false,
-                    zIndex:100,
-                    height: '350px'
+                    zIndex:100
                 });
 
                 this.overlays[commentid].get('contentBox').one('.commenttitle').remove();
index 5560a32..5e4a56f 100644 (file)
@@ -863,6 +863,12 @@ M.core_dock.genericblock.prototype = {
             return;
         }
 
+        // Disable the skip anchor when docking
+        var skipanchor = node.previous();
+        if (skipanchor.hasClass('skip-block')) {
+            skipanchor.hide();
+        }
+
         var blockclass = (function(classes){
             var r = /(^|\s)(block_[a-zA-Z0-9_]+)(\s|$)/;
             var m = r.exec(classes);
@@ -937,6 +943,12 @@ M.core_dock.genericblock.prototype = {
     return_to_block : function(dockitem) {
         var placeholder = this.Y.one('#content_placeholder_'+this.id);
 
+        // Enable the skip anchor when going back to block mode
+        var skipanchor = placeholder.previous();
+        if (skipanchor.hasClass('skip-block')) {
+            skipanchor.show();
+        }
+
         if (this.cachedcontentnode.one('.header')) {
             this.cachedcontentnode.one('.header').insert(dockitem.contents, 'after');
         } else {
index 086983d..38f8856 100644 (file)
 
         $r = html_writer::start_tag('li');
             $r.= html_writer::start_tag('div',array('class'=>'link'));
-                $r.= html_writer::link(clean_param($link,PARAM_URL), s($title), array('onclick'=>'this.target="_blank"'));
+                $r.= html_writer::link($link, s($title), array('onclick'=>'this.target="_blank"'));
             $r.= html_writer::end_tag('div');
 
             if($this->config->display_description && !empty($description)){
index f88d867..1b822cc 100644 (file)
@@ -27,6 +27,7 @@
 
 require_once("../config.php");
 require_once($CFG->dirroot.'/course/lib.php');
+require_once($CFG->libdir.'/textlib.class.php');
 
 $id = required_param('id', PARAM_INT); // Category id
 $page = optional_param('page', 0, PARAM_INT); // which page to show
@@ -75,7 +76,8 @@ $sesskeyprovided = !empty($sesskey) && confirm_sesskey($sesskey);
 // Process any category actions.
 if ($canmanage && $resort && $sesskeyprovided) {
     // Resort the category if requested
-    if ($courses = get_courses($category->id, "fullname ASC", 'c.id,c.fullname,c.sortorder')) {
+    if ($courses = get_courses($category->id, '', 'c.id,c.fullname,c.sortorder')) {
+        collatorlib::asort_objects_by_property($courses, 'fullname', collatorlib::SORT_NATURAL);
         $i = 1;
         foreach ($courses as $course) {
             $DB->set_field('course', 'sortorder', $category->sortorder+$i, array('id'=>$course->id));
index 355e790..5f94469 100644 (file)
@@ -156,7 +156,7 @@ class course_edit_form extends moodleform {
         $mform->addHelpButton('showreports', 'showreports');
         $mform->setDefault('showreports', $courseconfig->showreports);
 
-        $choices = get_max_upload_sizes($CFG->maxbytes);
+        $choices = get_max_upload_sizes($CFG->maxbytes, 0, 0, $course->maxbytes);
         $mform->addElement('select', 'maxbytes', get_string('maximumupload'), $choices);
         $mform->addHelpButton('maxbytes', 'maximumupload');
         $mform->setDefault('maxbytes', $courseconfig->maxbytes);
index f410e07..28ec82a 100644 (file)
@@ -36,7 +36,7 @@ M.course.format.get_config = function() {
 M.course.format.swap_sections = function(Y, node1, node2) {
     var CSS = {
         COURSECONTENT : 'course-content',
-        SECTIONADDMENUS : 'section_add_menus',
+        SECTIONADDMENUS : 'section_add_menus'
     };
 
     var sectionlist = Y.Node.all('.'+CSS.COURSECONTENT+' '+M.course.format.get_section_selector(Y));
index cf8f372..c4233f4 100644 (file)
@@ -4144,6 +4144,11 @@ class course_request {
         global $USER, $DB, $CFG;
         $data->requester = $USER->id;
 
+        // Setting the default category is none set.
+        if (empty($data->category) || empty($CFG->requestcategoryselection)) {
+            $data->category = $CFG->defaultrequestcategory;
+        }
+
         // Summary is a required field so copy the text over
         $data->summary       = $data->summary_editor['text'];
         $data->summaryformat = $data->summary_editor['format'];
@@ -4293,7 +4298,6 @@ class course_request {
 
         $user = $DB->get_record('user', array('id' => $this->properties->requester, 'deleted'=>0), '*', MUST_EXIST);
 
-        $category = get_course_category($CFG->defaultrequestcategory);
         $courseconfig = get_config('moodlecourse');
 
         // Transfer appropriate settings
@@ -4302,6 +4306,14 @@ class course_request {
         unset($data->reason);
         unset($data->requester);
 
+        // If the current user does not have the rights to change the category, or if the
+        // category does not exist, we set the default category to the course to be approved.
+        // The system level is used because the capability moodle/site:approvecourse is based on a system level.
+        if (!has_capability('moodle/course:changecategory', context_system::instance()) ||
+                (!$category = get_course_category($data->category))) {
+            $category = get_course_category($CFG->defaultrequestcategory);
+        }
+
         // Set category
         $data->category = $category->id;
         $data->sortorder = $category->sortorder; // place as the first in category
index 205a429..8ac5832 100644 (file)
@@ -101,8 +101,8 @@ if (empty($pending)) {
     $table = new html_table();
     $table->attributes['class'] = 'pendingcourserequests generaltable';
     $table->align = array('center', 'center', 'center', 'center', 'center', 'center');
-    $table->head = array(get_string('shortnamecourse'), get_string('fullnamecourse'),
-            get_string('requestedby'), get_string('summary'), get_string('requestreason'), get_string('action'));
+    $table->head = array(get_string('shortnamecourse'), get_string('fullnamecourse'), get_string('requestedby'),
+            get_string('summary'), get_string('category'), get_string('requestreason'), get_string('action'));
 
     foreach ($pending as $course) {
         $course = new course_request($course);
@@ -110,11 +110,22 @@ if (empty($pending)) {
         // Check here for shortname collisions and warn about them.
         $course->check_shortname_collision();
 
+        // Retreiving category name.
+        // If the user does not have the capability to change the category, we fallback on the default one.
+        // Else, the category proposed is fetched, but we fallback on the default one if we can't find it.
+        // It is just a matter of displaying the right information because the logic when approving the category
+        // proceeds the same way. The system context level is used as moodle/site:approvecourse uses it.
+        if (!has_capability('moodle/course:changecategory', context_system::instance()) ||
+                (!$category = get_course_category($course->category))) {
+            $category = get_course_category($CFG->defaultrequestcategory);
+        }
+
         $row = array();
         $row[] = format_string($course->shortname);
         $row[] = format_string($course->fullname);
         $row[] = fullname($course->get_requester());
         $row[] = $course->summary;
+        $row[] = format_string($category->name);
         $row[] = format_string($course->reason);
         $row[] = $OUTPUT->single_button(new moodle_url($baseurl, array('approve' => $course->id, 'sesskey' => sesskey())), get_string('approve'), 'get') .
                  $OUTPUT->single_button(new moodle_url($baseurl, array('reject' => $course->id)), get_string('rejectdots'), 'get');
index e74e40b..867a684 100644 (file)
@@ -42,7 +42,7 @@ require_once($CFG->libdir.'/formslib.php');
  */
 class course_request_form extends moodleform {
     function definition() {
-        global $DB, $USER;
+        global $CFG, $DB, $USER;
 
         $mform =& $this->_form;
 
@@ -68,6 +68,15 @@ class course_request_form extends moodleform {
         $mform->addRule('shortname', get_string('missingshortname'), 'required', null, 'client');
         $mform->setType('shortname', PARAM_TEXT);
 
+        if (!empty($CFG->requestcategoryselection)) {
+            $displaylist = array();
+            $parentlist = array();
+            make_categories_list($displaylist, $parentlist, '');
+            $mform->addElement('select', 'category', get_string('category'), $displaylist);
+            $mform->setDefault('category', $CFG->defaultrequestcategory);
+            $mform->addHelpButton('category', 'category');
+        }
+
         $mform->addElement('editor', 'summary_editor', get_string('summary'), null, course_request::summary_editor_options());
         $mform->addHelpButton('summary_editor', 'coursesummary');
         $mform->setType('summary_editor', PARAM_RAW);
index 42b8057..0004f7d 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+global $CFG;
+require_once($CFG->dirroot.'/course/lib.php');
 
 class courselib_testcase extends advanced_testcase {
 
+    public function test_create_course() {
+        global $DB;
+        $this->resetAfterTest(true);
+        $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+
+        $course = new stdClass();
+        $course->fullname = 'Apu loves Unit Təsts';
+        $course->shortname = 'Spread the lŭve';
+        $course->idnumber = '123';
+        $course->summary = 'Awesome!';
+        $course->summaryformat = FORMAT_PLAIN;
+        $course->format = 'topics';
+        $course->newsitems = 0;
+        $course->numsections = 5;
+        $course->category = $defaultcategory;
+
+        $created = create_course($course);
+        $context = context_course::instance($created->id);
+
+        // Compare original and created.
+        $original = (array) $course;
+        $this->assertEquals($original, array_intersect_key((array) $created, $original));
+
+        // Ensure default section is created.
+        $sectioncreated = $DB->record_exists('course_sections', array('course' => $created->id, 'section' => 0));
+        $this->assertTrue($sectioncreated);
+
+        // Ensure blocks have been associated to the course.
+        $blockcount = $DB->count_records('block_instances', array('parentcontextid' => $context->id));
+        $this->assertGreaterThan(0, $blockcount);
+    }
+
     public function test_reorder_sections() {
         global $DB;
         $this->resetAfterTest(true);
diff --git a/course/tests/courserequest_test.php b/course/tests/courserequest_test.php
new file mode 100644 (file)
index 0000000..d376772
--- /dev/null
@@ -0,0 +1,148 @@
+<?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/>.
+
+/**
+ * Course request related unit tests
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2012 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot.'/course/lib.php');
+
+class courserequest_testcase extends advanced_testcase {
+
+    public function test_create_request() {
+        global $DB, $USER;
+        $this->resetAfterTest(true);
+
+        $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+        set_config('enablecourserequests', 1);
+        set_config('requestcategoryselection', 0);
+        set_config('defaultrequestcategory', $defaultcategory);
+
+        // Create some categories.
+        $cat1 = $this->getDataGenerator()->create_category();
+        $cat2 = $this->getDataGenerator()->create_category();
+        $cat3 = $this->getDataGenerator()->create_category();
+
+        // Basic course request.
+        $data = new stdClass();
+        $data->fullname = 'Həllo World!';
+        $data->shortname = 'Hi th€re!';
+        $data->summary_editor['text'] = 'Lorem Ipsum ©';
+        $data->summary_editor['format'] = FORMAT_HTML;
+        $data->reason = 'Because PHP Unit is cool.';
+        $cr = course_request::create($data);
+
+        $this->assertEquals($data->fullname, $cr->fullname);
+        $this->assertEquals($data->shortname, $cr->shortname);
+        $this->assertEquals($data->summary_editor['text'], $cr->summary);
+        $this->assertEquals($data->summary_editor['format'], $cr->summaryformat);
+        $this->assertEquals($data->reason, $cr->reason);
+        $this->assertEquals($USER->id, $cr->requester);
+        $this->assertEquals($defaultcategory, $cr->category);
+
+        // Request with category but category selection not allowed.
+        set_config('defaultrequestcategory', $cat2->id);
+        $data->category = $cat1->id;
+        $cr = course_request::create($data);
+        $this->assertEquals($cat2->id, $cr->category);
+
+        // Request with category different than default and category selection allowed.
+        set_config('defaultrequestcategory', $cat3->id);
+        set_config('requestcategoryselection', 1);
+        $data->category = $cat1->id;
+        $cr = course_request::create($data);
+        $this->assertEquals($cat1->id, $cr->category);
+    }
+
+    public function test_approve_request() {
+        global $DB;
+        $this->resetAfterTest(true);
+        $this->preventResetByRollback();
+
+        $this->setAdminUser();
+        $defaultcategory = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
+        set_config('enablecourserequests', 1);
+        set_config('requestcategoryselection', 0);
+        set_config('defaultrequestcategory', $defaultcategory);
+
+        // Create some categories.
+        $cat1 = $this->getDataGenerator()->create_category();
+        $cat2 = $this->getDataGenerator()->create_category();
+
+        $data = new stdClass();
+        $data->fullname = 'Həllo World!';
+        $data->shortname = 'Hi th€re!';
+        $data->summary_editor['text'] = 'Lorem Ipsum ©';
+        $data->summary_editor['format'] = FORMAT_HTML;
+        $data->reason = 'Because PHP Unit is cool.';
+
+        // Test without category.
+        $cr = course_request::create($data);
+        $id = $cr->approve();
+        $this->assertDebuggingCalled(); // Caused by sending of message.
+        $course = $DB->get_record('course', array('id' => $id));
+        $this->assertEquals($data->fullname, $course->fullname);
+        $this->assertEquals($data->shortname, $course->shortname);
+        $this->assertEquals($data->summary_editor['text'], $course->summary);
+        $this->assertEquals($data->summary_editor['format'], $course->summaryformat);
+        $this->assertEquals(1, $course->requested);
+        $this->assertEquals($defaultcategory, $course->category);
+
+        // Test with category.
+        set_config('requestcategoryselection', 1);
+        set_config('defaultrequestcategory', $cat2->id);
+        $data->shortname .= ' 2nd';
+        $data->category = $cat1->id;
+        $cr = course_request::create($data);
+        $id = $cr->approve();
+        $this->assertDebuggingCalled(); // Caused by sending of message.
+        $course = $DB->get_record('course', array('id' => $id));
+        $this->assertEquals($data->category, $course->category);
+    }
+
+    public function test_reject_request() {
+        global $DB;
+        $this->resetAfterTest(true);
+        $this->preventResetByRollback();
+        $this->setAdminUser();
+        set_config('enablecourserequests', 1);
+        set_config('requestcategoryselection', 0);
+        set_config('defaultrequestcategory', $DB->get_field_select('course_categories', "MIN(id)", "parent=0"));
+
+        $data = new stdClass();
+        $data->fullname = 'Həllo World!';
+        $data->shortname = 'Hi th€re!';
+        $data->summary_editor['text'] = 'Lorem Ipsum ©';
+        $data->summary_editor['format'] = FORMAT_HTML;
+        $data->reason = 'Because PHP Unit is cool.';
+
+        $cr = course_request::create($data);
+        $this->assertTrue($DB->record_exists('course_request', array('id' => $cr->id)));
+        $cr->reject('Sorry!');
+        $this->assertDebuggingCalled(); // Caused by sending of message.
+        $this->assertFalse($DB->record_exists('course_request', array('id' => $cr->id)));
+    }
+
+}
index 08f8b4a..984b9ed 100644 (file)
@@ -165,20 +165,28 @@ class core_course_external_testcase extends externallib_advanced_testcase {
         global $DB;
 
         $this->resetAfterTest(true);
+
+        $generatedcats = array();
         $category1data['idnumber'] = 'idnumbercat1';
         $category1data['name'] = 'Category 1 for PHPunit test';
         $category1data['description'] = 'Category 1 description';
         $category1data['descriptionformat'] = FORMAT_MOODLE;
         $category1  = self::getDataGenerator()->create_category($category1data);
+        $generatedcats[$category1->id] = $category1;
         $category2  = self::getDataGenerator()->create_category(
                 array('parent' => $category1->id));
+        $generatedcats[$category2->id] = $category2;
         $category6  = self::getDataGenerator()->create_category(
                 array('parent' => $category1->id, 'visible' => 0));
+        $generatedcats[$category6->id] = $category6;
         $category3  = self::getDataGenerator()->create_category();
+        $generatedcats[$category3->id] = $category3;
         $category4  = self::getDataGenerator()->create_category(
                 array('parent' => $category3->id));
+        $generatedcats[$category4->id] = $category4;
         $category5  = self::getDataGenerator()->create_category(
                 array('parent' => $category4->id));
+        $generatedcats[$category5->id] = $category5;
 
         // Set the required capabilities by the external function.
         $context = context_system::instance();
@@ -193,11 +201,13 @@ class core_course_external_testcase extends externallib_advanced_testcase {
         $this->assertEquals(2, count($categories));
 
         // Check the return values
-        $this->assertEquals($categories[0]['id'], $category1->id);
-        $this->assertEquals($categories[0]['idnumber'], $category1->idnumber);
-        $this->assertEquals($categories[0]['name'], $category1->name);
-        $this->assertEquals($categories[0]['description'], $category1->description);
-        $this->assertEquals($categories[0]['descriptionformat'], FORMAT_HTML);
+        foreach ($categories as $category) {
+            $generatedcat = $generatedcats[$category['id']];
+            $this->assertEquals($category['idnumber'], $generatedcat->idnumber);
+            $this->assertEquals($category['name'], $generatedcat->name);
+            $this->assertEquals($category['description'], $generatedcat->description);
+            $this->assertEquals($category['descriptionformat'], FORMAT_HTML);
+        }
 
         // Check different params.
         $categories = core_course_external::get_categories(array(
@@ -440,13 +450,17 @@ class core_course_external_testcase extends externallib_advanced_testcase {
 
         $this->resetAfterTest(true);
 
+        $generatedcourses = array();
         $coursedata['idnumber'] = 'idnumbercourse1';
         $coursedata['fullname'] = 'Course 1 for PHPunit test';
         $coursedata['summary'] = 'Course 1 description';
         $coursedata['summaryformat'] = FORMAT_MOODLE;
         $course1  = self::getDataGenerator()->create_course($coursedata);
+        $generatedcourses[$course1->id] = $course1;
         $course2  = self::getDataGenerator()->create_course();
+        $generatedcourses[$course2->id] = $course2;
         $course3  = self::getDataGenerator()->create_course();
+        $generatedcourses[$course3->id] = $course3;
 
         // Set the required capabilities by the external function.
         $context = context_system::instance();
@@ -464,33 +478,33 @@ class core_course_external_testcase extends externallib_advanced_testcase {
         // Check we retrieve the good total number of categories.
         $this->assertEquals(2, count($courses));
 
-        // Check the return values for course 1
-        $dbcourse = $DB->get_record('course', array('id' => $course1->id));
-        $this->assertEquals($courses[0]['id'], $dbcourse->id);
-        $this->assertEquals($courses[0]['idnumber'], $coursedata['idnumber']);
-        $this->assertEquals($courses[0]['fullname'], $coursedata['fullname']);
-        $this->assertEquals($courses[0]['summary'], $coursedata['summary']);
-        $this->assertEquals($courses[0]['summaryformat'], FORMAT_HTML);
-        $this->assertEquals($courses[0]['shortname'], $dbcourse->shortname);
-        $this->assertEquals($courses[0]['categoryid'], $dbcourse->category);
-        $this->assertEquals($courses[0]['format'], $dbcourse->format);
-        $this->assertEquals($courses[0]['showgrades'], $dbcourse->showgrades);
-        $this->assertEquals($courses[0]['newsitems'], $dbcourse->newsitems);
-        $this->assertEquals($courses[0]['startdate'], $dbcourse->startdate);
-        $this->assertEquals($courses[0]['numsections'], $dbcourse->numsections);
-        $this->assertEquals($courses[0]['maxbytes'], $dbcourse->maxbytes);
-        $this->assertEquals($courses[0]['showreports'], $dbcourse->showreports);
-        $this->assertEquals($courses[0]['visible'], $dbcourse->visible);
-        $this->assertEquals($courses[0]['hiddensections'], $dbcourse->hiddensections);
-        $this->assertEquals($courses[0]['groupmode'], $dbcourse->groupmode);
-        $this->assertEquals($courses[0]['groupmodeforce'], $dbcourse->groupmodeforce);
-        $this->assertEquals($courses[0]['defaultgroupingid'], $dbcourse->defaultgroupingid);
-        $this->assertEquals($courses[0]['completionnotify'], $dbcourse->completionnotify);
-        $this->assertEquals($courses[0]['lang'], $dbcourse->lang);
-        $this->assertEquals($courses[0]['forcetheme'], $dbcourse->theme);
-        $this->assertEquals($courses[0]['completionstartonenrol'], $dbcourse->completionstartonenrol);
-        $this->assertEquals($courses[0]['enablecompletion'], $dbcourse->enablecompletion);
-        $this->assertEquals($courses[0]['completionstartonenrol'], $dbcourse->completionstartonenrol);
+        foreach ($courses as $course) {
+            $dbcourse = $generatedcourses[$course['id']];
+            $this->assertEquals($course['idnumber'], $dbcourse->idnumber);
+            $this->assertEquals($course['fullname'], $dbcourse->fullname);
+            $this->assertEquals($course['summary'], $dbcourse->summary);
+            $this->assertEquals($course['summaryformat'], FORMAT_HTML);
+            $this->assertEquals($course['shortname'], $dbcourse->shortname);
+            $this->assertEquals($course['categoryid'], $dbcourse->category);
+            $this->assertEquals($course['format'], $dbcourse->format);
+            $this->assertEquals($course['showgrades'], $dbcourse->showgrades);
+            $this->assertEquals($course['newsitems'], $dbcourse->newsitems);
+            $this->assertEquals($course['startdate'], $dbcourse->startdate);
+            $this->assertEquals($course['numsections'], $dbcourse->numsections);
+            $this->assertEquals($course['maxbytes'], $dbcourse->maxbytes);
+            $this->assertEquals($course['showreports'], $dbcourse->showreports);
+            $this->assertEquals($course['visible'], $dbcourse->visible);
+            $this->assertEquals($course['hiddensections'], $dbcourse->hiddensections);
+            $this->assertEquals($course['groupmode'], $dbcourse->groupmode);
+            $this->assertEquals($course['groupmodeforce'], $dbcourse->groupmodeforce);
+            $this->assertEquals($course['defaultgroupingid'], $dbcourse->defaultgroupingid);
+            $this->assertEquals($course['completionnotify'], $dbcourse->completionnotify);
+            $this->assertEquals($course['lang'], $dbcourse->lang);
+            $this->assertEquals($course['forcetheme'], $dbcourse->theme);
+            $this->assertEquals($course['completionstartonenrol'], $dbcourse->completionstartonenrol);
+            $this->assertEquals($course['enablecompletion'], $dbcourse->enablecompletion);
+            $this->assertEquals($course['completionstartonenrol'], $dbcourse->completionstartonenrol);
+        }
 
         // Get all courses in the DB
         $courses = core_course_external::get_courses(array());
index e6525e3..4fed790 100644 (file)
@@ -92,4 +92,15 @@ class enrol_category_plugin extends enrol_plugin {
         require_once("$CFG->dirroot/enrol/category/locallib.php");
         enrol_category_sync_course($course);
     }
+
+    /**
+     * Automatic enrol sync executed during restore.
+     * Useful for automatic sync by course->idnumber or course category.
+     * @param stdClass $course course record
+     */
+    public function restore_sync_course($course) {
+        global $CFG;
+        require_once("$CFG->dirroot/enrol/category/locallib.php");
+        enrol_category_sync_course($course);
+    }
 }
index ebb06fc..eacef31 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
 /**
  * Adds new instance of enrol_cohort to specified course.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -28,7 +26,7 @@ require('../../config.php');
 require_once("$CFG->dirroot/enrol/cohort/addinstance_form.php");
 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
 
-$id = required_param('id', PARAM_INT); // course id
+$id = required_param('id', PARAM_INT); // Course id.
 
 $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
 $context = context_course::instance($course->id, MUST_EXIST);
@@ -42,7 +40,7 @@ $PAGE->set_pagelayout('admin');
 
 navigation_node::override_active_url(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
 
-// Try and make the manage instances node on the navigation active
+// Try and make the manage instances node on the navigation active.
 $courseadmin = $PAGE->settingsnav->get('courseadmin');
 if ($courseadmin && $courseadmin->get('users') && $courseadmin->get('users')->get('manageinstances')) {
     $courseadmin->get('users')->get('manageinstances')->make_active();
index c209493..9658b49 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
 /**
  * Adds instance form
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -29,17 +27,20 @@ defined('MOODLE_INTERNAL') || die();
 require_once("$CFG->libdir/formslib.php");
 
 class enrol_cohort_addinstance_form extends moodleform {
+
+    protected $course;
+
     function definition() {
         global $CFG, $DB;
 
         $mform  = $this->_form;
-        $course = $this->_customdata;
-        $coursecontext = context_course::instance($course->id);
+        $this->course = $this->_customdata;
+        $coursecontext = context_course::instance($this->course->id);
 
         $enrol = enrol_get_plugin('cohort');
 
         $cohorts = array('' => get_string('choosedots'));
-        list($sqlparents, $params) = $DB->get_in_or_equal(get_parent_contexts($coursecontext));
+        list($sqlparents, $params) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids());
         $sql = "SELECT id, name, contextid
                   FROM {cohort}
                  WHERE contextid $sqlparents
@@ -56,7 +57,7 @@ class enrol_cohort_addinstance_form extends moodleform {
 
         $roles = get_assignable_roles($coursecontext);
         $roles[0] = get_string('none');
-        $roles = array_reverse($roles, true); // descending default sortorder
+        $roles = array_reverse($roles, true); // Descending default sortorder.
 
         $mform->addElement('header','general', get_string('pluginname', 'enrol_cohort'));
 
@@ -72,8 +73,18 @@ class enrol_cohort_addinstance_form extends moodleform {
 
         $this->add_action_buttons(true, get_string('addinstance', 'enrol'));
 
-        $this->set_data(array('id'=>$course->id));
+        $this->set_data(array('id'=>$this->course->id));
     }
 
-    //TODO: validate duplicate role-cohort does not exist
+    function validation($data, $files) {
+        global $DB;
+
+        $errors = parent::validation($data, $files);
+
+        if ($DB->record_exists('enrol', array('roleid'=>$data['roleid'], 'customint1'=>$data['cohortid'], 'courseid'=>$this->course->id, 'enrol'=>'cohort'))) {
+            $errors['cohortid'] = get_string('instanceexists', 'enrol_cohort');
+        }
+
+        return $errors;
+    }
 }
index b09fd4d..c831ecd 100644 (file)
@@ -20,8 +20,7 @@
  * The general idea behind this file is that any errors should throw exceptions
  * which will be returned and acted upon by the calling AJAX script.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2011 Sam Hemelryk
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -33,7 +32,7 @@ require_once($CFG->dirroot.'/enrol/locallib.php');
 require_once($CFG->dirroot.'/enrol/cohort/locallib.php');
 require_once($CFG->dirroot.'/group/lib.php');
 
-// Must have the sesskey
+// Must have the sesskey.
 $id      = required_param('id', PARAM_INT); // course id
 $action  = required_param('action', PARAM_ALPHANUMEXT);
 
@@ -50,7 +49,7 @@ require_login($course);
 require_capability('moodle/course:enrolreview', $context);
 require_sesskey();
 
-echo $OUTPUT->header(); // send headers
+echo $OUTPUT->header(); // Send headers.
 
 $manager = new course_enrolment_manager($PAGE, $course);
 
index 068c18b..4f1cd3b 100644 (file)
@@ -23,8 +23,7 @@
  *   - you need to change the "www-data" to match the apache user account
  *   - use "su" if "sudo" not available
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2011 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -35,7 +34,7 @@ require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
 require_once($CFG->libdir.'/clilib.php');
 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
 
-// now get cli options
+// Now get cli options.
 list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
 
 if ($unrecognized) {
index 98ce7b0..70c5528 100644 (file)
@@ -36,7 +36,7 @@ $capabilities = array(
         )
     ),
 
-    /* This is used only when sync suspends users instead of full unenrolment */
+    /* This is used only when sync suspends users instead of full unenrolment. */
     'enrol/cohort:unenrol' => array(
 
         'captype' => 'write',
@@ -47,8 +47,3 @@ $capabilities = array(
     ),
 
 );
-
-
-
-
-
index 4f26a20..9de7dcf 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-/* List of handlers */
+/* List of handlers. */
 $handlers = array (
     'cohort_member_added' => array (
         'handlerfile'      => '/enrol/cohort/locallib.php',
index 58b0c05..5da20bf 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Meta link enrolment plugin uninstallation.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2011 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -38,4 +37,4 @@ function xmldb_enrol_cohort_uninstall() {
     role_unassign_all(array('component'=>'enrol_cohort'));
 
     return true;
-}
\ No newline at end of file
+}
index 8196bb0..b198b76 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Strings for component 'enrol_cohort', language 'en', branch 'MOODLE_20_STABLE'
+ * Strings for component 'enrol_cohort', language 'en'
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda  {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -28,5 +26,6 @@ $string['ajaxmore'] = 'More...';
 $string['cohortsearch'] = 'Search';
 $string['cohort:config'] = 'Configure cohort instances';
 $string['cohort:unenrol'] = 'Unenrol suspended users';
+$string['instanceexists'] = 'Cohort is already synchronised with selected role';
 $string['pluginname'] = 'Cohort sync';
 $string['pluginname_desc'] = 'Cohort enrolment plugin synchronises cohort members with course participants.';
index 4ad304e..0621901 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Cohort enrolment plugin.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -32,9 +31,9 @@ defined('MOODLE_INTERNAL') || die();
  */
 class enrol_cohort_plugin extends enrol_plugin {
     /**
-     * Returns localised name of enrol instance
+     * Returns localised name of enrol instance.
      *
-     * @param object $instance (null is accepted too)
+     * @param stdClass $instance (null is accepted too)
      * @return string
      */
     public function get_instance_name($instance) {
@@ -67,12 +66,12 @@ class enrol_cohort_plugin extends enrol_plugin {
         if (!$this->can_add_new_instances($courseid)) {
             return NULL;
         }
-        // multiple instances supported - multiple parent courses linked
+        // Multiple instances supported - multiple parent courses linked.
         return new moodle_url('/enrol/cohort/addinstance.php', array('id'=>$courseid));
     }
 
     /**
-     * Given a courseid this function returns true if the user is able to enrol or configure cohorts
+     * Given a courseid this function returns true if the user is able to enrol or configure cohorts.
      * AND there are cohorts that the user can view.
      *
      * @param int $courseid
@@ -100,7 +99,6 @@ class enrol_cohort_plugin extends enrol_plugin {
         return false;
     }
 
-
     /**
      * Called for all enabled enrol plugins that returned true from is_cron_required().
      * @return void
@@ -116,8 +114,8 @@ class enrol_cohort_plugin extends enrol_plugin {
      * Called after updating/inserting course.
      *
      * @param bool $inserted true if course just inserted
-     * @param object $course
-     * @param object $data form data
+     * @param stdClass $course
+     * @param stdClass $data form data
      * @return void
      */
     public function course_updated($inserted, $course, $data) {
@@ -158,7 +156,7 @@ class enrol_cohort_plugin extends enrol_plugin {
     }
 
     /**
-     * Gets an array of the user enrolment actions
+     * Gets an array of the user enrolment actions.
      *
      * @param course_enrolment_manager $manager
      * @param stdClass $ue A user enrolment object
@@ -210,7 +208,7 @@ class enrol_cohort_plugin extends enrol_plugin {
         $button->strings_for_js('cohort', 'cohort');
         $button->strings_for_js('users', 'moodle');
 
-        // No point showing this at all if the user cant manually enrol users
+        // No point showing this at all if the user cant manually enrol users.
         $hasmanualinstance = has_capability('enrol/manual:enrol', $manager->get_context()) && $manager->has_instance('manual');
 
         $modules = array('moodle-enrol_cohort-quickenrolment', 'moodle-enrol_cohort-quickenrolment-skin');
@@ -224,6 +222,78 @@ class enrol_cohort_plugin extends enrol_plugin {
 
         return $button;
     }
-}
 
+    /**
+     * Restore instance and map settings.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $course
+     * @param int $oldid
+     */
+    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
+        global $DB, $CFG;
+
+        if (!$step->get_task()->is_samesite()) {
+            // No cohort restore from other sites.
+            $step->set_mapping('enrol', $oldid, 0);
+            return;
+        }
+
+        if ($data->roleid and $DB->record_exists('cohort', array('id'=>$data->customint1))) {
+            $instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
+            if ($instance) {
+                $instanceid = $instance->id;
+            } else {
+                $instanceid = $this->add_instance($course, (array)$data);
+            }
+            $step->set_mapping('enrol', $oldid, $instanceid);
+
+            require_once("$CFG->dirroot/enrol/cohort/locallib.php");
+            enrol_cohort_sync($course->id, false);
+
+        } else if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+            $data->customint1 = 0;
+            $instance = $DB->get_record('enrol', array('roleid'=>$data->roleid, 'customint1'=>$data->customint1, 'courseid'=>$course->id, 'enrol'=>$this->get_name()));
+
+            if ($instance) {
+                $instanceid = $instance->id;
+            } else {
+                $data->status = ENROL_INSTANCE_DISABLED;
+                $instanceid = $this->add_instance($course, (array)$data);
+            }
+            $step->set_mapping('enrol', $oldid, $instanceid);
+
+            require_once("$CFG->dirroot/enrol/cohort/locallib.php");
+            enrol_cohort_sync($course->id, false);
 
+        } else {
+            $step->set_mapping('enrol', $oldid, 0);
+        }
+    }
+
+    /**
+     * Restore user enrolment.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $instance
+     * @param int $oldinstancestatus
+     * @param int $userid
+     */
+    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
+        global $DB;
+
+        if ($this->get_config('unenrolaction') != ENROL_EXT_REMOVED_SUSPENDNOROLES) {
+            // Enrolments were already synchronised in restore_instance(), we do not want any suspended leftovers.
+            return;
+        }
+
+        // ENROL_EXT_REMOVED_SUSPENDNOROLES means all previous enrolments are restored
+        // but without roles and suspended.
+
+        if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
+            $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, ENROL_USER_SUSPENDED);
+        }
+    }
+}
index 869e06c..d26ee18 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Local stuff for cohort enrolment plugin.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -36,7 +35,7 @@ require_once($CFG->dirroot . '/enrol/locallib.php');
  */
 class enrol_cohort_handler {
     /**
-     * Event processor - cohort member added
+     * Event processor - cohort member added.
      * @param stdClass $ca
      * @return bool
      */
@@ -47,12 +46,12 @@ class enrol_cohort_handler {
             return true;
         }
 
-        // does any enabled cohort instance want to sync with this cohort?
+        // Does any enabled cohort instance want to sync with this cohort?
         $sql = "SELECT e.*, r.id as roleexists
                   FROM {enrol} e
              LEFT JOIN {role} r ON (r.id = e.roleid)
-                 WHERE customint1 = :cohortid AND enrol = 'cohort'
-              ORDER BY id ASC";
+                 WHERE e.customint1 = :cohortid AND e.enrol = 'cohort'
+              ORDER BY e.id ASC";
         if (!$instances = $DB->get_records_sql($sql, array('cohortid'=>$ca->cohortid))) {
             return true;
         }
@@ -60,14 +59,14 @@ class enrol_cohort_handler {
         $plugin = enrol_get_plugin('cohort');
         foreach ($instances as $instance) {
             if ($instance->status != ENROL_INSTANCE_ENABLED ) {
-                // no roles for disabled instances
+                // No roles for disabled instances.
                 $instance->roleid = 0;
             } else if ($instance->roleid and !$instance->roleexists) {
-                // invalid role - let's just enrol, they will have to create new sync and delete this one
+                // Invalid role - let's just enrol, they will have to create new sync and delete this one.
                 $instance->roleid = 0;
             }
             unset($instance->roleexists);
-            // no problem if already enrolled
+            // No problem if already enrolled.
             $plugin->enrol_user($instance, $ca->userid, $instance->roleid, 0, 0, ENROL_USER_ACTIVE);
         }
 
@@ -75,14 +74,14 @@ class enrol_cohort_handler {
     }
 
     /**
-     * Event processor - cohort member removed
+     * Event processor - cohort member removed.
      * @param stdClass $ca
      * @return bool
      */
     public static function member_removed($ca) {
         global $DB;
 
-        // does anything want to sync with this cohort?
+        // Does anything want to sync with this cohort?
         if (!$instances = $DB->get_records('enrol', array('customint1'=>$ca->cohortid, 'enrol'=>'cohort'), 'id ASC')) {
             return true;
         }
@@ -110,14 +109,14 @@ class enrol_cohort_handler {
     }
 
     /**
-     * Event processor - cohort deleted
+     * Event processor - cohort deleted.
      * @param stdClass $cohort
      * @return bool
      */
     public static function deleted($cohort) {
         global $DB;
 
-        // does anything want to sync with this cohort?
+        // Does anything want to sync with this cohort?
         if (!$instances = $DB->get_records('enrol', array('customint1'=>$cohort->id, 'enrol'=>'cohort'), 'id ASC')) {
             return true;
         }
@@ -149,7 +148,7 @@ class enrol_cohort_handler {
 function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     global $CFG, $DB;
 
-    // purge all roles if cohort sync disabled, those can be recreated later here by cron or CLI
+    // Purge all roles if cohort sync disabled, those can be recreated later here by cron or CLI.
     if (!enrol_is_enabled('cohort')) {
         if ($verbose) {
             mtrace('Cohort sync plugin is disabled, unassigning all plugin roles and stopping.');
@@ -158,7 +157,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
         return 2;
     }
 
-    // unfortunately this may take a long time, this script can be interrupted without problems
+    // Unfortunately this may take a long time, this script can be interrupted without problems.
     @set_time_limit(0);
     raise_memory_limit(MEMORY_HUGE);
 
@@ -173,7 +172,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $unenrolaction = $plugin->get_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
 
 
-    // iterate through all not enrolled yet users
+    // Iterate through all not enrolled yet users.
     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
     $sql = "SELECT cm.userid, e.id AS enrolid, ue.status
               FROM {cohort_members} cm
@@ -204,7 +203,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs->close();
 
 
-    // unenrol as necessary
+    // Unenrol as necessary.
     $sql = "SELECT ue.*, e.courseid
               FROM {user_enrolments} ue
               JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort' $onecourse)
@@ -217,14 +216,14 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
         }
         $instance = $instances[$ue->enrolid];
         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
-            // remove enrolment together with group membership, grades, preferences, etc.
+            // Temove enrolment together with group membership, grades, preferences, etc.
             $plugin->unenrol_user($instance, $ue->userid);
             if ($verbose) {
                 mtrace("  unenrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1");
             }
 
         } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
-            // just disable and ignore any changes
+            // Just disable and ignore any changes.
             if ($ue->status != ENROL_USER_SUSPENDED) {
                 $plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
                 $context = context_course::instance($instance->courseid);
@@ -239,7 +238,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     unset($instances);
 
 
-    // now assign all necessary roles to enrolled users - skip suspended instances and users
+    // Now assign all necessary roles to enrolled users - skip suspended instances and users.
     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
     $sql = "SELECT e.roleid, ue.userid, c.id AS contextid, e.id AS itemid, e.courseid
               FROM {user_enrolments} ue
@@ -264,7 +263,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs->close();
 
 
-    // remove unwanted roles - sync role can not be changed, we only remove role when unenrolled
+    // Remove unwanted roles - sync role can not be changed, we only remove role when unenrolled.
     $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
     $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid, e.courseid
               FROM {role_assignments} ra
@@ -384,7 +383,7 @@ function enrol_cohort_get_cohorts(course_enrolment_manager $manager) {
 }
 
 /**
- * Check if cohort exists and user is allowed to enrol it
+ * Check if cohort exists and user is allowed to enrol it.
  *
  * @global moodle_database $DB
  * @param int $cohortid Cohort ID
@@ -426,10 +425,10 @@ function enrol_cohort_search_cohorts(course_enrolment_manager $manager, $offset
 
     list($sqlparents, $params) = $DB->get_in_or_equal(get_parent_contexts($context));
 
-    // Add some additional sensible conditions
+    // Add some additional sensible conditions.
     $tests = array('contextid ' . $sqlparents);
 
-    // Modify the query to perform the search if required
+    // Modify the query to perform the search if required.
     if (!empty($search)) {
         $conditions = array(
             'name',
@@ -452,17 +451,17 @@ function enrol_cohort_search_cohorts(course_enrolment_manager $manager, $offset
     $order = ' ORDER BY name ASC';
     $rs = $DB->get_recordset_sql($fields . $sql . $order, $params, $offset);
 
-    // Produce the output respecting parameters
+    // Produce the output respecting parameters.
     foreach ($rs as $c) {
-        // Track offset
+        // Track offset.
         $offset++;
-        // Check capabilities
+        // Check capabilities.
         $context = context::instance_by_id($c->contextid);
         if (!has_capability('moodle/cohort:view', $context)) {
             continue;
         }
         if ($limit === 0) {
-            // we have reached the required number of items and know that there are more, exit now
+            // we have reached the required number of items and know that there are more, exit now.
             $offset--;
             break;
         }
@@ -472,9 +471,9 @@ function enrol_cohort_search_cohorts(course_enrolment_manager $manager, $offset
             'users'=>$DB->count_records('cohort_members', array('cohortid'=>$c->id)),
             'enrolled'=>in_array($c->id, $enrolled)
         );
-        // Count items
+        // Count items.
         $limit--;
     }
     $rs->close();
     return array('more' => !(bool)$limit, 'offset' => $offset, 'cohorts' => $cohorts);
-}
\ No newline at end of file
+}
index db9702a..8c08a74 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Cohort enrolment plugin settings and presets.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -45,4 +44,3 @@ if ($ADMIN->fulltree) {
         $settings->add(new admin_setting_configselect('enrol_cohort/unenrolaction', get_string('extremovedaction', 'enrol'), get_string('extremovedaction_help', 'enrol'), ENROL_EXT_REMOVED_UNENROL, $options));
     }
 }
-
index 74a2aa8..f23b2b3 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Cohort enrolment plugin version specification.
  *
- * @package    enrol
- * @subpackage cohort
+ * @package    enrol_cohort
  * @copyright  2010 Petr Skoda {@link http://skodak.org}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -28,4 +27,4 @@ defined('MOODLE_INTERNAL') || die();
 $plugin->version   = 2012061700;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012061700;        // 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
\ No newline at end of file
+$plugin->cron      = 60*60;             // run cron every hour by default, it is not out-of-sync often
index 9509c01..ae247a0 100644 (file)
@@ -2,6 +2,7 @@
 .qce-panel .yui3-widget-hd {background:url("sprite.png");background-repeat:repeat-x;background-color:#DDD;background-position: 0 -15px;border-bottom:1px solid #555;border-top:1px solid #fff;}
 .qce-panel .yui3-widget-hd h2 {margin:3px 5px 2px;padding:0;font-size:110%;}
 .qce-panel .yui3-widget-hd .close {width:25px;height:15px;position:absolute;top:3px;right:1em;cursor:pointer;background:url("sprite.png") no-repeat scroll 0 0 transparent;}
+.dir-rtl .qce-panel .yui3-widget-hd .close {right:auto;left:1em;}
 .qce-panel .yui3-overlay-content {background-color:#F6F6F6;border:1px solid #555;margin-top:-2px;margin-left:-2px;}
 .qce-panel .qce-enrollable-cohorts {margin:5px;}
 .qce-panel .qce-cohorts {border:1px solid #666;min-width:408px;background-color:#FFF;height:375px;overflow:auto;}
index 02aec70..acd2536 100644 (file)
@@ -366,18 +366,22 @@ class enrol_guest_plugin extends enrol_plugin {
         return $this->add_instance($course, $fields);
     }
 
-}
+    /**
+     * Restore instance and map settings.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $course
+     * @param int $oldid
+     */
+    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
+        global $DB;
 
-/**
- * Indicates API features that the enrol plugin supports.
- *
- * @param string $feature
- * @return mixed True if yes (some features may use other values)
- */
-function enrol_guest_supports($feature) {
-    switch($feature) {
-        case ENROL_RESTORE_TYPE: return ENROL_RESTORE_NOUSERS;
+        if (!$DB->record_exists('enrol', array('courseid' => $data->courseid, 'enrol' => $this->get_name()))) {
+            $this->add_instance($course, (array)$data);
+        }
 
-        default: return null;
+        // No need to set mapping, we do not restore users or roles here.
+        $step->set_mapping('enrol', $oldid, 0);
     }
 }
index 2cf94bb..fb648e0 100644 (file)
@@ -295,18 +295,89 @@ class enrol_manual_plugin extends enrol_plugin {
         );
         return $bulkoperations;
     }
-}
 
-/**
- * Indicates API features that the enrol plugin supports.
- *
- * @param string $feature
- * @return mixed True if yes (some features may use other values)
- */
-function enrol_manual_supports($feature) {
-    switch($feature) {
-        case ENROL_RESTORE_TYPE: return ENROL_RESTORE_EXACT;
+    /**
+     * Restore instance and map settings.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $course
+     * @param int $oldid
+     */
+    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
+        global $DB;
+        // There is only I manual enrol instance allowed per course.
+        if ($instances = $DB->get_records('enrol', array('courseid'=>$data->courseid, 'enrol'=>'manual'), 'id')) {
+            $instance = reset($instances);
+            $instanceid = $instance->id;
+        } else {
+            $instanceid = $this->add_instance($course, (array)$data);
+        }
+        $step->set_mapping('enrol', $oldid, $instanceid);
+    }
 
-        default: return null;
+    /**
+     * Restore user enrolment.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $instance
+     * @param int $oldinstancestatus
+     * @param int $userid
+     */
+    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
+        global $DB;
+
+        // Note: this is a bit tricky because other types may be converted to manual enrolments,
+        //       and manual is restricted to one enrolment per user.
+
+        $ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid));
+        $enrol = false;
+        if ($ue and $ue->status == ENROL_USER_ACTIVE) {
+            // We do not want to restrict current active enrolments, let's kind of merge the times only.
+            // This prevents some teacher lockouts too.
+            if ($data->status == ENROL_USER_ACTIVE) {
+                if ($data->timestart > $ue->timestart) {
+                    $data->timestart = $ue->timestart;
+                    $enrol = true;
+                }
+
+                if ($data->timeend == 0) {
+                    if ($ue->timeend != 0) {
+                        $enrol = true;
+                    }
+                } else if ($ue->timeend == 0) {
+                    $data->timeend = 0;
+                } else if ($data->timeend < $ue->timeend) {
+                    $data->timeend = $ue->timeend;
+                    $enrol = true;
+                }
+            }
+        } else {
+            if ($instance->status == ENROL_INSTANCE_ENABLED and $oldinstancestatus != ENROL_INSTANCE_ENABLED) {
+                // Make sure that user enrolments are not activated accidentally,
+                // we do it only here because it is not expected that enrolments are migrated to other plugins.
+                $data->status = ENROL_USER_SUSPENDED;
+            }
+            $enrol = true;
+        }
+
+        if ($enrol) {
+            $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
+        }
+    }
+
+    /**
+     * Restore role assignment.
+     *
+     * @param stdClass $instance
+     * @param int $roleid
+     * @param int $userid
+     * @param int $contextid
+     */
+    public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
+        // This is necessary only because we may migrate other types to this instance,
+        // we do not use component in manual or self enrol.
+        role_assign($roleid, $userid, $contextid, '', 0);
     }
 }
diff --git a/enrol/manual/tests/externallib_test.php b/enrol/manual/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..823568b
--- /dev/null
@@ -0,0 +1,73 @@
+<?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/>.
+
+/**
+ * Enrol manual external PHPunit tests
+ *
+ * @package    enrol_manual
+ * @category   external
+ * @copyright  2012 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.4
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/enrol/manual/externallib.php');
+
+class enrol_manual_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test get_enrolled_users
+     */
+    public function test_enrol_users() {
+        global $USER, $CFG;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        // Set the required capabilities by the external function.
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('enrol/manual:enrol', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Add manager role to $USER.
+        // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'.
+        role_assign(1, $USER->id, context_system::instance()->id);
+
+        // Call the external function.
+        enrol_manual_external::enrol_users(array(
+            array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course->id),
+            array('roleid' => 3, 'userid' => $user2->id, 'courseid' => $course->id)
+        ));
+
+        // Check we retrieve the good total number of enrolled users.
+        require_once($CFG->dirroot . '/enrol/externallib.php');
+        $enrolledusers = core_enrol_external::get_enrolled_users($course->id);
+        $this->assertEquals(2, count($enrolledusers));
+
+        // Call without required capability.
+        $this->unassignUserCapability('enrol/manual:enrol', $context->id, $roleid);
+        $this->setExpectedException('moodle_exception');
+        $categories = enrol_manual_external::enrol_users($course->id);
+    }
+}
\ No newline at end of file
index fda2414..8c459f9 100644 (file)
@@ -403,18 +403,68 @@ class enrol_self_plugin extends enrol_plugin {
         }
         return $actions;
     }
-}
 
-/**
- * Indicates API features that the enrol plugin supports.
- *
- * @param string $feature
- * @return mixed true if yes (some features may use other values)
- */
-function enrol_self_supports($feature) {
-    switch($feature) {
-        case ENROL_RESTORE_TYPE: return ENROL_RESTORE_EXACT;
+    /**
+     * Restore instance and map settings.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $course
+     * @param int $oldid
+     */
+    public function restore_instance(restore_enrolments_structure_step $step, stdClass $data, $course, $oldid) {
+        global $DB;
+        if ($step->get_task()->get_target() == backup::TARGET_NEW_COURSE) {
+            $merge = false;
+        } else {
+            $merge = array(
+                'courseid'   => $data->courseid,
+                'enrol'      => $this->get_name(),
+                'roleid'     => $data->roleid,
+            );
+        }
+        if ($merge and $instances = $DB->get_records('enrol', $merge, 'id')) {
+            $instance = reset($instances);
+            $instanceid = $instance->id;
+        } else {
+            if (!empty($data->customint5)) {
+                if ($step->get_task()->is_samesite()) {
+                    // Keep cohort restriction unchanged - we are on the same site.
+                } else {
+                    // Use some id that can not exist in order to prevent self enrolment,
+                    // because we do not know what cohort it is in this site.
+                    $data->customint5 = -1;
+                }
+            }
+            $instanceid = $this->add_instance($course, (array)$data);
+        }
+        $step->set_mapping('enrol', $oldid, $instanceid);
+    }
 
-        default: return null;
+    /**
+     * Restore user enrolment.
+     *
+     * @param restore_enrolments_structure_step $step
+     * @param stdClass $data
+     * @param stdClass $instance
+     * @param int $oldinstancestatus
+     * @param int $userid
+     */
+    public function restore_user_enrolment(restore_enrolments_structure_step $step, $data, $instance, $userid, $oldinstancestatus) {
+        $this->enrol_user($instance, $userid, null, $data->timestart, $data->timeend, $data->status);
+    }
+
+    /**
+     * Restore role assignment.
+     *
+     * @param stdClass $instance
+     * @param int $roleid
+     * @param int $userid
+     * @param int $contextid
+     */
+    public function restore_role_assignment($instance, $roleid, $userid, $contextid) {
+        // This is necessary only because we may migrate other types to this instance,
+        // we do not use component in manual or self enrol.
+        role_assign($roleid, $userid, $contextid, '', 0);
     }
 }
diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..7fb9500
--- /dev/null
@@ -0,0 +1,224 @@
+<?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/>.
+
+/**
+ * Enrol/Role external PHPunit tests
+ *
+ * @package    core_enrol
+ * @category   external
+ * @copyright  2012 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.4
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/enrol/externallib.php');
+
+/**
+ * Enrol external PHPunit tests
+ *
+ * @package    core_enrol
+ * @category   external
+ * @copyright  2012 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.4
+ */
+class core_enrol_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test get_enrolled_users
+     */
+    public function test_get_enrolled_users() {
+        global $USER;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+        $user1 = self::getDataGenerator()->create_user();
+        $user2 = self::getDataGenerator()->create_user();
+
+        // Set the required capabilities by the external function.
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $context->id);
+        $this->assignUserCapability('moodle/user:viewdetails', $context->id, $roleid);
+
+        // Enrol the users in the course.
+        // We use the manual plugin.
+        $enrol = enrol_get_plugin('manual');
+        $enrolinstances = enrol_get_instances($course->id, true);
+        foreach ($enrolinstances as $courseenrolinstance) {
+            if ($courseenrolinstance->enrol == "manual") {
+                $instance = $courseenrolinstance;
+                break;
+            }
+        }
+        $enrol->enrol_user($instance, $user1->id, $roleid);
+        $enrol->enrol_user($instance, $user2->id, $roleid);
+        $enrol->enrol_user($instance, $USER->id, $roleid);
+
+        // Call the external function.
+        $enrolledusers = core_enrol_external::get_enrolled_users($course->id);
+
+        // Check we retrieve the good total number of enrolled users.
+        $this->assertEquals(3, count($enrolledusers));
+
+        // Call without required capability.
+        $this->unassignUserCapability('moodle/course:viewparticipants', $context->id, $roleid);
+        $this->setExpectedException('moodle_exception');
+        $categories = core_enrol_external::get_enrolled_users($course->id);
+    }
+
+    /**
+     * Test get_users_courses
+     */
+    public function test_get_users_courses() {
+        global $USER;
+
+        $this->resetAfterTest(true);
+
+        $course1 = self::getDataGenerator()->create_course();
+        $course2 = self::getDataGenerator()->create_course();
+        $courses = array($course1, $course2);
+
+        // Enrol $USER in the courses.
+        // We use the manual plugin.
+        $enrol = enrol_get_plugin('manual');
+        $roleid = null;
+        foreach ($courses as $course) {
+            $context = context_course::instance($course->id);
+            $roleid = $this->assignUserCapability('moodle/course:viewparticipants',
+                    $context->id, $roleid);
+
+            $enrolinstances = enrol_get_instances($course->id, true);
+            foreach ($enrolinstances as $courseenrolinstance) {
+                if ($courseenrolinstance->enrol == "manual") {
+                    $instance = $courseenrolinstance;
+                    break;
+                }
+            }
+            $enrol->enrol_user($instance, $USER->id, $roleid);
+        }
+
+        // Call the external function.
+        $enrolledincourses = core_enrol_external::get_users_courses($USER->id);
+
+        // Check we retrieve the good total number of enrolled users.
+        $this->assertEquals(2, count($enrolledincourses));
+    }
+}
+
+/**
+ * Role external PHPunit tests
+ *
+ * @package    core_enrol
+ * @category   external
+ * @copyright  2012 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.4
+ */
+class core_role_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Tests set up
+     */
+    protected function setUp() {
+        global $CFG;
+        require_once($CFG->dirroot . '/enrol/externallib.php');
+    }
+
+    /**
+     * Test assign_roles
+     */
+    public function test_assign_roles() {
+        global $USER;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+
+        // Set the required capabilities by the external function.
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/role:assign', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Add manager role to $USER.
+        // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'.
+        role_assign(1, $USER->id, context_system::instance()->id);
+
+        // Check the teacher role has not been assigned to $USER.
+        $users = get_role_users(3, $context);
+        $this->assertEquals(count($users), 0);
+
+        // Call the external function. Assign teacher role to $USER.
+        core_role_external::assign_roles(array(
+            array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)));
+
+        // Check the role has been assigned.
+        $users = get_role_users(3, $context);
+        $this->assertEquals(count($users), 1);
+
+        // Call without required capability.
+        $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid);
+        $this->setExpectedException('moodle_exception');
+        $categories = core_role_external::assign_roles(
+            array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id));
+    }
+
+    /**
+     * Test unassign_roles
+     */
+    public function test_unassign_roles() {
+        global $USER;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+
+        // Set the required capabilities by the external function.
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/role:assign', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Add manager role to $USER.
+        // So $USER is allowed to assign 'manager', 'editingteacher', 'teacher' and 'student'.
+        role_assign(1, $USER->id, context_system::instance()->id);
+
+        // Add teacher role to $USER on course context.
+        role_assign(3, $USER->id, $context->id);
+
+        // Check the teacher role has been assigned to $USER on course context.
+        $users = get_role_users(3, $context);
+        $this->assertEquals(count($users), 1);
+
+        // Call the external function. Assign teacher role to $USER.
+        core_role_external::unassign_roles(array(
+            array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id)));
+
+        // Check the role has been unassigned on course context.
+        $users = get_role_users(3, $context);
+        $this->assertEquals(count($users), 0);
+
+        // Call without required capability.
+        $this->unassignUserCapability('moodle/role:assign', $context->id, $roleid);
+        $this->setExpectedException('moodle_exception');
+        $categories = core_role_external::unassign_roles(
+            array('roleid' => 3, 'userid' => $USER->id, 'contextid' => $context->id));
+    }
+}
index 8fcd760..85dbf78 100644 (file)
@@ -7,6 +7,8 @@ information provided here is intended especially for developers.
 required changes in code:
 * use role_get_name() or role_fix_names() if you need any role names, using role.name
   directly from database is not correct any more
+* new restore support: ENROL_RESTORE_EXACT, ENROL_RESTORE_NOUSERS
+  and ENROL_RESTORE_CLASS were removed, implement new restore_* plugin methods instead
 
 other changes:
 * course enrolment manager now works with disabled plugins too
index 6a648b0..98c8431 100644 (file)
@@ -526,6 +526,7 @@ class core_files_renderer extends plugin_renderer_base {
                     <div class="{!}fp-tb-logout"><img src="'.$this->pix_url('a/logout').'" /><a href="#"></a></div>
                     <div class="{!}fp-tb-manage"><a href="#"><img src="'.$this->pix_url('a/setting').'" /> '.get_string('manageurl', 'repository').'</a></div>
                     <div class="{!}fp-tb-help"><a href="#"><img src="'.$this->pix_url('a/help').'" /> '.get_string('help').'</a></div>
+                    <div class="{!}fp-tb-message"></div>
                 </div>
                 <div class="{!}fp-viewbar">
                     <a class="{!}fp-vb-icons" href="#"></a>
diff --git a/group/tests/externallib_test.php b/group/tests/externallib_test.php
new file mode 100644 (file)
index 0000000..5963937
--- /dev/null
@@ -0,0 +1,204 @@
+<?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/>.
+
+/**
+ * Group external PHPunit tests
+ *
+ * @package    core_group
+ * @category   external
+ * @copyright  2012 Jerome Mouneyrac
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.4
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+require_once($CFG->dirroot . '/webservice/tests/helpers.php');
+require_once($CFG->dirroot . '/group/externallib.php');
+
+class core_group_external_testcase extends externallib_advanced_testcase {
+
+    /**
+     * Test create_groups
+     */
+    public function test_create_groups() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $course  = self::getDataGenerator()->create_course();
+
+        $group1 = array();
+        $group1['courseid'] = $course->id;
+        $group1['name'] = 'Group Test 1';
+        $group1['description'] = 'Group Test 1 description';
+        $group1['descriptionformat'] = FORMAT_MOODLE;
+        $group1['enrolmentkey'] = 'Test group enrol secret phrase';
+        $group2 = array();
+        $group2['courseid'] = $course->id;
+        $group2['name'] = 'Group Test 2';
+        $group2['description'] = 'Group Test 2 description';
+        $group3 = array();
+        $group3['courseid'] = $course->id;
+        $group3['name'] = 'Group Test 3';
+        $group3['description'] = 'Group Test 3 description';
+
+        // Set the required capabilities by the external function
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Call the external function.
+        $groups = core_group_external::create_groups(array($group1, $group2));
+
+        // Checks against DB values
+        $this->assertEquals(2, count($groups));
+        foreach ($groups as $group) {
+            $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
+            switch ($dbgroup->name) {
+                case $group1['name']:
+                    $groupdescription = $group1['description'];
+                    $groupcourseid = $group1['courseid'];
+                    $this->assertEquals($dbgroup->descriptionformat, $group1['descriptionformat']);
+                    $this->assertEquals($dbgroup->enrolmentkey, $group1['enrolmentkey']);
+                    break;
+                case $group2['name']:
+                    $groupdescription = $group2['description'];
+                    $groupcourseid = $group2['courseid'];
+                    break;
+                default:
+                    throw new moodle_exception('unknowgroupname');
+                    break;
+            }
+            $this->assertEquals($dbgroup->description, $groupdescription);
+            $this->assertEquals($dbgroup->courseid, $groupcourseid);
+        }
+
+        // Call without required capability
+        $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
+        $this->setExpectedException('required_capability_exception');
+        $froups = core_group_external::create_groups(array($group3));
+    }
+
+    /**
+     * Test get_groups
+     */
+    public function test_get_groups() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+        $group1data = array();
+        $group1data['courseid'] = $course->id;
+        $group1data['name'] = 'Group Test 1';
+        $group1data['description'] = 'Group Test 1 description';
+        $group1data['descriptionformat'] = FORMAT_MOODLE;
+        $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
+        $group2data = array();
+        $group2data['courseid'] = $course->id;
+        $group2data['name'] = 'Group Test 2';
+        $group2data['description'] = 'Group Test 2 description';
+        $group1 = self::getDataGenerator()->create_group($group1data);
+        $group2 = self::getDataGenerator()->create_group($group2data);
+
+        // Set the required capabilities by the external function
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Call the external function.
+        $groups = core_group_external::get_groups(array($group1->id, $group2->id));
+
+        // Checks against DB values
+        $this->assertEquals(2, count($groups));
+        foreach ($groups as $group) {
+            $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
+            switch ($dbgroup->name) {
+                case $group1->name:
+                    $groupdescription = $group1->description;
+                    $groupcourseid = $group1->courseid;
+                    $this->assertEquals($dbgroup->descriptionformat, $group1->descriptionformat);
+                    $this->assertEquals($dbgroup->enrolmentkey, $group1->enrolmentkey);
+                    break;
+                case $group2->name:
+                    $groupdescription = $group2->description;
+                    $groupcourseid = $group2->courseid;
+                    break;
+                default:
+                    throw new moodle_exception('unknowgroupname');
+                    break;
+            }
+            $this->assertEquals($dbgroup->description, $groupdescription);
+            $this->assertEquals($dbgroup->courseid, $groupcourseid);
+        }
+
+        // Call without required capability
+        $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
+        $this->setExpectedException('required_capability_exception');
+        $froups = core_group_external::get_groups(array($group1->id, $group2->id));
+    }
+
+    /**
+     * Test delete_groups
+     */
+    public function test_delete_groups() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $course = self::getDataGenerator()->create_course();
+        $group1data = array();
+        $group1data['courseid'] = $course->id;
+        $group1data['name'] = 'Group Test 1';
+        $group1data['description'] = 'Group Test 1 description';
+        $group1data['descriptionformat'] = FORMAT_MOODLE;
+        $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
+        $group2data = array();
+        $group2data['courseid'] = $course->id;
+        $group2data['name'] = 'Group Test 2';
+        $group2data['description'] = 'Group Test 2 description';
+        $group3data['courseid'] = $course->id;
+        $group3data['name'] = 'Group Test 3';
+        $group3data['description'] = 'Group Test 3 description';
+        $group1 = self::getDataGenerator()->create_group($group1data);
+        $group2 = self::getDataGenerator()->create_group($group2data);
+        $group3 = self::getDataGenerator()->create_group($group3data);
+
+        // Set the required capabilities by the external function
+        $context = context_course::instance($course->id);
+        $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
+        $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
+
+        // Checks against DB values
+        $groupstotal = $DB->count_records('groups', array());
+        $this->assertEquals(3, $groupstotal);
+
+        // Call the external function.
+        core_group_external::delete_groups(array($group1->id, $group2->id));
+
+        // Checks against DB values
+        $groupstotal = $DB->count_records('groups', array());
+        $this->assertEquals(1, $groupstotal);
+
+        // Call without required capability
+        $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
+        $this->setExpectedException('required_capability_exception');
+        $froups = core_group_external::delete_groups(array($group3->id));
+    }
+}
\ No newline at end of file
index 38a4438..dd35032 100644 (file)
--- a/index.php
+++ b/index.php
             break;
 
             case FRONTPAGECOURSELIST:
+                $ncourses = $DB->count_records('course');
                 if (isloggedin() and !$hassiteconfig and !isguestuser() and empty($CFG->disablemycourses)) {
                     echo html_writer::tag('a', get_string('skipa', 'access', textlib::strtolower(get_string('mycourses'))), array('href'=>'#skipmycourses', 'class'=>'skip-block'));
                     echo $OUTPUT->heading(get_string('mycourses'), 2, 'headingblock header');
                     print_my_moodle();
                     echo html_writer::tag('span', '', array('class'=>'skip-block-to', 'id'=>'skipmycourses'));
-                } else if ((!$hassiteconfig and !isguestuser()) or ($DB->count_records('course') <= FRONTPAGECOURSELIMIT)) {
+                } else if ((!$hassiteconfig and !isguestuser()) or ($ncourses <= FRONTPAGECOURSELIMIT)) {
                     // admin should not see list of courses when there are too many of them
                     echo html_writer::tag('a', get_string('skipa', 'access', textlib::strtolower(get_string('availablecourses'))), array('href'=>'#skipavailablecourses', 'class'=>'skip-block'));
                     echo $OUTPUT->heading(get_string('availablecourses'), 2, 'headingblock header');
                     print_courses(0);
                     echo html_writer::tag('span', '', array('class'=>'skip-block-to', 'id'=>'skipavailablecourses'));
+                } else {
+                    echo html_writer::tag('div', get_string('therearecourses', '', $ncourses), array('class' => 'notifyproblem'));
+                    print_course_search('', false, 'short');
                 }
             break;
 
index 51e908c..e751fee 100644 (file)
@@ -39,5 +39,5 @@ $string['clitypevaluedefault'] = 'sartu balorea, sakatu Enter-i berezko balorea
 $string['cliunknowoption'] = 'Aukera ezezagunak:{$a}
 Mesedez, erabili --laguntza aukera.';
 $string['cliyesnoprompt'] = 'idatzi b (bai esateko) edo e (ez esateko)';
-$string['environmentrequireinstall'] = 'derrigorrezkoa da instalatuta etagaituta izatea';
+$string['environmentrequireinstall'] = 'derrigorrezkoa da instalatuta eta gaituta izatea';
 $string['environmentrequireversion'] = '{$a->needed} bertsioa beharrezkoa da eta zu {$a->current} ari zara egikaritzen';
diff --git a/install/lang/ga/error.php b/install/lang/ga/error.php
new file mode 100644 (file)
index 0000000..3655f09
--- /dev/null
@@ -0,0 +1,48 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['cannotcreatelangdir'] = 'Ní féidir eolaire teanga a chruthú';
+$string['cannotcreatetempdir'] = 'Ní féidir eolaire sealadach a chruthú';
+$string['cannotdownloadcomponents'] = 'Ní féidir na comhphairteanna a íoslódáil';
+$string['cannotdownloadzipfile'] = 'Ní féidir comhad ZIP a íoslódáil';
+$string['cannotfindcomponent'] = 'Ní féidir teacht ar chomhpháirt';
+$string['cannotsavemd5file'] = 'Ni féidir an comhad md5 a shábháil';
+$string['cannotsavezipfile'] = 'Ni féidir an comhad ZIP a shábháil';
+$string['cannotunzipfile'] = 'Ní féidir an comhad a dhízipeáil';
+$string['componentisuptodate'] = 'Tá an chomhpháirt suas chun dáta';
+$string['downloadedfilecheckfailed'] = 'Theip ar an seiceáil comhaid íoslódailte';
+$string['invalidmd5'] = 'Bhí "an athróg seiceála mícheart – triail arís é';
+$string['missingrequiredfield'] = 'Réimse riachtanach ar iarraidh';
+$string['remotedownloaderror'] = 'Theip ar íoslódail comhpháirte chuig do fhreastalaí, dearbhaigh seachshocruithe moltar iarmhíreanna , PHP cURL go hard.<br /"><br />Y Caithfidh tú an comhad <a href="""{$a->url}"">{$a->url}</a> a íoslódáil de láimh';
+$string['wrongdestpath'] = 'Cosán sprice mícheart';
+$string['wrongsourcebase'] = 'Foinse mícheart do bhunachar URL';
+$string['wrongzipfilename'] = 'Ainm mícherat comhaid ZIP';
index 28adfbc..22d60fc 100644 (file)
@@ -41,6 +41,7 @@ $string['dataroot'] = 'Katalog z danymi';
 $string['dbprefix'] = 'Prefiks tabel';
 $string['dirroot'] = 'Katalog Moodle';
 $string['environmenthead'] = 'Sprawdzam środowisko (ustawienia) ...';
+$string['environmentsub2'] = 'Każde wydanie Moodle ma pewne minimalne wymagania wersji PHP i pewną liczbę obowiązkowych rozszerzeń PHP. Pełna kontrola środowiska odbywa się przed każdą instalacją i aktualizacją. Prosimy o kontakt z administratorem serwera, jeśli nie wiesz jak zainstalować nową wersję lub włączyć rozszerzenie PHP.';
 $string['errorsinenvironment'] = 'Kontrola środowiska zakończona niepowodzeniem!';
 $string['installation'] = 'Instalacja';
 $string['langdownloaderror'] = 'Niestety język "{$a}" nie może zostać pobrany. Proces instalacji będzie kontynuowany w języku angielskim.';
index bc06968..e16259e 100644 (file)
@@ -40,6 +40,7 @@ $string['databasehost'] = 'Сервер баз данных';
 $string['databasename'] = 'Название базы данных';
 $string['databasetypehead'] = 'Выберите драйвер базы данных';
 $string['dataroot'] = 'Каталог данных';
+$string['datarootpermission'] = 'Разрешения на каталоги данных';
 $string['dbprefix'] = 'Префикс имён таблиц';
 $string['dirroot'] = 'Каталог Moodle';
 $string['environmenthead'] = 'Проверка среды...';
index 5e7b0d9..9f62c29 100644 (file)
@@ -283,6 +283,7 @@ $string['configrcache'] = 'Use the cache to store database records. Remember to
 $string['configrcachettl'] = 'Time-to-live for cached records, in seconds. Use a short (&lt;15) value here.';
 $string['configrecaptchaprivatekey'] = 'String of characters used to communicate between your Moodle server and the recaptcha server. Obtain one for this site by visiting http://www.google.com/recaptcha';
 $string['configrecaptchapublickey'] = 'String of characters used to display the reCAPTCHA element in the signup form. Generated by http://www.google.com/recaptcha';
+$string['configrequestcategoryselection'] = 'Allow the selection of a category when requesting a course.';
 $string['configrequestedstudentname'] = 'Word for student used in requested courses';
 $string['configrequestedstudentsname'] = 'Word for students used in requested courses';
 $string['configrequestedteachername'] = 'Word for teacher used in requested courses';
@@ -871,6 +872,7 @@ $string['requires'] = 'Requires';
 $string['purgecaches']= 'Purge all caches';
 $string['purgecachesconfirm']= 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data.  Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code.  There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
 $string['purgecachesfinished']= 'All caches were purged.';
+$string['requestcategoryselection'] = 'Enable category selection';
 $string['restorernewroleid'] = 'Restorers\' role in courses';
 $string['restorernewroleid_help'] = 'If the user does not already have the permission to manage the newly restored course, the user is automatically assigned this role and enrolled if necessary. Select "None" if you do not want restorers to be able to manage every restored course.';
 $string['reverseproxy'] = 'Reverse proxy';
index e053c96..23216e2 100644 (file)
@@ -210,6 +210,7 @@ $string['restoretonewcourse'] = 'Restore as a new course';
 $string['restoringcourse'] = 'Course restoration in progress';
 $string['restoringcourseshortname'] = 'restoring';
 $string['restorerolemappings'] = 'Restore role mappings';
+$string['rootenrolmanual'] = 'Restore as manual enrolments';
 $string['rootsettings'] = 'Backup settings';
 $string['rootsettingusers'] = 'Include enrolled users';
 $string['rootsettinganonymize'] = 'Anonymize user information';
index dd0b7b4..3f1cc5f 100644 (file)
@@ -59,7 +59,7 @@ $string['eventduration'] = 'Duration';
 $string['eventendtime'] = 'End time';
 $string['eventinstanttime'] = 'Time';
 $string['eventkind'] = 'Type of event';
-$string['eventname'] = 'Name';
+$string['eventname'] = 'Event title';
 $string['eventnone'] = 'No events';
 $string['eventrepeat'] = 'Repeats';
 $string['eventsall'] = 'All events';
index f7f3084..ba2def6 100644 (file)
@@ -363,6 +363,7 @@ $string['createaccount'] = 'Create my new account';
 $string['createcategory'] = 'Create category';
 $string['createfolder'] = 'Create a folder in {$a}';
 $string['createuserandpass'] = 'Choose your username and password';
+$string['createuser'] = 'Create user';
 $string['createziparchive'] = 'Create zip archive';
 $string['creatingblocks'] = 'Creating blocks';
 $string['creatingblocksroles'] = 'Creating block level role assignments and overrides';
@@ -1670,6 +1671,7 @@ $string['theme'] = 'Theme';
 $string['themes'] = 'Themes';
 $string['themesaved'] = 'New theme saved';
 $string['thereareno'] = 'There are no {$a} in this course';
+$string['therearecourses'] = 'There are {$a} courses';
 $string['thiscategorycontains'] = 'This category contains';
 $string['time'] = 'Time';
 $string['timezone'] = 'Timezone';
index c2bf463..02763e5 100644 (file)
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['downloadas'] = 'Download table data as';
-$string['downloadcsv'] = 'a comma separated values text file';
-$string['downloadexcel'] = 'a Microsoft Excel spreadsheet';
-$string['downloadods'] = 'an OpenDocument Spreadsheet (ODS)';
+$string['downloadas'] = 'Download table data as {$a->formatsmenu} {$a->downloadbutton}';
+$string['downloadcsv'] = 'Comma separated values text file';
+$string['downloadexcel'] = 'Excel spreadsheet';
+$string['downloadods'] = 'OpenDocument spreadsheet';
 $string['downloadoptions'] = 'Select download options';
-$string['downloadtsv'] = 'a tab separated values text file';
-$string['downloadxhtml'] = 'an unpaged XHTML document';
+$string['downloadtsv'] = 'Tab separated values text file';
+$string['downloadxhtml'] = 'Unpaged XHTML document';
index 8ee900a..1bc2a43 100644 (file)
@@ -6377,9 +6377,8 @@ function format_admin_setting($setting, $title='', $form='', $description='', $l
     $str = '
 <div class="form-item clearfix" id="admin-'.$setting->name.'">
   <div class="form-label">
-    <label '.$labelfor.'>'.highlightfast($query, $title).'<span class="form-shortname">'.highlightfast($query, $name).'</span>
-      '.$override.$warning.'
-    </label>
+    <label '.$labelfor.'>'.highlightfast($query, $title).$override.$warning.'</label>
+    <span class="form-shortname">'.highlightfast($query, $name).'</span>
   </div>
   <div class="form-setting">'.$form.$defaultinfo.'</div>
   <div class="form-description">'.highlight($query, markdown_to_html($description)).'</div>
index 246ddee..e9c133f 100644 (file)
@@ -1158,7 +1158,7 @@ class block_manager {
             $PAGE->set_title($blocktitle . ': ' . $strdeletecheck);
             $PAGE->set_heading($site->fullname);
             echo $OUTPUT->header();
-            $confirmurl = new moodle_url("$deletepage->url?", array('sesskey' => sesskey(), 'bui_deleteid' => $block->instance->id, 'bui_confirm' => 1));
+            $confirmurl = new moodle_url($deletepage->url, array('sesskey' => sesskey(), 'bui_deleteid' => $block->instance->id, 'bui_confirm' => 1));
             $cancelurl = new moodle_url($deletepage->url);
             $yesbutton = new single_button($confirmurl, get_string('yes'));
             $nobutton = new single_button($cancelurl, get_string('no'));
index 97e90b7..abf24df 100644 (file)
@@ -1765,6 +1765,11 @@ function user_accesstime_log($courseid=0) {
         return;
     }
 
+    if (isguestuser()) {
+        // Do not update guest access times/ips for performance.
+        return;
+    }
+
     if (empty($courseid)) {
         $courseid = SITEID;
     }
index 35c7bfc..bcd5920 100644 (file)
         <FIELD NAME="fullname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="shortname"/>
         <FIELD NAME="shortname" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="fullname" NEXT="summary"/>
         <FIELD NAME="summary" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="shortname" NEXT="summaryformat"/>
-        <FIELD NAME="summaryformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="summary" NEXT="reason"/>
-        <FIELD NAME="reason" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="summaryformat" NEXT="requester"/>
+        <FIELD NAME="summaryformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="summary" NEXT="category"/>
+        <FIELD NAME="category" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="summaryformat" NEXT="reason"/>
+        <FIELD NAME="reason" TYPE="text" NOTNULL="true" SEQUENCE="false" PREVIOUS="category" NEXT="requester"/>
         <FIELD NAME="requester" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="reason" NEXT="password"/>
         <FIELD NAME="password" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="requester"/>
       </FIELDS>
index f3b34fd..4fc23fc 100644 (file)
@@ -248,7 +248,7 @@ function xmldb_main_upgrade($oldversion) {
         if ($CFG->restrictmodulesfor === 'all') {
             $courses = $DB->get_records_menu('course', array(), 'id', 'id, 1');
         } else if ($CFG->restrictmodulesfor === 'requested') {
-            $courses = $DB->get_records_menu('course', array('retrictmodules' => 1), 'id', 'id, 1');
+            $courses = $DB->get_records_menu('course', array('restrictmodules' => 1), 'id', 'id, 1');
         } else {
             $courses = array();
         }
@@ -314,7 +314,7 @@ function xmldb_main_upgrade($oldversion) {
 
     if ($oldversion < 2012031500.02) {
 
-        // Define field retrictmodules to be dropped from course
+        // Define field restrictmodules to be dropped from course
         $table = new xmldb_table('course');
         $field = new xmldb_field('restrictmodules');
 
@@ -1122,8 +1122,8 @@ function xmldb_main_upgrade($oldversion) {
     if ($oldversion < 2012082300.01) {
         // Add more custom enrol fields.
         $table = new xmldb_table('enrol');
-
         $field = new xmldb_field('customint5', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'customint4');
+
         if (!$dbman->field_exists($table, $field)) {
             $dbman->add_field($table, $field);
         }
@@ -1194,5 +1194,17 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012090500.00);
     }
 
+    if ($oldversion < 2012090700.01) {
+        // Add a category field in the course_request table
+        $table = new xmldb_table('course_request');
+        $field = new xmldb_field('category', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, 'summaryformat');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012090700.01);
+    }
+
     return true;
 }
index dfe7f45..3484b04 100644 (file)
@@ -160,13 +160,12 @@ class ddl_testcase extends database_driver_testcase {
         // first make sure it returns false if table does not exist
         $table = $this->tables['test_table0'];
 
-        ob_start(); // hide debug warning
         try {
             $result = $DB->get_records('test_table0');
         } catch (dml_exception $e) {
             $result = false;
         }
-        ob_end_clean();
+        $this->resetDebugging();
 
         $this->assertFalse($result);
 
@@ -183,13 +182,12 @@ class ddl_testcase extends database_driver_testcase {
         // drop table and test again
         $dbman->drop_table($table);
 
-        ob_start(); // hide debug warning
         try {
             $result = $DB->get_records('test_table0');
         } catch (dml_exception $e) {
             $result = false;
         }
-        ob_end_clean();
+        $this->resetDebugging();
 
         $this->assertFalse($result);
 
@@ -1123,13 +1121,12 @@ class ddl_testcase extends database_driver_testcase {
         $record = new stdClass();
         $record->name = NULL;
 
-        ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
         } catch (dml_exception $e) {
             $result = false;
         }
-        ob_end_clean();
+        $this->resetDebugging();
         $this->assertFalse($result);
 
         $field = new xmldb_field('name');
@@ -1145,13 +1142,12 @@ class ddl_testcase extends database_driver_testcase {
         $field->set_attributes(XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null);
         $dbman->change_field_notnull($table, $field);
 
-        ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
         } catch (dml_exception $e) {
             $result = false;
         }
-        ob_end_clean();
+        $this->resetDebugging();
         $this->assertFalse($result);
 
         $dbman->drop_table($table);
@@ -1214,13 +1210,12 @@ class ddl_testcase extends database_driver_testcase {
         $index->set_attributes(XMLDB_INDEX_UNIQUE, array('onenumber', 'name'));
         $dbman->add_index($table, $index);
 
-        ob_start(); // hide debug warning
         try {
             $result = $DB->insert_record('test_table_cust0', $record, false);
         } catch (dml_exception $e) {
             $result = false;;
         }
-        ob_end_clean();
+        $this->resetDebugging();
         $this->assertFalse($result);
 
         $dbman->drop_table($table);
@@ -1398,12 +1393,10 @@ class ddl_testcase extends database_driver_testcase {
 
         // feed nonexistent file
         try {
-            ob_start(); // hide debug warning
             $dbman->delete_tables_from_xmldb_file('fpsoiudfposui');
-            ob_end_clean();
             $this->assertTrue(false);
         } catch (Exception $e) {
-            ob_end_clean();
+            $this->resetDebugging();
             $this->assertTrue($e instanceof moodle_exception);
         }
 
@@ -1414,12 +1407,10 @@ class ddl_testcase extends database_driver_testcase {
             $devhack = true;
         }
         try {
-            ob_start(); // hide debug warning
             $dbman->delete_tables_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
             $this->assertTrue(false);
-            ob_end_clean();
         } catch (Exception $e) {
-            ob_end_clean();
+            $this->resetDebugging();
             $this->assertTrue($e instanceof moodle_exception);
         }
         if ($devhack) {
@@ -1443,12 +1434,10 @@ class ddl_testcase extends database_driver_testcase {
 
         // feed nonexistent file
         try {
-            ob_start(); // hide debug warning
             $dbman->install_from_xmldb_file('fpsoiudfposui');
-            ob_end_clean();
             $this->assertTrue(false);
         } catch (Exception $e) {
-            ob_end_clean();
+            $this->resetDebugging();
             $this->assertTrue($e instanceof moodle_exception);
         }
 
@@ -1459,12 +1448,10 @@ class ddl_testcase extends database_driver_testcase {
             $devhack = true;
         }
         try {
-            ob_start(); // hide debug warning
             $dbman->install_from_xmldb_file(__DIR__ . '/fixtures/invalid.xml');
-            ob_end_clean();
             $this->assertTrue(false);
         } catch (Exception $e) {
-            ob_end_clean();
+            $this->resetDebugging();
             $this->assertTrue($e instanceof moodle_exception);
         }
         if ($devhack) {
@@ -1555,10 +1542,9 @@ class ddl_testcase extends database_driver_testcase {
         $this->assertTrue($dbman->table_exists('test_table1'));
 
         // Make sure it can be dropped using deprecated drop_temp_table()
-        $CFG->debug = 0;
         $dbman->drop_temp_table($table1);
         $this->assertFalse($dbman->table_exists('test_table1'));
-        $CFG->debug = DEBUG_DEVELOPER;
+        $this->assertDebuggingCalled();
     }
 
     public function test_concurrent_temp_tables() {
index 4fa3ca4..ef53f08 100644 (file)
@@ -1101,6 +1101,20 @@ abstract class moodle_database {
      */
     public abstract function get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);
 
+    /**
+     * Get all records from a table.
+     *
+     * This method works around potential memory problems and may improve performance,
+     * this method may block access to table until the recordset is closed.
+     *
+     * @param string $table Name of database table.
+     * @return moodle_recordset A moodle_recordset instance {@link function get_recordset}.
+     * @throws dml_exception A DML specific exception is thrown for any errors.
+     */
+    public function export_table_recordset($table) {
+        return $this->get_recordset($table, array());
+    }
+
     /**
      * Get a number of records as an array of objects where all the given conditions met.
      *
index 38eb523..5a91e9c 100644 (file)
@@ -909,6 +909,27 @@ class mysqli_native_moodle_database extends moodle_database {
         return $this->create_recordset($result);
     }
 
+    /**
+     * Get all records from a table.
+     *
+     * This method works around potential memory problems and may improve performance,
+     * this method may block access to table until the recordset is closed.
+     *
+     * @param string $table Name of database table.
+     * @return moodle_recordset A moodle_recordset instance {@link function get_recordset}.
+     * @throws dml_exception A DML specific exception is thrown for any errors.
+     */
+    public function export_table_recordset($table) {
+        $sql = $this->fix_table_names("SELECT * FROM {{$table}}");
+
+        $this->query_start($sql, array(), SQL_QUERY_SELECT);
+        // MYSQLI_STORE_RESULT may eat all memory for large tables, unfortunately MYSQLI_USE_RESULT blocks other queries.
+        $result = $this->mysqli->query($sql, MYSQLI_USE_RESULT);
+        $this->query_end($result);
+
+        return $this->create_recordset($result);
+    }
+
     protected function create_recordset($result) {
         return new mysqli_native_moodle_recordset($result);
     }
index 4289cbb..11e53bc 100644 (file)
@@ -51,17 +51,6 @@ class dml_testcase extends database_driver_testcase {
         return new xmldb_table($tablename);
     }
 
-    protected function enable_debugging() {
-        ob_start(); // hide debug warning
-    }
-
-    protected function get_debugging() {
-        $debuginfo = ob_get_contents();
-        ob_end_clean();
-
-        return $debuginfo;
-    }
-
     function test_diagnose() {
         $DB = $this->tdb;
         $result = $DB->diagnose();
@@ -982,10 +971,7 @@ class dml_testcase extends database_driver_testcase {
         $conditions = array('onetext' => '1');
         try {
             $rs = $DB->get_recordset($tablename, $conditions);
-            if (debugging()) {
-                // only in debug mode - hopefully all devs test code in debug mode...
-                $this->fail('An Exception is missing, expected due to equating of text fields');
-            }
+            $this->fail('An Exception is missing, expected due to equating of text fields');
         } catch (exception $e) {
             $this->assertTrue($e instanceof dml_exception);
             $this->assertEquals($e->errorcode, 'textconditionsnotallowed');
@@ -1300,6 +1286,36 @@ class dml_testcase extends database_driver_testcase {
         // note: fetching nulls, empties, LOBs already tested by test_insert_record() no needed here
     }
 
+    public function test_export_table_recordset() {
+        $DB = $this->tdb;
+        $dbman = $DB->get_manager();
+
+        $table = $this->get_test_table();
+        $tablename = $table->getName();
+
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $dbman->create_table($table);
+
+        $ids = array();
+        $ids[] = $DB->insert_record($tablename, array('course' => 3));
+        $ids[] = $DB->insert_record($tablename, array('course' => 5));
+        $ids[] = $DB->insert_record($tablename, array('course' => 4));
+        $ids[] = $DB->insert_record($tablename, array('course' => 3));
+        $ids[] = $DB->insert_record($tablename, array('course' => 2));
+        $ids[] = $DB->insert_record($tablename, array('course' => 1));
+        $ids[] = $DB->insert_record($tablename, array('course' => 0));
+
+        $rs = $DB->export_table_recordset($tablename);
+        $rids = array();
+        foreach ($rs as $record) {
+            $rids[] = $record->id;
+        }
+        $rs->close();
+        $this->assertEquals($ids, $rids, '', 0, 0, true);
+    }
+
     public function test_get_records() {
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
@@ -1439,6 +1455,8 @@ class dml_testcase extends database_driver_testcase {
     }
 
     public function test_get_records_sql() {
+        global $CFG;
+
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
 
@@ -1477,10 +1495,14 @@ class dml_testcase extends database_driver_testcase {
         $this->assertEquals($inskey4, next($records)->id);
 
         // Awful test, requires debug enabled and sent to browser. Let's do that and restore after test
-        $this->enable_debugging();
         $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
-        $this->assertFalse($this->get_debugging() === '');
+        $this->assertDebuggingCalled();
         $this->assertEquals(6, count($records));
+        $CFG->debug = DEBUG_MINIMAL;
+        $records = $DB->get_records_sql("SELECT course AS id, course AS course FROM {{$tablename}}", null);
+        $this->assertDebuggingNotCalled();
+        $this->assertEquals(6, count($records));
+        $CFG->debug = DEBUG_DEVELOPER;
 
         // negative limits = no limits
         $records = $DB->get_records_sql("SELECT * FROM {{$tablename}} ORDER BY id", null, -1, -1);
@@ -1692,6 +1714,8 @@ class dml_testcase extends database_driver_testcase {
     }
 
     public function test_get_record_sql() {
+        global $CFG;
+
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
 
@@ -1728,9 +1752,12 @@ class dml_testcase extends database_driver_testcase {
             $this->assertTrue(true);
         }
 
-        $this->enable_debugging();
         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
-        $this->assertFalse($this->get_debugging() === '');
+        $this->assertDebuggingCalled();
+        $CFG->debug = DEBUG_MINIMAL;
+        $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MISSING));
+        $this->assertDebuggingNotCalled();
+        $CFG->debug = DEBUG_DEVELOPER;
 
         // multiple matches ignored
         $this->assertNotEmpty($DB->get_record_sql("SELECT * FROM {{$tablename}}", array(), IGNORE_MULTIPLE));
@@ -1772,13 +1799,11 @@ class dml_testcase extends database_driver_testcase {
             $this->assertTrue(true);
         }
 
-        $this->enable_debugging();
         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MULTIPLE));
-        $this->assertSame($this->get_debugging(), '');
+        $this->assertDebuggingNotCalled();
 
-        $this->enable_debugging();
         $this->assertEquals(5, $DB->get_field($tablename, 'course', array('course' => 5), IGNORE_MISSING));
-        $this->assertFalse($this->get_debugging() === '');
+        $this->assertDebuggingCalled();
 
         // test for exception throwing on text conditions being compared. (MDL-24863, unwanted auto conversion of param to int)
         $conditions = array('onetext' => '1');
index 7051c82..4d9b7bb 100644 (file)
@@ -144,7 +144,7 @@ abstract class database_exporter {
         $tables = $this->schema->getTables();
         $this->begin_database_export($CFG->version, $CFG->release, date('c'), $description);
         foreach ($tables as $table) {
-            $rs = $this->mdb->get_recordset_sql('SELECT * FROM {'.$table->getName().'}');
+            $rs = $this->mdb->export_table_recordset($table->getName());
             if (!$rs) {
                 throw new ddl_table_missing_exception($table->getName());
             }
@@ -153,6 +153,7 @@ abstract class database_exporter {
                 $this->export_table_data($table, $row);
             }
             $this->finish_table_export($table);
+            $rs->close();
         }
         $this->finish_database_export();
     }
similarity index 67%
rename from lib/editor/tinymce/extra/strings.php
rename to lib/editor/tinymce/all_strings.php
index e8de348..9d71124 100644 (file)
 define('NO_MOODLE_COOKIES', true);
 define('NO_UPGRADE_CHECK', true);
 
-require('../../../../config.php');
+require('../../../config.php');
+require_once("$CFG->dirroot/lib/jslib.php");
+require_once("$CFG->dirroot/lib/configonlylib.php");
 
 $lang  = optional_param('elanguage', 'en', PARAM_SAFEDIR);
-$theme = optional_param('etheme', 'advanced', PARAM_SAFEDIR);
+$rev   = optional_param('rev', -1, PARAM_INT);
 
 $PAGE->set_context(context_system::instance());
 $PAGE->set_url('/lib/editor/tinymce/extra/strings.php');
 
 if (!get_string_manager()->translation_exists($lang, false)) {
     $lang = 'en';
+    $rev = -1; // Do not cache missing langs.
+}
+
+$candidate = "$CFG->cachedir/editor_tinymce/$rev/$lang.js";
+$etag = sha1("$lang/$rev");
+
+if ($rev > -1 and file_exists($candidate)) {
+    if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+        // we do not actually need to verify the etag value because our files
+        // never change in cache because we increment the rev parameter
+        js_send_unmodified(filemtime($candidate), $etag);
+    }
+    js_send_cached($candidate, $etag, 'all_strings.php');
 }
 
 $string = get_string_manager()->load_component_strings('editor_tinymce', $lang);
@@ -66,18 +81,21 @@ foreach (get_plugin_list('tinymce') as $component => $ignored) {
             // Ignore malformed strings with more colons.
             continue;
         }
-        $result[$parts[0]][$parts[1]] = $value;
+        $component = $parts[0];
+        $string = $parts[1];
+        $result[$component][$string] = $value;
     }
 }
 
 $output = 'tinyMCE.addI18n({'.$lang.':'.json_encode($result).'});';
 
-$lifetime = '10'; // TODO: increase later
-@header('Content-type: text/javascript; charset=utf-8');
-@header('Content-length: '.strlen($output));
-@header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
-@header('Cache-control: max-age='.$lifetime);
-@header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .'GMT');
-@header('Pragma: ');
+if ($rev > -1) {
+    js_write_cache_file_content($candidate, $output);
+    // verify nothing failed in cache file creation
+    clearstatcache();
+    if (file_exists($candidate)) {
+        js_send_cached($candidate, $etag, 'all_strings.php');
+    }
+}
 
-echo $output;
+js_send_uncached($output, 'all_strings.php');
diff --git a/lib/editor/tinymce/cli/update_lang_files.php b/lib/editor/tinymce/cli/update_lang_files.php
new file mode 100644 (file)
index 0000000..790d42e
--- /dev/null
@@ -0,0 +1,215 @@
+<?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/>.
+
+/**
+ * This script imports TinyMCE lang strings into Moodle English lang pack.
+ *
+ * @package    editor_tinymce
+ * @copyright  2009 Petr Skoda (http://skodak.org)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+
+require __DIR__ . '/../../../../config.php';
+
+if (!debugging('', DEBUG_DEVELOPER)) {
+    die('Only for developers!!!!!');
+}
+
+// Current strings in our lang pack.
+$old_strings = editor_tinymce_get_all_strings();
+ksort($old_strings);
+
+// Upstream strings.
+$parsed = editor_tinymce_parse_js_files();
+ksort($parsed);
+
+// Our modifications and upstream changes in existing strings.
+$tweaked = array();
+
+// Detect changes and new additions - ignore case difference, no UTF-8 here.
+foreach ($parsed as $key=>$value) {
+    if (array_key_exists($key, $old_strings)) {
+        $oldvalue = $old_strings[$key];
+        if (strtolower($oldvalue) === strtolower($value)) {
+            $parsed[$key] = $oldvalue;
+        } else {
+            $tweaked[$key] = $oldvalue;
+        }
+        unset($old_strings[$key]);
+    }
+}
+
+if (!$handle = fopen("$CFG->dirroot/lib/editor/tinymce/lang/en/editor_tinymce.php", 'w')) {
+     echo "Cannot write to $filename !!";
+     exit(1);
+}
+
+$header = <<<EOT
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for component 'editor_tinymce', language 'en'.
+ *
+ * Note: use editor/tinymce/extra/tools/update_lang_files.php script to import strings from upstream JS lang files.
+ *
+ * @package    editor_tinymce
+ * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+EOT;
+
+fwrite($handle, $header);
+
+fwrite($handle, "\n\n//== Custom Moodle strings that are not part of upstream TinyMCE ==\n");
+foreach ($old_strings as $key=>$value) {
+    fwrite($handle, editor_tinymce_encode_stringline($key, $value));
+}
+
+fwrite($handle, "\n\n// == TinyMCE upstream lang strings from all standard upstream plugins ==\n");
+foreach ($parsed as $key=>$value) {
+    fwrite($handle, editor_tinymce_encode_stringline($key, $value, isset($tweaked[$key])));
+}
+
+if ($tweaked) {
+    fwrite($handle, "\n\n// == Our modifications or upstream changes ==\n");
+    foreach ($tweaked as $key=>$value) {
+        fwrite($handle, editor_tinymce_encode_stringline($key, $value));
+    }
+}
+
+fclose($handle);
+
+get_string_manager()->reset_caches();
+die("\nFinished update of EN lang pack (other langs have to be imported via AMOS)\n\n");
+
+
+
+/// ============ Utility functions ========================
+
+function editor_tinymce_encode_stringline($key, $value, $commentedout=false) {
+    $return = "\$string['$key'] = ".var_export($value, true).";";
+    if ($commentedout) {
+        $return = "/* $return */";
+    }
+    return $return."\n";
+}
+
+function editor_tinymce_get_all_strings() {
+    $sm = get_string_manager();
+    return $sm->load_component_strings('editor_tinymce', 'en', true, true);
+}
+
+function editor_tinymce_parse_js_files() {
+    global $CFG;
+
+    require_once("$CFG->libdir/editorlib.php");
+    $editor = get_texteditor('tinymce');
+    $basedir = "$CFG->libdir/editor/tinymce/tiny_mce/$editor->version";
+
+    $files = array();
+    $strings = array();
+
+    $files['simple'] = "$basedir/themes/simple/langs/en.js";
+    $files['advanced'] = "$basedir/themes/advanced/langs/en.js";
+    $files['advanced_dlg'] = "$basedir/themes/advanced/langs/en_dlg.js";
+
+    $items = new DirectoryIterator("$basedir/plugins/");
+    foreach ($items as $item) {
+        if ($item->isDot() or !$item->isDir()) {
+            continue;
+        }
+        $plugin = $item->getFilename();
+        if ($plugin === 'example') {
+            continue;
+        }
+        if (file_exists("$basedir/plugins/$plugin/langs/en.js")) {
+            $files[$plugin] = "$basedir/plugins/$plugin/langs/en.js";
+        }
+        if (file_exists("$basedir/plugins/$plugin/langs/en_dlg.js")) {
+            $files[$plugin.'_dlg'] = "$basedir/plugins/$plugin/langs/en_dlg.js";
+        }
+        unset($item);
+    }
+    unset($items);
+
+    // It would be too easy if TinyMCE used standard JSON in lang files...
+
+    // Core upstream pack.
+    $content = file_get_contents("$basedir/langs/en.js");
+    $content = trim($content);
+    $content = preg_replace("/^tinyMCE.addI18n\(\{en:/", '', $content);
+    $content = preg_replace("/\}\);$/", '', $content);
+    $content = preg_replace("/([\{,])([a-zA-Z0-9_]+):/", '$1"$2":', $content);
+    $content = preg_replace("/:'([^']*)'/", ':"$1"', $content);
+    $content = str_replace("\\'", "'", $content);
+    $maindata = json_decode($content, true);
+
+    if (is_null($maindata) or json_last_error() != 0) {
+        echo "error processing main lang file\n";
+        echo $content."\n\n";
+        exit(1);
+    }
+    foreach($maindata as $component=>$data) {
+        foreach ($data as $key=>$value) {
+            $strings["$component:$key"] = $value;
+        }
+    }
+    unset($content);
+    unset($maindata);
+
+    // Upstream plugins.
+    foreach($files as $plugin=>$path) {
+        $content = file_get_contents($path);
+        $content = trim($content);
+        $content = preg_replace("/^tinyMCE\.addI18n\('en\.[a-z09_]*',\s*/", '', $content);
+        $content = preg_replace("/\);$/", '', $content);
+
+        $content = preg_replace('/(\{|"\s*,)\s*([a-z0-9_]+)\s*:\s*"/m', '$1"$2":"', $content);
+        $content = str_replace("\\'", "'", $content);
+
+        $data = json_decode($content, true);
+        if (is_null($data) or json_last_error() != 0) {
+            echo "error processing $path lang file\n";
+            echo $content."\n\n";
+            exit(1);
+        }
+        foreach ($data as $key=>$value) {
+            if ($key === '_empty_') {
+                continue;
+            }
+            $strings["$plugin:$key"] = $value;
+        }
+    }
+
+    return $strings;
+}
diff --git a/lib/editor/tinymce/extra/tools/download_langs.sh b/lib/editor/tinymce/extra/tools/download_langs.sh
deleted file mode 100644 (file)
index 7c185f4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-mkdir temp
-mkdir temp/tinylangs
-mkdir temp/langs
-for lang in 'sq' 'ar' 'az' 'be' 'bn' 'nb' 'bs' 'br' 'bg' 'ca' 'ch' 'zh' 'hr' 'cs' 'da' 'dv' 'nl' 'en' 'et' 'fi' 'fr' 'gl' 'de' 'el' 'gu' 'he' 'hi' 'hu' 'is' 'id' 'ia' 'it' 'ja' 'ko' 'lv' 'lt' 'mk' 'ms' 'mn' 'se' 'no' 'nn' 'fa' 'pl' 'pt' 'ro' 'ru' 'sc' 'sr' 'ii' 'si' 'sk' 'sl' 'es' 'sv' 'ta' 'tt' 'te' 'th' 'tr' 'tw' 'uk' 'cy' 'vi'
-do
- wget "http://services.moxiecode.com/i18n/download.aspx?format=xml&code=$lang&product=tinymce" -O temp/tinylangs/$lang.xml
-done
\ No newline at end of file
diff --git a/lib/editor/tinymce/extra/tools/update_lang_files.php b/lib/editor/tinymce/extra/tools/update_lang_files.php
deleted file mode 100644 (file)
index 5223e8b..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-<?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/>.
-
-/**
- * This script imports TinyMCE lang strings into Moodle lang packs.
- *
- * @package    editor_tinymce
- * @copyright  2009 Petr Skoda (http://skodak.org)
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-define('CLI_SCRIPT', true);
-require dirname(dirname(dirname(dirname(dirname(dirname(__FILE__)))))).'/config.php';
-
-if (!debugging('', DEBUG_DEVELOPER)) {
-    die('Only for developers!!!!!');
-}
-
-$langconversion = array(
-    // Mapping of TinyMCE lang codes to Moodle codes.
-    'nb'    => 'no',
-    'sr'    => 'sr_lt',
-
-    // Ignore the following files due to known errors.
-    'ch'    => false,   // XML parsing error, Moodle does not seem to have Chamorro yet anyway
-    'zh'    => false,   // XML parsing error, use 'zh' => 'zh_tw' when sorted out
-);
-
-$targetlangdir = "$CFG->dirroot/lib/editor/tinymce/extra/tools/temp/langs"; // change if needed
-$tempdir       = "$CFG->dirroot/lib/editor/tinymce/extra/tools/temp/tinylangs";
-$enfile        = "$CFG->dirroot/lib/editor/tinymce/lang/en/editor_tinymce.php";
-
-
-/// First update English lang pack.
-if (!file_exists("$tempdir/en.xml")) {
-    die('Missing temp/tinylangs/en.xml! Did you download langs?');
-}
-$old_strings = editor_tinymce_get_all_strings('en');
-ksort($old_strings);
-
-// Our modifications and upstream changes in existing strings.
-$tweaked = array();
-
-// Detect changes and new additions.
-$parsed = editor_tinymce_parse_xml_lang("$tempdir/en.xml");
-ksort($parsed);
-foreach ($parsed as $key=>$value) {
-    if (array_key_exists($key, $old_strings)) {
-        $oldvalue = $old_strings[$key];
-        if ($oldvalue !== $value) {
-            $tweaked[$key] = $oldvalue;
-        }
-        unset($old_strings[$key]);
-    }
-}
-
-if (!$handle = fopen($enfile, 'w')) {
-     echo "Cannot write to $filename !!";
-     exit;
-}
-
-$header = <<<EOT
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Strings for component 'editor_tinymce', language 'en'
- *
- * @package    editor_tinymce
- * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-EOT;
-
-fwrite($handle, $header);
-
-fwrite($handle, "\n\n//== Custom Moodle strings that are not part of upstream TinyMCE ==\n");
-foreach ($old_strings as $key=>$value) {
-    fwrite($handle, editor_tinymce_encode_stringline($key, $value));
-}
-
-fwrite($handle, "\n\n// == TinyMCE upstream lang strings from all standard upstream plugins ==\n");
-foreach ($parsed as $key=>$value) {
-    fwrite($handle, editor_tinymce_encode_stringline($key, $value));
-}
-
-if ($tweaked) {
-    fwrite($handle, "\n\n// == Our modifications or upstream changes ==\n");
-    foreach ($tweaked as $key=>$value) {
-        fwrite($handle, editor_tinymce_encode_stringline($key, $value));
-    }
-}
-
-fclose($handle);
-
-// Now update all other langs.
-$en_strings = editor_tinymce_get_all_strings('en');
-if (!file_exists($targetlangdir)) {
-    echo "Can not find target lang dir: $targetlangdir !!";
-}
-
-$xmlfiles = new DirectoryIterator($tempdir);
-foreach ($xmlfiles as $xmlfile) {
-    if ($xmlfile->isDot() or $xmlfile->isLink() or $xmlfile->isDir()) {
-        continue;
-    }
-
-    $filename = $xmlfile->getFilename();
-
-    if ($filename == 'en.xml') {
-        continue;
-    }
-    if (substr($filename, -4) !== '.xml') {
-        continue;
-    }
-
-    $xmllang = substr($filename, 0, strlen($filename) - 4);
-
-    echo "Processing $xmllang ...\n";
-
-    if (array_key_exists($xmllang, $langconversion)) {
-        $lang = $langconversion[$xmllang];
-        if (empty($lang)) {
-            echo "  Ignoring: $xmllang\n";
-            continue;
-        } else {
-            echo "  Mapped to: $lang\n";
-        }
-    } else {
-        $lang = $xmllang;
-    }
-
-    $langfile = "$targetlangdir/$lang/editor_tinymce.php";
-    if (!file_exists(dirname($langfile))) {
-        mkdir(dirname($langfile), 0755, true);
-    }
-
-    if (file_exists($langfile)) {
-        unlink($langfile);
-    }
-
-    $parsed = editor_tinymce_parse_xml_lang("$tempdir/$xmlfile");
-    ksort($parsed);
-
-    if (!$handle = fopen($langfile, 'w')) {
-         echo "*** Cannot write to $langfile !!\n";
-         continue;
-    }
-
-    fwrite($handle, "<?php\n\n// upload this file into the AMOS stage, rebase the stage, review the changes and commit\n");
-    foreach ($parsed as $key=>$value) {
-        fwrite($handle, editor_tinymce_encode_stringline($key, $value));
-    }
-
-    fclose($handle);
-}
-unset($xmlfiles);
-
-
-die("\nFinished!\n\n");
-
-
-
-
-
-
-
-/// ============ Utility functions ========================
-
-function editor_tinymce_encode_stringline($key, $value) {
-        return "\$string['$key'] = ".var_export($value, true).";\n";
-}
-
-function editor_tinymce_parse_xml_lang($file) {
-    $result = array();
-
-    $doc = new DOMDocument();
-    $doc->load($file);
-    $groups = $doc->getElementsByTagName('group');
-    foreach($groups as $group) {
-        $section = $group->getAttribute('target');
-        $items = $group->getElementsByTagName('item');
-        foreach($items as $item) {
-            $name  = $item->getAttribute('name');
-            $value = $item->textContent;
-            //undo quoted stuff
-            $value = str_replace('\n', "\n", $value);
-            $value = str_replace('\'', "'", $value);
-            $value = str_replace('\\\\', '\\', $value);
-            $result["$section:$name"] = $value;
-        }
-    }
-    return $result;
-}
-
-function editor_tinymce_get_all_strings($lang) {
-    $sm = get_string_manager();
-    return $sm->load_component_strings('editor_tinymce', $lang);
-}
index fa01e4e..b3c4ee8 100644 (file)
@@ -15,7 +15,9 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Strings for component 'editor_tinymce', language 'en'
+ * Strings for component 'editor_tinymce', language 'en'.
+ *
+ * Note: use editor/tinymce/extra/tools/update_lang_files.php script to import strings from upstream JS lang files.
  *
  * @package    editor_tinymce
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
 
 //== Custom Moodle strings that are not part of upstream TinyMCE ==
 $string['availablebuttons'] = 'Available buttons';
-$string['common:browseimage'] = 'Find or upload an image...';
-$string['common:browsemedia'] = 'Find or upload a sound, video or applet...';
 $string['customtoolbar'] = 'Editor toolbar';
 $string['customtoolbar_desc'] = 'Each line contains a list of comma separated button names, use "|" as a group separator, empty lines are ignored. See <a href="{$a}" target="_blank">{$a}</a> for the list of default TinyMCE buttons.';
 $string['fontselectlist'] = 'Available fonts list';
-$string['media_dlg:filename'] = 'Filename';
 $string['pluginname'] = 'TinyMCE HTML editor';
 $string['settings'] = 'General settings';
 $string['subplugindeleteconfirm'] = 'You are about to completely delete TinyMCE subplugin \'{$a}\'. This will completely delete everything in the database associated with this subplugin. Are you SURE you want to continue?';
@@ -44,25 +43,25 @@ $string['advanced:anchor_delta_width'] = '';
 $string['advanced:anchor_desc'] = 'Insert/edit anchor';
 $string['advanced:backcolor_desc'] = 'Select background color';
 $string['advanced:block'] = 'Format';
-$string['advanced:blockquote'] = 'Blockquote';
-$string['advanced:blockquote_desc'] = 'Blockquote';
+$string['advanced:blockquote'] = 'Block quote';
+$string['advanced:blockquote_desc'] = 'Block quote';
 $string['advanced:bold_desc'] = 'Bold (Ctrl+B)';
-$string['advanced:bullist_desc'] = 'Unordered list';
+$string['advanced:bullist_desc'] = 'Insert/remove bulleted list';
 $string['advanced:charmap_delta_height'] = '';
 $string['advanced:charmap_delta_width'] = '';
-$string['advanced:charmap_desc'] = 'Insert custom character';
+$string['advanced:charmap_desc'] = 'Insert special character';
 $string['advanced:cleanup_desc'] = 'Cleanup messy code';
-$string['advanced:clipboard_msg'] = 'Copy/Cut/Paste is not available in Mozilla and Firefox.
+$string['advanced:clipboard_msg'] = 'Copy/cut/paste is not available in Mozilla and Firefox.
 Do you want more information about this issue?';
 $string['advanced:code'] = 'Code';
-$string['advanced:code_desc'] = 'Edit HTML Source';
+$string['advanced:code_desc'] = 'Edit HTML source';
 $string['advanced:colorpicker_delta_height'] = '';
 $string['advanced:colorpicker_delta_width'] = '';
-$string['advanced:copy_desc'] = 'Copy';
+/* $string['advanced:copy_desc'] = 'Copy (Ctrl+C)'; */
 $string['advanced:custom1_desc'] = 'Your custom description here';
-$string['advanced:cut_desc'] = 'Cut';
+/* $string['advanced:cut_desc'] = 'Cut (Ctrl+X)'; */
 $string['advanced:dd'] = 'Definition description';
-$string['advanced:div'] = 'Div';
+$string['advanced:div'] = 'DIV';
 $string['advanced:dt'] = 'Definition term ';
 $string['advanced:font_size'] = 'Font size';
 $string['advanced:fontdefault'] = 'Font family';
@@ -74,12 +73,13 @@ $string['advanced:h4'] = 'Heading 4';
 $string['advanced:h5'] = 'Heading 5';
 $string['advanced:h6'] = 'Heading 6';
 $string['advanced:help_desc'] = 'Help';
-$string['advanced:hr_desc'] = 'Insert horizontal ruler';
+$string['advanced:help_shortcut'] = 'Press ALT-F10 for toolbar. Press ALT-0 for help';
+$string['advanced:hr_desc'] = 'Insert horizontal line';
 $string['advanced:image_delta_height'] = '';
 $string['advanced:image_delta_width'] = '';
 $string['advanced:image_desc'] = 'Insert/edit image';
 $string['advanced:image_props_desc'] = 'Image properties';
-$string['advanced:indent_desc'] = 'Indent';
+$string['advanced:indent_desc'] = 'Increase indent';
 $string['advanced:italic_desc'] = 'Italic (Ctrl+I)';
 $string['advanced:justifycenter_desc'] = 'Align center';
 $string['advanced:justifyfull_desc'] = 'Align full';
@@ -88,27 +88,31 @@ $string['advanced:justifyright_desc'] = 'Align right';
 $string['advanced:link_delta_height'] = '';
 $string['advanced:link_delta_width'] = '';
 $string['advanced:link_desc'] = 'Insert/edit link';
-$string['advanced:more_colors'] = 'More colors';
+$string['advanced:more_colors'] = 'More colors...';
 $string['advanced:newdocument'] = 'Are you sure you want clear all contents?';
 $string['advanced:newdocument_desc'] = 'New document';
-$string['advanced:numlist_desc'] = 'Ordered list';
-$string['advanced:outdent_desc'] = 'Outdent';
+$string['advanced:numlist_desc'] = 'Insert/remove numbered list';
+$string['advanced:outdent_desc'] = 'Decrease indent';
 $string['advanced:paragraph'] = 'Paragraph';
-$string['advanced:paste_desc'] = 'Paste';
+/* $string['advanced:paste_desc'] = 'Paste (Ctrl+V)'; */
 $string['advanced:path'] = 'Path';
 $string['advanced:pre'] = 'Preformatted';
 $string['advanced:redo_desc'] = 'Redo (Ctrl+Y)';
 $string['advanced:removeformat_desc'] = 'Remove formatting';
+$string['advanced:rich_text_area'] = 'Rich text area';
 $string['advanced:samp'] = 'Code sample';
+/* $string['advanced:shortcuts_desc'] = 'Accessability Help'; */
 $string['advanced:striketrough_desc'] = 'Strikethrough';
 $string['advanced:style_select'] = 'Styles';
 $string['advanced:sub_desc'] = 'Subscript';
 $string['advanced:sup_desc'] = 'Superscript';
-$string['advanced:toolbar_focus'] = 'Jump to tool buttons - Alt+Q, Jump to editor - Alt-Z, Jump to element path - Alt-X';
+$string['advanced:toolbar'] = 'Toolbar';
+$string['advanced:toolbar_focus'] = 'Jump to tool buttons - Alt+Q, jump to editor - Alt-Z, jump to element path - Alt-X';
 $string['advanced:underline_desc'] = 'Underline (Ctrl+U)';
 $string['advanced:undo_desc'] = 'Undo (Ctrl+Z)';
 $string['advanced:unlink_desc'] = 'Unlink';
-$string['advanced:visualaid_desc'] = 'Toggle guidelines/invisible elements';
+$string['advanced:visualaid_desc'] = 'Show/hide guidelines/invisible elements';
+$string['advanced_dlg:'] = '';
 $string['advanced_dlg:about_author'] = 'Author';
 $string['advanced_dlg:about_general'] = 'About';
 $string['advanced_dlg:about_help'] = 'Help';
@@ -118,10 +122,14 @@ $string['advanced_dlg:about_plugin'] = 'Plugin';
 $string['advanced_dlg:about_plugins'] = 'Plugins';
 $string['advanced_dlg:about_title'] = 'About TinyMCE';
 $string['advanced_dlg:about_version'] = 'Version';
+$string['advanced_dlg:accessibility_help'] = 'Accessibility help';
+$string['advanced_dlg:accessibility_usage_title'] = 'General usage';
+$string['advanced_dlg:anchor_invalid'] = 'Please specify a valid anchor name.';
 $string['advanced_dlg:anchor_name'] = 'Anchor name';
 $string['advanced_dlg:anchor_title'] = 'Insert/edit anchor';
-$string['advanced_dlg:charmap_title'] = 'Select custom character';
-$string['advanced_dlg:code_title'] = 'HTML Source Editor';
+$string['advanced_dlg:charmap_title'] = 'Select special character';
+$string['advanced_dlg:charmap_usage'] = 'Use left and right arrows to navigate.';
+$string['advanced_dlg:code_title'] = 'HTML source editor';
 $string['advanced_dlg:code_wordwrap'] = 'Word wrap';
 $string['advanced_dlg:colorpicker_color'] = 'Color:';
 $string['advanced_dlg:colorpicker_name'] = 'Name:';
@@ -149,8 +157,9 @@ $string['advanced_dlg:image_list'] = 'Image list';
 $string['advanced_dlg:image_src'] = 'Image URL';
 $string['advanced_dlg:image_title'] = 'Insert/edit image';
 $string['advanced_dlg:image_vspace'] = 'Vertical space';
-$string['advanced_dlg:link_is_email'] = 'The URL you entered seems to be an email address, do you want to add the required mailto: prefix?';
-$string['advanced_dlg:link_is_external'] = 'The URL you entered seems to external link, do you want to add the required http:// prefix?';
+$string['advanced_dlg:invalid_color_value'] = 'Invalid color value';
+$string['advanced_dlg:link_is_email'] = 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?';
+$string['advanced_dlg:link_is_external'] = 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?';
 $string['advanced_dlg:link_list'] = 'Link list';
 $string['advanced_dlg:link_target'] = 'Target';
 $string['advanced_dlg:link_target_blank'] = 'Open link in a new window';
@@ -158,12 +167,14 @@ $string['advanced_dlg:link_target_same'] = 'Open link in the same window';
 $string['advanced_dlg:link_title'] = 'Insert/edit link';
 $string['advanced_dlg:link_titlefield'] = 'Title';
 $string['advanced_dlg:link_url'] = 'Link URL';
-$string['advhr:advhr_desc'] = 'Horizontal rule';
+$string['advhr:advhr_desc'] = 'Insert horizontal line';
 $string['advhr:delta_height'] = '';
 $string['advhr:delta_width'] = '';
+$string['advhr_dlg:normal'] = 'Normal';
 $string['advhr_dlg:noshade'] = 'No shadow';
 $string['advhr_dlg:size'] = 'Height';
 $string['advhr_dlg:width'] = 'Width';
+$string['advhr_dlg:widthunits'] = 'Units';
 $string['advimage:delta_height'] = '';
 $string['advimage:delta_width'] = '';
 $string['advimage:image_desc'] = 'Insert/edit image';
@@ -185,8 +196,9 @@ $string['advimage_dlg:dialog_title'] = 'Insert/edit image';
 $string['advimage_dlg:dimensions'] = 'Dimensions';
 $string['advimage_dlg:example_img'] = 'Appearance preview image';
 $string['advimage_dlg:general'] = 'General';
+$string['advimage_dlg:height'] = 'Height';
 $string['advimage_dlg:hspace'] = 'Horizontal space';
-$string['advimage_dlg:id'] = 'Id';
+$string['advimage_dlg:id'] = 'ID';
 $string['advimage_dlg:image_list'] = 'Image list';
 $string['advimage_dlg:langcode'] = 'Language code';
 $string['advimage_dlg:langdir'] = 'Language direction';
@@ -195,7 +207,7 @@ $string['advimage_dlg:long_desc'] = 'Long description link';
 $string['advimage_dlg:ltr'] = 'Left to right';
 $string['advimage_dlg:map'] = 'Image map';
 $string['advimage_dlg:misc'] = 'Miscellaneous';
-$string['advimage_dlg:missing_alt'] = 'Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.';
+$string['advimage_dlg:missing_alt'] = 'Are you sure you want to continue without including an image description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off.';
 $string['advimage_dlg:mouseout'] = 'for mouse out';
 $string['advimage_dlg:mouseover'] = 'for mouse over';
 $string['advimage_dlg:preview'] = 'Preview';
@@ -208,10 +220,11 @@ $string['advimage_dlg:tab_appearance'] = 'Appearance';
 $string['advimage_dlg:tab_general'] = 'General';
 $string['advimage_dlg:title'] = 'Title';
 $string['advimage_dlg:vspace'] = 'Vertical space';
+$string['advimage_dlg:width'] = 'Width';
 $string['advlink:delta_height'] = '';
 $string['advlink:delta_width'] = '';
 $string['advlink:link_desc'] = 'Insert/edit link';
-$string['advlink_dlg:accesskey'] = 'Accesskey';
+$string['advlink_dlg:accesskey'] = 'AccessKey';
 $string['advlink_dlg:advanced_props'] = 'Advanced properties';
 $string['advlink_dlg:advanced_tab'] = 'Advanced';
 $string['advlink_dlg:anchor_names'] = 'Anchors';
@@ -221,16 +234,17 @@ $string['advlink_dlg:event_props'] = 'Events';
 $string['advlink_dlg:events_tab'] = 'Events';
 $string['advlink_dlg:general_props'] = 'General properties';
 $string['advlink_dlg:general_tab'] = 'General';
-$string['advlink_dlg:id'] = 'Id';
-$string['advlink_dlg:is_email'] = 'The URL you entered seems to be an email address, do you want to add the required mailto: prefix?';
-$string['advlink_dlg:is_external'] = 'The URL you entered seems to external link, do you want to add the required http:// prefix?';
+$string['advlink_dlg:height'] = 'Height';
+$string['advlink_dlg:id'] = 'ID';
+$string['advlink_dlg:is_email'] = 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?';
+$string['advlink_dlg:is_external'] = 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?';
 $string['advlink_dlg:langcode'] = 'Language code';
 $string['advlink_dlg:langdir'] = 'Language direction';
 $string['advlink_dlg:link_list'] = 'Link list';
 $string['advlink_dlg:list'] = 'Link list';
 $string['advlink_dlg:ltr'] = 'Left to right';
 $string['advlink_dlg:mime'] = 'Target MIME type';
-$string['advlink_dlg:popup'] = 'Javascript popup';
+$string['advlink_dlg:popup'] = 'JavaScript popup';
 $string['advlink_dlg:popup_dependent'] = 'Dependent (Mozilla/Firefox only)';
 $string['advlink_dlg:popup_location'] = 'Show location bar';
 $string['advlink_dlg:popup_menubar'] = 'Show menu bar';
@@ -250,17 +264,18 @@ $string['advlink_dlg:rel'] = 'Relationship page to target';
 $string['advlink_dlg:rev'] = 'Relationship target to page';
 $string['advlink_dlg:rtl'] = 'Right to left';
 $string['advlink_dlg:style'] = 'Style';
-$string['advlink_dlg:tabindex'] = 'Tabindex';
+$string['advlink_dlg:tabindex'] = 'TabIndex';
 $string['advlink_dlg:target'] = 'Target';
 $string['advlink_dlg:target_blank'] = 'Open in new window';
 $string['advlink_dlg:target_langcode'] = 'Target language';
 $string['advlink_dlg:target_name'] = 'Target name';
-$string['advlink_dlg:target_parent'] = 'Open in parent window / frame';
-$string['advlink_dlg:target_same'] = 'Open in this window / frame';
+$string['advlink_dlg:target_parent'] = 'Open in parent window/frame';
+$string['advlink_dlg:target_same'] = 'Open in this window/frame';
 $string['advlink_dlg:target_top'] = 'Open in top frame (replaces all frames)';
 $string['advlink_dlg:title'] = 'Insert/edit link';
 $string['advlink_dlg:titlefield'] = 'Title';
 $string['advlink_dlg:url'] = 'Link URL';
+$string['advlink_dlg:width'] = 'Width';
 $string['advlist:circle'] = 'Circle';
 $string['advlist:def'] = 'Default';
 $string['advlist:disc'] = 'Disc';
@@ -271,26 +286,71 @@ $string['advlist:square'] = 'Square';
 $string['advlist:types'] = 'Types';
 $string['advlist:upper_alpha'] = 'Upper alpha';
 $string['advlist:upper_roman'] = 'Upper roman';
-$string['autosave:restore_content'] = 'Restore auto-saved content.';
+$string['aria:rich_text_area'] = 'Rich text area';
+$string['autosave:restore_content'] = 'Restore auto-saved content';
 $string['autosave:unload_msg'] = 'The changes you made will be lost if you navigate away from this page.';
 $string['autosave:warning_message'] = 'If you restore the saved content, you will lose all the content that is currently in the editor.
 
-Are you sure you want to restore the saved content?.';
+Are you sure you want to restore the saved content?';
+$string['colors:000000'] = 'Black';
+$string['colors:000080'] = 'Navy blue';
+$string['colors:0000FF'] = 'Blue';
+$string['colors:003300'] = 'Dark green';
+$string['colors:003366'] = 'Dark azure';
+$string['colors:008000'] = 'Green';
+$string['colors:008080'] = 'Teal';
+$string['colors:00CCFF'] = 'Sky blue';
+$string['colors:00FF00'] = 'Lime';
+$string['colors:00FFFF'] = 'Aqua';
+$string['colors:333300'] = 'Dark olive';
+$string['colors:333333'] = 'Very dark gray';
+$string['colors:333399'] = 'Indigo';
+$string['colors:3366FF'] = 'Royal blue';
+$string['colors:339966'] = 'Sea green';
+$string['colors:33CCCC'] = 'Turquoise';
+$string['colors:666699'] = 'Grayish blue';
+$string['colors:800000'] = 'Maroon';
+$string['colors:800080'] = 'Purple';
+$string['colors:808000'] = 'Olive';
+$string['colors:808080'] = 'Gray';
+$string['colors:993300'] = 'Burnt orange';
+$string['colors:993366'] = 'Brown';
+$string['colors:999999'] = 'Medium gray';
+$string['colors:99CC00'] = 'Yellow green';
+$string['colors:99CCFF'] = 'Light sky blue';
+$string['colors:C0C0C0'] = 'Silver';
+$string['colors:CC99FF'] = 'Plum';
+$string['colors:CCFFCC'] = 'Pale green';
+$string['colors:CCFFFF'] = 'Pale cyan';
+$string['colors:FF0000'] = 'Red';
+$string['colors:FF00FF'] = 'Magenta';
+$string['colors:FF6600'] = 'Orange';
+$string['colors:FF9900'] = 'Amber';
+$string['colors:FF99CC'] = 'Pink';
+$string['colors:FFCC00'] = 'Gold';
+$string['colors:FFCC99'] = 'Peach';
+$string['colors:FFFF00'] = 'Yellow';
+$string['colors:FFFF99'] = 'Light yellow';
+$string['colors:FFFFFF'] = 'White';
 $string['common:apply'] = 'Apply';
 $string['common:browse'] = 'Browse';
 $string['common:cancel'] = 'Cancel';
 $string['common:class_name'] = 'Class';
-$string['common:clipboard_msg'] = 'Copy/Cut/Paste is not available in Mozilla and Firefox.
+$string['common:clipboard_msg'] = 'Copy/cut/paste is not available in Mozilla and Firefox.
 Do you want more information about this issue?';
 $string['common:clipboard_no_support'] = 'Currently not supported by your browser, use keyboard shortcuts instead.';
 $string['common:close'] = 'Close';
 $string['common:edit_confirm'] = 'Do you want to use the WYSIWYG mode for this textarea?';
 $string['common:insert'] = 'Insert';
 $string['common:invalid_data'] = 'Error: Invalid values entered, these are marked in red.';
-$string['common:more_colors'] = 'More colors';
+$string['common:invalid_data_min'] = '{#field} must be a number greater than {#min}';
+$string['common:invalid_data_number'] = '{#field} must be a number';
+$string['common:invalid_data_size'] = '{#field} must be a number or percentage';
+$string['common:more_colors'] = 'More colors...';
 $string['common:not_set'] = '-- Not set --';
 $string['common:popup_blocked'] = 'Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.';
 $string['common:update'] = 'Update';
+$string['common:value'] = '(value)';
 $string['contextmenu:align'] = 'Alignment';
 $string['contextmenu:center'] = 'Center';
 $string['contextmenu:full'] = 'Full';
@@ -317,6 +377,7 @@ $string['emotions_dlg:surprised'] = 'Surprised';
 $string['emotions_dlg:title'] = 'Insert emotion';
 $string['emotions_dlg:tongue_out'] = 'Tongue out';
 $string['emotions_dlg:undecided'] = 'Undecided';
+$string['emotions_dlg:usage'] = 'Use left and right arrows to navigate.';
 $string['emotions_dlg:wink'] = 'Wink';
 $string['emotions_dlg:yell'] = 'Yell';
 $string['fullpage:delta_height'] = '';
@@ -358,8 +419,8 @@ $string['fullpage_dlg:fontsize'] = 'Font size';
 $string['fullpage_dlg:general_props'] = 'General';
 $string['fullpage_dlg:head_elements'] = 'Head elements';
 $string['fullpage_dlg:hover_color'] = 'Hover color';
-$string['fullpage_dlg:href'] = 'Href';
-$string['fullpage_dlg:hreflang'] = 'Href lang';
+$string['fullpage_dlg:href'] = 'HREF';
+$string['fullpage_dlg:hreflang'] = 'HREF lang';
 $string['fullpage_dlg:info'] = 'Information';
 $string['fullpage_dlg:langcode'] = 'Language code';
 $string['fullpage_dlg:langdir'] = 'Language direction';
@@ -376,7 +437,7 @@ $string['fullpage_dlg:meta_index_follow'] = 'Index and follow the links';
 $string['fullpage_dlg:meta_index_nofollow'] = 'Index and don\'t follow the links';
 $string['fullpage_dlg:meta_keywords'] = 'Keywords';
 $string['fullpage_dlg:meta_noindex_follow'] = 'Do not index but follow the links';
-$string['fullpage_dlg:meta_noindex_nofollow'] = 'Do not index and don\\\'t follow the links';
+$string['fullpage_dlg:meta_noindex_nofollow'] = 'Do not index and don\'t follow the links';
 $string['fullpage_dlg:meta_props'] = 'Meta information';
 $string['fullpage_dlg:meta_robots'] = 'Robots';
 $string['fullpage_dlg:meta_tab'] = 'General';
@@ -391,7 +452,7 @@ $string['fullpage_dlg:rev'] = 'Rev';
 $string['fullpage_dlg:right_margin'] = 'Right margin';
 $string['fullpage_dlg:rtl'] = 'Right to left';
 $string['fullpage_dlg:script_element'] = 'Script element';
-$string['fullpage_dlg:src'] = 'Src';
+$string['fullpage_dlg:src'] = 'Source';
 $string['fullpage_dlg:style'] = 'Style';
 $string['fullpage_dlg:style_element'] = 'Style element';
 $string['fullpage_dlg:stylesheet'] = 'Stylesheet';
@@ -405,9 +466,9 @@ $string['fullpage_dlg:type'] = 'Type';
 $string['fullpage_dlg:value'] = 'Value';
 $string['fullpage_dlg:visited_color'] = 'Visited color';
 $string['fullpage_dlg:xml_pi'] = 'XML declaration';
-$string['fullscreen:desc'] = 'Toggle fullscreen mode';
+$string['fullscreen:desc'] = 'Toggle full screen mode';
 $string['iespell:download'] = 'ieSpell not detected. Do you want to install it now?';
-$string['iespell:iespell_desc'] = 'Run spell checking';
+$string['iespell:iespell_desc'] = 'Check spelling';
 $string['insertdatetime:date_fmt'] = '%Y-%m-%d';
 $string['insertdatetime:day_long'] = 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday';
 $string['insertdatetime:day_short'] = 'Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun';
@@ -423,7 +484,7 @@ $string['layer:forward_desc'] = 'Move forward';
 $string['layer:insertlayer_desc'] = 'Insert new layer';
 $string['media:delta_height'] = '';
 $string['media:delta_width'] = '';
-$string['media:desc'] = 'Insert / edit embedded media';
+$string['media:desc'] = 'Insert/edit embedded media';
 $string['media:edit'] = 'Edit embedded media';
 $string['media_dlg:advanced'] = 'Advanced';
 $string['media_dlg:align'] = 'Align';
@@ -436,15 +497,18 @@ $string['media_dlg:align_right'] = 'Right';
 $string['media_dlg:align_top'] = 'Top';
 $string['media_dlg:align_top_left'] = 'Top left';
 $string['media_dlg:align_top_right'] = 'Top right';
+$string['media_dlg:altsource1'] = 'Alternative source 1';
+$string['media_dlg:altsource2'] = 'Alternative source 2';
+$string['media_dlg:audio'] = 'HTML5 audio';
 $string['media_dlg:autogotourl'] = 'Auto goto URL';
-$string['media_dlg:autohref'] = 'AutoHREF';
+$string['media_dlg:autohref'] = 'Auto HREF';
 $string['media_dlg:autostart'] = 'Auto start';
 $string['media_dlg:balance'] = 'Balance';
 $string['media_dlg:base'] = 'Base';
 $string['media_dlg:baseurl'] = 'Base URL';
 $string['media_dlg:bgcolor'] = 'Background';
 $string['media_dlg:cache'] = 'Cache';
-$string['media_dlg:captioningid'] = 'Captioning id';
+$string['media_dlg:captioningid'] = 'Captioning ID';
 $string['media_dlg:center'] = 'Center';
 $string['media_dlg:class_name'] = 'Class';
 $string['media_dlg:console'] = 'Console';
@@ -455,30 +519,24 @@ $string['media_dlg:correction'] = 'No correction';
 $string['media_dlg:currentmarker'] = 'Current marker';
 $string['media_dlg:currentposition'] = 'Current position';
 $string['media_dlg:defaultframe'] = 'Default frame';
+$string['media_dlg:embedded_audio_options'] = 'Embedded audio options';
+$string['media_dlg:embeddedaudio'] = 'Embedded audio';
 $string['media_dlg:enabled'] = 'Enabled';
 $string['media_dlg:enablejavascript'] = 'Enable JavaScript';
 $string['media_dlg:endtime'] = 'End time';
 $string['media_dlg:file'] = 'File/URL';
+$string['media_dlg:flash'] = 'Flash';
 $string['media_dlg:flash_options'] = 'Flash options';
-$string['media_dlg:flashvars'] = 'Flashvars';
-$string['media_dlg:flv_autostart'] = 'Auto start';
-$string['media_dlg:flv_buffer'] = 'Buffer';
-$string['media_dlg:flv_defaultvolume'] = 'Default volumne';
-$string['media_dlg:flv_hiddengui'] = 'Hidden GUI';
-$string['media_dlg:flv_jscallback'] = 'JS Callback';
-$string['media_dlg:flv_loop'] = 'Loop';
-$string['media_dlg:flv_options'] = 'Flash video options';
-$string['media_dlg:flv_scalemode'] = 'Scale mode';
-$string['media_dlg:flv_showscalemodes'] = 'Show scale modes';
-$string['media_dlg:flv_smoothvideo'] = 'Smooth video';
-$string['media_dlg:flv_startimage'] = 'Start image';
-$string['media_dlg:flv_starttime'] = 'Start time';
-$string['media_dlg:fullscreen'] = 'Fullscreen';
+$string['media_dlg:flashvars'] = 'Flash vars';
+$string['media_dlg:fullscreen'] = 'Full screen';
 $string['media_dlg:general'] = 'General';
 $string['media_dlg:hidden'] = 'Hidden';
-$string['media_dlg:href'] = 'Href';
+$string['media_dlg:href'] = 'HREF';
 $string['media_dlg:hspace'] = 'H-Space';
-$string['media_dlg:id'] = 'Id';
+$string['media_dlg:html5_audio_options'] = 'Audio options';
+$string['media_dlg:html5_video_options'] = 'HTML5 video options';
+$string['media_dlg:id'] = 'ID';
+$string['media_dlg:iframe'] = 'Iframe';
 $string['media_dlg:imagestatus'] = 'Image status';
 $string['media_dlg:invokeurls'] = 'Invoke URLs';
 $string['media_dlg:kioskmode'] = 'Kiosk mode';
@@ -489,61 +547,72 @@ $string['media_dlg:maintainaspect'] = 'Maintain aspect';
 $string['media_dlg:menu'] = 'Show menu';
 $string['media_dlg:mute'] = 'Mute';
 $string['media_dlg:name'] = 'Name';
-$string['media_dlg:nojava'] = 'No java';
+$string['media_dlg:nojava'] = 'No Java';
 $string['media_dlg:numloop'] = 'Num loops';
 $string['media_dlg:play'] = 'Auto play';
 $string['media_dlg:playcount'] = 'Play count';
 $string['media_dlg:playeveryframe'] = 'Play every frame';
+$string['media_dlg:poster'] = 'Poster';
 $string['media_dlg:prefetch'] = 'Prefetch';
+$string['media_dlg:preload'] = 'Preload';
+$string['media_dlg:preload_auto'] = 'Let user\'s browser decide';
+$string['media_dlg:preload_metadata'] = 'Preload video metadata';
+$string['media_dlg:preload_none'] = 'Don\'t preload';
 $string['media_dlg:preview'] = 'Preview';
 $string['media_dlg:progress'] = 'Progress';
-$string['media_dlg:qt_options'] = 'Quicktime options';
-$string['media_dlg:qt_stream_warn'] = 'Streamed rtsp resources should be added to the QT Src field under the advanced tab.
-You should also add a non streamed version to the Src field..';
-$string['media_dlg:qtsrc'] = 'QT Src';
+$string['media_dlg:qt_options'] = 'QuickTime options';
+$string['media_dlg:qt_stream_warn'] = 'Streamed RTSP resources should be added to the QT source field under the advanced tab.
+You should also add a non-streamed version to the source field.';
+$string['media_dlg:qtsrc'] = 'QT source';
 $string['media_dlg:qtsrcchokespeed'] = 'Choke speed';
 $string['media_dlg:quality'] = 'Quality';
+$string['media_dlg:quicktime'] = 'QuickTime';
 $string['media_dlg:rate'] = 'Rate';
-$string['media_dlg:rmp_options'] = 'Real media player options';
+$string['media_dlg:realmedia'] = 'Real Media';
+$string['media_dlg:rmp_options'] = 'Real Media Player options';
 $string['media_dlg:salign'] = 'SAlign';
 $string['media_dlg:scale'] = 'Scale';
 $string['media_dlg:scriptcallbacks'] = 'Script callbacks';
+$string['media_dlg:shockwave'] = 'Shockwave';
 $string['media_dlg:shockwave_options'] = 'Shockwave options';
 $string['media_dlg:shuffle'] = 'Shuffle';
 $string['media_dlg:size'] = 'Dimensions';
 $string['media_dlg:sound'] = 'Sound';
+$string['media_dlg:source'] = 'Source';
 $string['media_dlg:starttime'] = 'Start time';
 $string['media_dlg:stretchtofit'] = 'Stretch to fit';
 $string['media_dlg:swstretchhalign'] = 'Stretch H-Align';
-$string['media_dlg:swstretchstyle'] = 'Stretch style';
+$string['media_dlg:swstretchstyle'] = 'Stretch Style';
 $string['media_dlg:swstretchvalign'] = 'Stretch V-Align';
 $string['media_dlg:target'] = 'Target';
 $string['media_dlg:targetcache'] = 'Target cache';
-$string['media_dlg:title'] = 'Insert / edit embedded media';
+$string['media_dlg:title'] = 'Insert/edit embedded media';
 $string['media_dlg:type'] = 'Type';
-$string['media_dlg:uimode'] = 'UI Mode';
+$string['media_dlg:uimode'] = 'UI mode';
+$string['media_dlg:video'] = 'HTML5 video';
 $string['media_dlg:volume'] = 'Volume';
 $string['media_dlg:vspace'] = 'V-Space';
 $string['media_dlg:windowlessvideo'] = 'Windowless video';
+$string['media_dlg:windowsmedia'] = 'Windows Media';
 $string['media_dlg:wmode'] = 'WMode';
-$string['media_dlg:wmp_options'] = 'Windows media player options';
+$string['media_dlg:wmp_options'] = 'Windows Media Player options';
 $string['nonbreaking:nonbreaking_desc'] = 'Insert non-breaking space character';
-$string['pagebreak:desc'] = 'Insert page break.';
-$string['paste:paste_text_desc'] = 'Paste as Plain Text';
+$string['pagebreak:desc'] = 'Insert page break for printing';
+$string['paste:paste_text_desc'] = 'Paste as plain Text';
 $string['paste:paste_word_desc'] = 'Paste from Word';
-$string['paste:plaintext_mode'] = 'Paste is now in plain text mode. Click again to toggle back to regular paste mode.';
-$string['paste:plaintext_mode_sticky'] = 'Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.';
+$string['paste:plaintext_mode'] = 'Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.';
+$string['paste:plaintext_mode_stick'] = 'Paste is now in plain text mode. Click again to toggle back to regular paste mode.';
 $string['paste:selectall_desc'] = 'Select All';
 $string['paste_dlg:text_linebreaks'] = 'Keep linebreaks';
-$string['paste_dlg:text_title'] = 'Use CTRL+V on your keyboard to paste the text into the window.';
-$string['paste_dlg:word_title'] = 'Use CTRL+V on your keyboard to paste the text into the window.';
+$string['paste_dlg:text_title'] = 'Use Ctrl+V on your keyboard to paste the text into the window.';
+$string['paste_dlg:word_title'] = 'Use Ctrl+V on your keyboard to paste the text into the window.';
 $string['preview:preview_desc'] = 'Preview';
 $string['print:print_desc'] = 'Print';
 $string['save:cancel_desc'] = 'Cancel all changes';
 $string['save:save_desc'] = 'Save';
 $string['searchreplace:delta_height'] = '';
 $string['searchreplace:delta_width'] = '';
-$string['searchreplace:replace_desc'] = 'Find/Replace';
+$string['searchreplace:replace_desc'] = 'Find/replace';
 $string['searchreplace:search_desc'] = 'Find';
 $string['searchreplace_dlg:allreplaced'] = 'All occurrences of the search string were replaced.';
 $string['searchreplace_dlg:direction'] = 'Direction';
@@ -553,34 +622,36 @@ $string['searchreplace_dlg:findwhat'] = 'Find what';
 $string['searchreplace_dlg:mcase'] = 'Match case';
 $string['searchreplace_dlg:notfound'] = 'The search has been completed. The search string could not be found.';
 $string['searchreplace_dlg:replace'] = 'Replace';
-$string['searchreplace_dlg:replace_title'] = 'Find/Replace';
+$string['searchreplace_dlg:replace_title'] = 'Find/replace';
 $string['searchreplace_dlg:replaceall'] = 'Replace all';
 $string['searchreplace_dlg:replacewith'] = 'Replace with';
 $string['searchreplace_dlg:search_title'] = 'Find';
 $string['searchreplace_dlg:searchnext_desc'] = 'Find again';
 $string['searchreplace_dlg:up'] = 'Up';
 $string['simple:bold_desc'] = 'Bold (Ctrl+B)';
-$string['simple:bullist_desc'] = 'Unordered list';
+$string['simple:bullist_desc'] = 'Insert/remove bulleted list';
 $string['simple:cleanup_desc'] = 'Cleanup messy code';
 $string['simple:italic_desc'] = 'Italic (Ctrl+I)';
-$string['simple:numlist_desc'] = 'Ordered list';
+$string['simple:numlist_desc'] = 'Insert/remove numbered list';
 $string['simple:redo_desc'] = 'Redo (Ctrl+Y)';
 $string['simple:striketrough_desc'] = 'Strikethrough';
 $string['simple:underline_desc'] = 'Underline (Ctrl+U)';
 $string['simple:undo_desc'] = 'Undo (Ctrl+Z)';
-$string['spellchecker:desc'] = 'Toggle spellchecker';
+$string['spellchecker:desc'] = 'Toggle spell checker';
 $string['spellchecker:ignore_word'] = 'Ignore word';
 $string['spellchecker:ignore_words'] = 'Ignore all';
 $string['spellchecker:langs'] = 'Languages';
-$string['spellchecker:menu'] = 'Spellchecker settings';
+$string['spellchecker:learn_word'] = 'Learn word';
+$string['spellchecker:menu'] = 'Spell checker settings';
 $string['spellchecker:no_mpell'] = 'No misspellings found.';
-$string['spellchecker:no_sug'] = 'No suggestions';
+$string['spellchecker:no_sug'] = 'No Suggestions';
 $string['spellchecker:sug'] = 'Suggestions';
 $string['spellchecker:wait'] = 'Please wait...';
 $string['style:delta_height'] = '';
 $string['style:delta_width'] = '';
-$string['style:desc'] = 'Edit CSS Style';
+$string['style:desc'] = 'Edit CSS style';
 $string['style_dlg:apply'] = 'Apply';
+$string['style_dlg:background'] = 'Background';
 $string['style_dlg:background_attachment'] = 'Attachment';
 $string['style_dlg:background_color'] = 'Background color';
 $string['style_dlg:background_hpos'] = 'Horizontal position';
@@ -588,6 +659,7 @@ $string['style_dlg:background_image'] = 'Background image';
 $string['style_dlg:background_repeat'] = 'Repeat';
 $string['style_dlg:background_tab'] = 'Background';
 $string['style_dlg:background_vpos'] = 'Vertical position';
+$string['style_dlg:block'] = 'Block';
 $string['style_dlg:block_display'] = 'Display';
 $string['style_dlg:block_letterspacing'] = 'Letter spacing';
 $string['style_dlg:block_tab'] = 'Block';
@@ -596,8 +668,10 @@ $string['style_dlg:block_text_indent'] = 'Text indent';
 $string['style_dlg:block_vertical_alignment'] = 'Vertical alignment';
 $string['style_dlg:block_whitespace'] = 'Whitespace';
 $string['style_dlg:block_wordspacing'] = 'Word spacing';
+$string['style_dlg:border'] = 'Border';
 $string['style_dlg:border_tab'] = 'Border';
 $string['style_dlg:bottom'] = 'Bottom';
+$string['style_dlg:box'] = 'Box';
 $string['style_dlg:box_clear'] = 'Clear';
 $string['style_dlg:box_float'] = 'Float';
 $string['style_dlg:box_height'] = 'Height';
@@ -608,6 +682,7 @@ $string['style_dlg:clip'] = 'Clip';
 $string['style_dlg:color'] = 'Color';
 $string['style_dlg:height'] = 'Height';
 $string['style_dlg:left'] = 'Left';
+$string['style_dlg:list'] = 'List';
 $string['style_dlg:list_tab'] = 'List';
 $string['style_dlg:list_type'] = 'Type';
 $string['style_dlg:margin'] = 'Margin';
@@ -620,23 +695,25 @@ $string['style_dlg:positioning_type'] = 'Type';
 $string['style_dlg:right'] = 'Right';
 $string['style_dlg:same'] = 'Same for all';
 $string['style_dlg:style'] = 'Style';
-$string['style_dlg:text_blink'] = 'blink';
+$string['style_dlg:text'] = 'Text';
+$string['style_dlg:text_blink'] = 'Blink';
 $string['style_dlg:text_case'] = 'Case';
 $string['style_dlg:text_color'] = 'Color';
 $string['style_dlg:text_decoration'] = 'Decoration';
 $string['style_dlg:text_font'] = 'Font';
 $string['style_dlg:text_lineheight'] = 'Line height';
-$string['style_dlg:text_none'] = 'none';
-$string['style_dlg:text_overline'] = 'overline';
+$string['style_dlg:text_none'] = 'None';
+$string['style_dlg:text_overline'] = 'Overline';
 $string['style_dlg:text_props'] = 'Text';
 $string['style_dlg:text_size'] = 'Size';
-$string['style_dlg:text_striketrough'] = 'strikethrough';
+$string['style_dlg:text_striketrough'] = 'Strikethrough';
 $string['style_dlg:text_style'] = 'Style';
 $string['style_dlg:text_tab'] = 'Text';
-$string['style_dlg:text_underline'] = 'underline';
+$string['style_dlg:text_underline'] = 'Underline';
 $string['style_dlg:text_variant'] = 'Variant';
 $string['style_dlg:text_weight'] = 'Weight';
-$string['style_dlg:title'] = 'Edit CSS Style';
+$string['style_dlg:title'] = 'Edit CSS style';
+$string['style_dlg:toggle_insert_span'] = 'Insert span at selection';
 $string['style_dlg:top'] = 'Top';
 $string['style_dlg:visibility'] = 'Visibility';
 $string['style_dlg:width'] = 'Width';
@@ -650,10 +727,10 @@ $string['table:col_after_desc'] = 'Insert column after';
 $string['table:col_before_desc'] = 'Insert column before';
 $string['table:copy_row_desc'] = 'Copy table row';
 $string['table:cut_row_desc'] = 'Cut table row';
-$string['table:del'] = 'Delete table';
-$string['table:delete_col_desc'] = 'Remove column';
+$string['table:del'] = 'Delete Table';
+$string['table:delete_col_desc'] = 'Delete column';
 $string['table:delete_row_desc'] = 'Delete row';
-$string['table:desc'] = 'Inserts a new table';
+$string['table:desc'] = 'Insert/edit table';
 $string['table:merge_cells_delta_height'] = '';
 $string['table:merge_cells_delta_width'] = '';
 $string['table:merge_cells_desc'] = 'Merge table cells';
@@ -685,15 +762,16 @@ $string['table_dlg:bordercolor'] = 'Border color';
 $string['table_dlg:caption'] = 'Table caption';
 $string['table_dlg:cell_all'] = 'Update all cells in table';
 $string['table_dlg:cell_cell'] = 'Update current cell';
+$string['table_dlg:cell_col'] = 'Update all cells in column';
 $string['table_dlg:cell_limit'] = 'You\'ve exceeded the maximum number of cells of {$cells}.';
 $string['table_dlg:cell_row'] = 'Update all cells in row';
 $string['table_dlg:cell_title'] = 'Table cell properties';
 $string['table_dlg:cell_type'] = 'Cell type';
-$string['table_dlg:cellpadding'] = 'Cellpadding';
-$string['table_dlg:cellspacing'] = 'Cellspacing';
+$string['table_dlg:cellpadding'] = 'Cell padding';
+$string['table_dlg:cellspacing'] = 'Cell spacing';
 $string['table_dlg:col_limit'] = 'You\'ve exceeded the maximum number of columns of {$cols}.';
-$string['table_dlg:colgroup'] = 'Col Group';
-$string['table_dlg:cols'] = 'Cols';
+$string['table_dlg:colgroup'] = 'Col group';
+$string['table_dlg:cols'] = 'Columns';
 $string['table_dlg:frame'] = 'Frame';
 $string['table_dlg:frame_all'] = 'all';
 $string['table_dlg:frame_cols'] = 'cols';
@@ -703,7 +781,7 @@ $string['table_dlg:frame_rows'] = 'rows';
 $string['table_dlg:general_props'] = 'General properties';
 $string['table_dlg:general_tab'] = 'General';
 $string['table_dlg:height'] = 'Height';
-$string['table_dlg:id'] = 'Id';
+$string['table_dlg:id'] = 'ID';
 $string['table_dlg:langcode'] = 'Language code';
 $string['table_dlg:langdir'] = 'Language direction';
 $string['table_dlg:ltr'] = 'Left to right';
@@ -716,9 +794,9 @@ $string['table_dlg:row_limit'] = 'You\'ve exceeded the maximum number of rows of
 $string['table_dlg:row_odd'] = 'Update odd rows in table';
 $string['table_dlg:row_row'] = 'Update current row';
 $string['table_dlg:row_title'] = 'Table row properties';
-$string['table_dlg:rowgroup'] = 'Row Group';
+$string['table_dlg:rowgroup'] = 'Row group';
 $string['table_dlg:rows'] = 'Rows';
-$string['table_dlg:rowtype'] = 'Row in table part';
+$string['table_dlg:rowtype'] = 'Row type';
 $string['table_dlg:rtl'] = 'Right to left';
 $string['table_dlg:rules'] = 'Rules';
 $string['table_dlg:rules_above'] = 'above';
@@ -733,12 +811,12 @@ $string['table_dlg:rules_vsides'] = 'vsides';
 $string['table_dlg:scope'] = 'Scope';
 $string['table_dlg:style'] = 'Style';
 $string['table_dlg:summary'] = 'Summary';
-$string['table_dlg:tbody'] = 'Table Body';
+$string['table_dlg:tbody'] = 'Body';
 $string['table_dlg:td'] = 'Data';
-$string['table_dlg:tfoot'] = 'Table Foot';
+$string['table_dlg:tfoot'] = 'Footer';
 $string['table_dlg:th'] = 'Header';
-$string['table_dlg:thead'] = 'Table Head';
-$string['table_dlg:title'] = 'Insert/Modify table';
+$string['table_dlg:thead'] = 'Header';
+$string['table_dlg:title'] = 'Insert/edit table';
 $string['table_dlg:valign'] = 'Vertical alignment';
 $string['table_dlg:width'] = 'Width';
 $string['template:desc'] = 'Insert predefined template content';
@@ -755,7 +833,9 @@ $string['template_dlg:preview'] = 'Preview';
 $string['template_dlg:select'] = 'Select a template';
 $string['template_dlg:title'] = 'Templates';
 $string['template_dlg:warning'] = 'Warning: Updating a template with a different one may cause data loss.';
-$string['visualchars:desc'] = 'Visual control characters on/off.';
+$string['visualblocks:desc'] = 'Show/hide block elements';
+$string['visualchars:desc'] = 'Show/hide visual control characters';
+$string['wordcount:words'] = 'Words:';
 $string['xhtmlxtras:abbr_delta_height'] = '';
 $string['xhtmlxtras:abbr_delta_width'] = '';
 $string['xhtmlxtras:abbr_desc'] = 'Abbreviation';
@@ -764,7 +844,7 @@ $string['xhtmlxtras:acronym_delta_width'] = '';
 $string['xhtmlxtras:acronym_desc'] = 'Acronym';
 $string['xhtmlxtras:attribs_delta_height'] = '';
 $string['xhtmlxtras:attribs_delta_width'] = '';
-$string['xhtmlxtras:attribs_desc'] = 'Insert/Edit Attributes';
+$string['xhtmlxtras:attribs_desc'] = 'Insert/edit attributes';
 $string['xhtmlxtras:cite_delta_height'] = '';
 $string['xhtmlxtras:cite_delta_width'] = '';
 $string['xhtmlxtras:cite_desc'] = 'Citation';
@@ -775,32 +855,39 @@ $string['xhtmlxtras:ins_delta_height'] = '';
 $string['xhtmlxtras:ins_delta_width'] = '';
 $string['xhtmlxtras:ins_desc'] = 'Insertion';
 $string['xhtmlxtras_dlg:attrib_tab'] = 'Attributes';
-$string['xhtmlxtras_dlg:attribs_title'] = 'Insert/Edit Attributes';
+$string['xhtmlxtras_dlg:attribs_title'] = 'Insert/edit attributes';
 $string['xhtmlxtras_dlg:attribute_attrib_tab'] = 'Attributes';
 $string['xhtmlxtras_dlg:attribute_events_tab'] = 'Events';
 $string['xhtmlxtras_dlg:attribute_label_accesskey'] = 'AccessKey';
 $string['xhtmlxtras_dlg:attribute_label_cite'] = 'Cite';
 $string['xhtmlxtras_dlg:attribute_label_class'] = 'Class';
-$string['xhtmlxtras_dlg:attribute_label_datetime'] = 'Date/Time';
+$string['xhtmlxtras_dlg:attribute_label_datetime'] = 'Date/time';
 $string['xhtmlxtras_dlg:attribute_label_id'] = 'ID';
 $string['xhtmlxtras_dlg:attribute_label_langcode'] = 'Language';
-$string['xhtmlxtras_dlg:attribute_label_langdir'] = 'Text Direction';
+$string['xhtmlxtras_dlg:attribute_label_langdir'] = 'Text direction';
 $string['xhtmlxtras_dlg:attribute_label_style'] = 'Style';
 $string['xhtmlxtras_dlg:attribute_label_tabindex'] = 'TabIndex';
 $string['xhtmlxtras_dlg:attribute_label_title'] = 'Title';
 $string['xhtmlxtras_dlg:attribute_option_ltr'] = 'Left to right';
 $string['xhtmlxtras_dlg:attribute_option_rtl'] = 'Right to left';
 $string['xhtmlxtras_dlg:events_tab'] = 'Events';
-$string['xhtmlxtras_dlg:fieldset_attrib_tab'] = 'Element Attributes';
-$string['xhtmlxtras_dlg:fieldset_events_tab'] = 'Element Events';
-$string['xhtmlxtras_dlg:fieldset_general_tab'] = 'General Settings';
+$string['xhtmlxtras_dlg:fieldset_attrib_tab'] = 'Element attributes';
+$string['xhtmlxtras_dlg:fieldset_events_tab'] = 'Element events';
+$string['xhtmlxtras_dlg:fieldset_general_tab'] = 'General settings';
 $string['xhtmlxtras_dlg:general_tab'] = 'General';
 $string['xhtmlxtras_dlg:insert_date'] = 'Insert current date/time';
 $string['xhtmlxtras_dlg:option_ltr'] = 'Left to right';
 $string['xhtmlxtras_dlg:option_rtl'] = 'Right to left';
 $string['xhtmlxtras_dlg:remove'] = 'Remove';
-$string['xhtmlxtras_dlg:title_abbr_element'] = 'Abbreviation Element';
-$string['xhtmlxtras_dlg:title_acronym_element'] = 'Acronym Element';
-$string['xhtmlxtras_dlg:title_cite_element'] = 'Citation Element';
-$string['xhtmlxtras_dlg:title_del_element'] = 'Deletion Element';
-$string['xhtmlxtras_dlg:title_ins_element'] = 'Insertion Element';
+$string['xhtmlxtras_dlg:title_abbr_element'] = 'Abbreviation element';
+$string['xhtmlxtras_dlg:title_acronym_element'] = 'Acronym element';
+$string['xhtmlxtras_dlg:title_cite_element'] = 'Citation element';
+$string['xhtmlxtras_dlg:title_del_element'] = 'Deletion element';
+$string['xhtmlxtras_dlg:title_ins_element'] = 'Insertion element';
+
+
+// == Our modifications or upstream changes ==
+$string['advanced:copy_desc'] = 'Copy';
+$string['advanced:cut_desc'] = 'Cut';
+$string['advanced:paste_desc'] = 'Paste';
+$string['advanced:shortcuts_desc'] = 'Accessibility help';
index ae784d3..db9843b 100644 (file)
@@ -96,10 +96,11 @@ class tinymce_texteditor extends texteditor {
      */
     public function use_editor($elementid, array $options=null, $fpoptions=null) {
         global $PAGE;
+        // Note: use full moodle_url instance to prevent standard JS loader.
         if (debugging('', DEBUG_DEVELOPER)) {
-            $PAGE->requires->js('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce_src.js');
+            $PAGE->requires->js(new moodle_url('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce_src.js'));
         } else {
-            $PAGE->requires->js('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce.js');
+            $PAGE->requires->js(new moodle_url('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce.js'));
         }
         $PAGE->requires->js_init_call('M.editor_tinymce.init_editor', array($elementid, $this->get_init_params($elementid, $options)), true);
         if ($fpoptions) {
@@ -128,6 +129,11 @@ class tinymce_texteditor extends texteditor {
 
         $fontselectlist = empty($config->fontselectlist) ? '' : $config->fontselectlist;
 
+        $langrev = -1;
+        if (!empty($CFG->cachejs)) {
+            $langrev = get_string_manager()->get_revision();
+        }
+
         $params = array(
             'moodle_config' => $config,
             'mode' => "exact",
@@ -160,6 +166,8 @@ class tinymce_texteditor extends texteditor {
             'min_height' => 30,
             'theme_advanced_toolbar_location' => "top",
             'theme_advanced_statusbar_location' => "bottom",
+            'language_load' => false, // We load all lang strings directly from Moodle.
+            'langrev' => $langrev,
         );
 
         // Should we override the default toolbar layout unconditionally?
index ba04498..eedc3e1 100644 (file)
@@ -25,8 +25,26 @@ M.editor_tinymce = M.editor_tinymce || {};
 
 M.editor_tinymce.editor_options = M.editor_tinymce.options || {};
 M.editor_tinymce.filepicker_options = M.editor_tinymce.filepicker_options || {};
+M.editor_tinymce.initialised = false;
 
 M.editor_tinymce.init_editor = function(Y, editorid, options) {
+
+    if (!M.editor_tinymce.initialised) {
+        // Load all language strings for all plugins - we do not use standard TinyMCE lang pack loading!
+        tinymce.ScriptLoader.add(M.cfg.wwwroot + '/lib/editor/tinymce/all_strings.php?elanguage=' + options.language + '&rev=' + options.langrev);
+
+        // Monkey patch for MDL-35284 - this hack ignores empty toolbars.
+        tinymce.ui.Toolbar.prototype.oldRenderHTML = tinymce.ui.Toolbar.prototype.renderHTML;
+        tinymce.ui.Toolbar.prototype.renderHTML = function() {
+            if (this.controls.length == 0) {
+                return;
+            }
+            return tinymce.ui.Toolbar.prototype.oldRenderHTML.call(this);
+        };
+
+        M.editor_tinymce.initialised = true;
+    }
+
     M.editor_tinymce.editor_options[editorid] = options;
 
     // Load necessary Moodle plugins into editor.
index 117cd2a..b312b04 100644 (file)
@@ -24,4 +24,5 @@
 
 $string['pluginname'] = 'Insert image';
 
-/* This plugin abuses strings from the standard TinyMCE advimage plugin, there is no need to duplicate them here. */
\ No newline at end of file
+/* This plugin abuses strings from the standard TinyMCE advimage plugin, there is no need to duplicate them here. */
+$string['moodleimage:browseimage'] = 'Find or upload an image...';
index 7d5d6f2..7105228 100644 (file)
@@ -26,7 +26,7 @@
                 return "";\r
 \r
             html = '<a class="moodlebutton" id="' + id + '_link" href="javascript:openBrowser(\'' + id + '\',\'' + target_form_element + '\', \'' + type + '\',\'' + option + '\');" onmousedown="return false;">';\r
-            html += '<span id="' + id + '">' + tinyMCEPopup.getLang('browseimage') + '</span>';\r
+            html += '<span id="' + id + '">' + tinyMCEPopup.getLang('moodleimage.browseimage') + '</span>';\r
             html += '</a>';\r
 \r
             return html;\r
index 256432c..c70193e 100644 (file)
@@ -26,4 +26,5 @@ $string['nopreview'] = 'Can not preview media.';
 $string['pluginname'] = 'Insert media';
 
 /* All lang strings used from TinyMCE JavaScript code must be named 'pluginname:stringname', no need to create langs/en_dlg.js */
+$string['moodlemedia:browsemedia'] = 'Find or upload a sound, video or applet...';
 $string['moodlemedia:desc'] = 'Insert Moodle media';
index 37cf1b2..fdcce90 100644 (file)
@@ -25,7 +25,7 @@
                 return "";
 
             html = '<a class="moodlebutton" id="' + id + '_link" href="javascript:openBrowser(\'' + id + '\',\'' + target_form_element + '\', \'' + type + '\',\'' + option + '\');" onmousedown="return false;">';
-            html += '<span id="' + id + '">' + tinyMCEPopup.getLang('browsemedia') + '</span>';
+            html += '<span id="' + id + '">' + tinyMCEPopup.getLang('moodlemedia.browsemedia') + '</span>';
             html += '</a>';
 
             return html;
index 6205cb5..06ec94a 100644 (file)
@@ -7,39 +7,14 @@ License: GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999
 Moodle maintainer: Petr Skoda (skodak)
 
 =========================================================================================
-Upgrade procedure (by maintainer):
+Upgrade procedure:
 
- 1/ clone https://github.com/moodle/custom-tinymce
- 2/ cherry pick Moodle changes into new branches based on stable upstream
- 3/ tweak paths in build script in moodle_build.sh and execute
- 4/ fix line endings
- 5/ download all TinyMCE lang files (extra/tools/download_langs.sh)
- 6/ make sure your moodle installation has all language packs installed.
- 7/ update moodle lang string files (extra/tools/update_lang_files.php)
- 8/ ensure lang packs are updated into AMOS (lang.moodle.net)
+1/ extract standard TinyMCE package into lib/editor/tinymce/tiny_mce/x.y.z/
+2/ bump up editor version in lib.php to match the directory name x.y.z
+3/ bump up main version.php
+4/ update lib/thirdpartylibs.xml
+5/ execute cli/update_lang_files.php and review changes in lang/en/editor_tinymce.php
 
-=========================================================================================
-Prepare local modification procedure (by developer):
-
- 1/ clone https://github.com/moodle/custom-tinymce
- 2/ apply local modifications to the STABLE branches in those two repos
- 3/ tweak paths in build script in moodle_build.sh and execute
- 4/ fix line endings
- 5/ provide 2 patches into the corresponding MDL issue:
-    a) one patch with the version to be applied to custom-tinymce (one for each target branch)
-    b) another patch with the resulting changes to be applied to moodle.git (one for each target branch)
- 6/ then integrator will:
-    a) apply patches in 5a/ to custom-tinymce repo
-    b/ standard integration of patches in 5b/ to moodle.git (review, test, upstream)
- 7/ Done!
-
-Note1: if the local modification includes lang changes, then steps 5-7 (from upgrade) may be necessary.
-Contact AMOS maintainer / custom-tinymce maintainer / integrators about that.
-
-=========================================================================================
-
-Modified:
- * string processing - uses our lang framework instead of js files
 
 TODO:
- * update strings to integrate with AMOS
+ * create some new automated script that sends other languages from upstream into AMOS
diff --git a/lib/editor/tinymce/tiny_mce/3.6.0/jquery.tinymce.js b/lib/editor/tinymce/tiny_mce/3.6.0/jquery.tinymce.js
deleted file mode 100644 (file)
index b4d0c39..0000000
+++ /dev/null
@@ -1 +0,0 @@
-(function(c){var b,e,a=[],d=window;c.fn.tinymce=function(j){var p=this,g,k,h,m,i,l="",n="";if(!p.length){return p}if(!j){return tinyMCE.get(p[0].id)}p.css("visibility","hidden");function o(){var r=[],q=0;if(f){f();f=null}p.each(function(t,u){var s,w=u.id,v=j.oninit;if(!w){u.id=w=tinymce.DOM.uniqueId()}s=new tinymce.Editor(w,j);r.push(s);s.onInit.add(function(){var x,y=v;p.css("visibility","");if(v){if(++q==r.length){if(tinymce.is(y,"string")){x=(y.indexOf(".")===-1)?null:tinymce.resolve(y.replace(/\.\w+$/,""));y=tinymce.resolve(y)}y.apply(x||tinymce,r)}}})});c.each(r,function(t,s){s.render()})}if(!d.tinymce&&!e&&(g=j.script_url)){e=1;h=g.substring(0,g.lastIndexOf("/"));if(/_(src|dev)\.js/g.test(g)){n="_src"}m=g.lastIndexOf("?");if(m!=-1){l=g.substring(m+1)}d.tinyMCEPreInit=d.tinyMCEPreInit||{base:h,suffix:n,query:l};if(g.indexOf("gzip")!=-1){i=j.language||"en";g=g+(/\?/.test(g)?"&":"?")+"js=true&core=true&suffix="+escape(n)+"&themes="+escape(j.theme)+"&plugins="+escape(j.plugins)+"&languages="+i;if(!d.tinyMCE_GZ){tinyMCE_GZ={start:function(){tinymce.suffix=n;function q(r){tinymce.ScriptLoader.markDone(tinyMCE.baseURI.toAbsolute(r))}q("langs/"+i+".js");q("themes/"+j.theme+"/editor_template"+n+".js");q("themes/"+j.theme+"/langs/"+i+".js");c.each(j.plugins.split(","),function(s,r){if(r){q("plugins/"+r+"/editor_plugin"+n+".js");q("plugins/"+r+"/langs/"+i+".js")}})},end:function(){}}}}c.ajax({type:"GET",url:g,dataType:"script",cache:true,success:function(){tinymce.dom.Event.domLoaded=1;e=2;if(j.script_loaded){j.script_loaded()}o();c.each(a,function(q,r){r()})}})}else{if(e===1){a.push(o)}else{o()}}return p};c.extend(c.expr[":"],{tinymce:function(g){return !!(g.id&&"tinyMCE" in window&&tinyMCE.get(g.id))}});function f(){function i(l){if(l==="remove"){this.each(function(n,o){var m=h(o);if(m){m.remove()}})}this.find("span.mceEditor,div.mceEditor").each(function(n,o){var m=tinyMCE.get(o.id.replace(/_parent$/,""));if(m){m.remove()}})}function k(n){var m=this,l;if(n!==b){i.call(m);m.each(function(p,q){var o;if(o=tinyMCE.get(q.id)){o.setContent(n)}})}else{if(m.length>0){if(l=tinyMCE.get(m[0].id)){return l.getContent()}}}}function h(m){var l=null;(m)&&(m.id)&&(d.tinymce)&&(l=tinyMCE.get(m.id));return l}function g(l){return !!((l)&&(l.length)&&(d.tinymce)&&(l.is(":tinymce")))}var j={};c.each(["text","html","val"],function(n,l){var o=j[l]=c.fn[l],m=(l==="text");c.fn[l]=function(s){var p=this;if(!g(p)){return o.apply(p,arguments)}if(s!==b){k.call(p.filter(":tinymce"),s);o.apply(p.not(":tinymce"),arguments);return p}else{var r="";var q=arguments;(m?p:p.eq(0)).each(function(u,v){var t=h(v);r+=t?(m?t.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):t.getContent({save:true})):o.apply(c(v),q)});return r}}});c.each(["append","prepend"],function(n,m){var o=j[m]=c.fn[m],l=(m==="prepend");c.fn[m]=function(q){var p=this;if(!g(p)){return o.apply(p,arguments)}if(q!==b){p.filter(":tinymce").each(function(s,t){var r=h(t);r&&r.setContent(l?q+r.getContent():r.getContent()+q)});o.apply(p.not(":tinymce"),arguments);return p}}});c.each(["remove","replaceWith","replaceAll","empty"],function(m,l){var n=j[l]=c.fn[l];c.fn[l]=function(){i.call(this,l);return n.apply(this,arguments)}});j.attr=c.fn.attr;c.fn.attr=function(o,q){var m=this,n=arguments;if((!o)||(o!=="value")||(!g(m))){if(q!==b){return j.attr.apply(m,n)}else{return j.attr.apply(m,n)}}if(q!==b){k.call(m.filter(":tinymce"),q);j.attr.apply(m.not(":tinymce"),n);return m}else{var p=m[0],l=h(p);return l?l.getContent({save:true}):j.attr.apply(c(p),n)}}}})(jQuery);
\ No newline at end of file
index 1837b0a..60d6d4c 100644 (file)
-                 GNU LESSER GENERAL PUBLIC LICENSE
-                      Version 2.1, February 1999
-
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-[This is the first released version of the Lesser GPL.  It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
-                           Preamble
-
-  The licenses for most software are designed to take away your
-freedom to share and change it.  By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
-  This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it.  You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
-  When we speak of free software, we are referring to freedom of use,
-not price.  Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
-  To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights.  These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
-  For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you.  You must make sure that they, too, receive or can get the source
-code.  If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it.  And you must show them these terms so they know their rights.
-
-  We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
-  To protect each distributor, we want to make it very clear that
-there is no warranty for the free library.  Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
-  Finally, software patents pose a constant threat to the existence of
-any free program.  We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder.  Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
-  Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License.  This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License.  We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
-  When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library.  The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom.  The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
-  We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License.  It also provides other free software developers Less
-of an advantage over competing non-free programs.  These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries.  However, the Lesser license provides advantages in certain
-special circumstances.
-
-  For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard.  To achieve this, non-free programs must be
-allowed to use the library.  A more frequent case is that a free
-library does the same job as widely used non-free libraries.  In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
-  In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software.  For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
-  Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.  Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library".  The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
-                 GNU LESSER GENERAL PUBLIC LICENSE
-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-  0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
-  A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
-  The "Library", below, refers to any such software library or work
-which has been distributed under these terms.  A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language.  (Hereinafter, translation is
-incl