Merge branch 'MDL-42592_master' of https://github.com/markn86/moodle
authorDan Poltawski <dan@moodle.com>
Mon, 4 Nov 2013 14:27:49 +0000 (22:27 +0800)
committerDan Poltawski <dan@moodle.com>
Mon, 4 Nov 2013 14:27:49 +0000 (22:27 +0800)
530 files changed:
admin/tool/behat/tests/behat/basic_actions.feature
admin/tool/generator/classes/course_backend.php
admin/tool/installaddon/classes/installer.php
admin/tool/installaddon/tests/installer_test.php
admin/tool/installaddon/tests/validator_test.php
admin/tool/uploaduser/index.php
admin/tool/uploaduser/user_form.php
backup/moodle2/backup_stepslib.php
backup/util/dbops/backup_controller_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
badges/tests/badgeslib_test.php
badges/tests/behat/award_badge.feature
blocks/community/communitycourse.php
blocks/community/locallib.php
blocks/html/lib.php
blocks/settings/styles.css
blog/locallib.php
calendar/classes/type_base.php
calendar/tests/calendartype_test_example.php
calendar/type/gregorian/classes/structure.php
composer.json
config-dist.php
course/classes/management/helper.php
course/classes/management_renderer.php
course/dnduploadlib.php
course/lib.php
course/management.php
course/modedit.php
course/modlib.php
course/renderer.php
course/tests/behat/behat_course.php
course/tests/behat/category_management.feature
course/tests/behat/category_resort.feature
course/tests/behat/course_category_management_listing.feature
course/yui/toolboxes/toolboxes.js
file.php
files/renderer.php
files/tests/behat/course_files.feature
filter/glossary/filter.php
filter/glossary/tests/filter_test.php [new file with mode: 0644]
grade/edit/outcome/import.php
grade/edit/outcome/import_outcomes_form.php
install/lang/ckb/moodle.php [moved from theme/mymobile/version.php with 53% similarity]
install/lang/nl/install.php
lang/en/cache.php
lang/en/moodle.php
lib/classes/event/blog_entry_created.php
lib/classes/event/blog_entry_deleted.php
lib/classes/event/blog_entry_updated.php
lib/classes/event/content_viewed.php
lib/classes/event/course_completed.php
lib/classes/event/course_completion_updated.php
lib/classes/event/course_module_completion_updated.php
lib/classes/event/course_module_created.php
lib/classes/event/course_module_updated.php
lib/classes/event/role_assigned.php
lib/classes/event/role_unassigned.php
lib/classes/event/user_deleted.php
lib/classes/event/user_enrolment_created.php
lib/classes/event/user_enrolment_deleted.php
lib/classes/event/user_enrolment_updated.php
lib/classes/plugin_manager.php
lib/classes/plugininfo/orphaned.php [new file with mode: 0644]
lib/conditionlib.php
lib/coursecatlib.php
lib/db/caches.php
lib/db/events.php
lib/db/upgrade.php
lib/editor/tinymce/db/upgrade.php
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/editor/tinymce/plugins/dragmath/dragmath.php
lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.png
lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.svg [new file with mode: 0644]
lib/editor/tinymce/plugins/pdw/lib.php
lib/editor/tinymce/plugins/pdw/version.php
lib/editor/tinymce/plugins/wrap/tinymce/editor_plugin.js
lib/editor/tinymce/settings.php
lib/editor/tinymce/styles.css
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/attributes.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/attributes.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bold.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bold.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bullet_list.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/bullet_list.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/button_bg.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cancel.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cancel.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cleanup_messy_code.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/cleanup_messy_code.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/clear_formatting.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/clear_formatting.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/decrease_indent.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/decrease_indent.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/delete.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/document_properties.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/document_properties.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/emoticons.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/emoticons.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/find_replace.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/find_replace.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/help.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/help.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/increase_indent.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/increase_indent.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_image.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_image.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_video.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_edit_video.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_nonbreaking_space.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_nonbreaking_space.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_time.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/insert_time.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/italic.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/italic.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_over.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_over.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_under.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/layers_under.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/left_to_right.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/left_to_right.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/merge_cells.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/numbered_list.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/numbered_list.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/page_break.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/page_break.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/paste.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/question.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/question.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/resize.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/resize.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/right_to_left.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/right_to_left.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/save.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/save.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/show_invisible_characters.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/show_invisible_characters.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/spellcheck.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/split_cells.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/strikethrough.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/strikethrough.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/styleprops.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/styleprops.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/subscript.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/subscript.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/superscript.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/superscript.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/table.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/template.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/template.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color_picker.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_color_picker.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight_picker.png [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/text_highlight_picker.svg [new file with mode: 0644]
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/underline.png
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/underline.svg
lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/ui.css
lib/editor/tinymce/version.php
lib/filelib.php
lib/filterlib.php
lib/form/dateselector.php
lib/form/datetimeselector.php
lib/form/searchableselector.php
lib/form/yui/dateselector/dateselector.js
lib/grade/grade_grade.php
lib/moodlelib.php
lib/outputcomponents.php
lib/outputrenderers.php
lib/portfolio/exporter.php
lib/setup.php
lib/testing/generator/module_generator.php
lib/testing/tests/generator_test.php
lib/tests/accesslib_test.php
lib/tests/behat/behat_general.php
lib/tests/behat/behat_hooks.php
lib/tests/behat/behat_transformations.php
lib/tests/completionlib_test.php
lib/tests/conditionlib_test.php
lib/tests/eventslib_test.php
lib/tests/filterlib_test.php
lib/tests/modinfolib_test.php
lib/timezone.txt
lib/upgrade.txt
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js
lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js
lib/yui/src/actionmenu/js/actionmenu.js
message/lib.php
message/tests/behat/display_history.feature [new file with mode: 0644]
mod/assign/feedback/editpdf/classes/pdf.php
mod/assign/feedback/editpdf/styles.css
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-debug.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor-min.js
mod/assign/feedback/editpdf/yui/build/moodle-assignfeedback_editpdf-editor/moodle-assignfeedback_editpdf-editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotation.js
mod/assign/feedback/editpdf/yui/src/editor/js/annotationstamp.js
mod/assign/feedback/editpdf/yui/src/editor/js/colourpicker.js
mod/assign/feedback/editpdf/yui/src/editor/js/comment.js
mod/assign/feedback/editpdf/yui/src/editor/js/commentmenu.js
mod/assign/feedback/editpdf/yui/src/editor/js/commentsearch.js
mod/assign/feedback/editpdf/yui/src/editor/js/dropdown.js
mod/assign/feedback/editpdf/yui/src/editor/js/editor.js
mod/assign/feedback/editpdf/yui/src/editor/js/stamppicker.js
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/assign/tests/generator/lib.php
mod/assign/tests/locallib_test.php
mod/assignment/tests/generator/lib.php
mod/assignment/type/online/assignment.class.php
mod/book/classes/event/course_module_viewed.php
mod/book/lib.php
mod/book/tests/generator/lib.php
mod/book/tests/generator_test.php
mod/chat/tests/generator/lib.php
mod/choice/classes/event/course_module_viewed.php
mod/choice/tests/generator/lib.php
mod/data/field/picture/field.class.php
mod/data/tests/generator/lib.php
mod/feedback/item/multichoice/lib.php
mod/feedback/item/multichoicerated/lib.php
mod/feedback/styles.css
mod/feedback/tests/generator/lib.php
mod/folder/lib.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/rsslib.php
mod/forum/tests/generator/lib.php
mod/forum/tests/generator_test.php
mod/glossary/rsslib.php
mod/glossary/tests/behat/entries_require_approval.feature [new file with mode: 0644]
mod/glossary/tests/generator/lib.php [new file with mode: 0644]
mod/glossary/tests/generator_test.php [new file with mode: 0644]
mod/imscp/lib.php
mod/label/tests/generator/lib.php
mod/lesson/locallib.php
mod/lesson/mod_form.php
mod/lesson/renderer.php
mod/lesson/report.php
mod/lesson/tests/generator/lib.php [new file with mode: 0644]
mod/lesson/tests/generator_test.php [new file with mode: 0644]
mod/lesson/timer.js
mod/lesson/view.php
mod/lti/tests/generator/lib.php
mod/page/classes/event/course_module_viewed.php
mod/page/lib.php
mod/page/tests/generator/lib.php
mod/quiz/locallib.php
mod/quiz/report/responses/responses_table.php
mod/quiz/tests/generator/lib.php
mod/resource/lib.php
mod/resource/tests/generator/lib.php
mod/scorm/lib.php
mod/scorm/report/graphs/graph.php
mod/scorm/tests/behat/add_scorm.feature [new file with mode: 0644]
mod/scorm/tests/packages/singlescobasic.zip [new file with mode: 0644]
mod/survey/tests/generator/lib.php [new file with mode: 0644]
mod/survey/tests/generator_test.php [new file with mode: 0644]
mod/url/tests/generator/lib.php [new file with mode: 0644]
mod/url/tests/generator_test.php [new file with mode: 0644]
mod/url/view.php
mod/wiki/backup/moodle2/restore_wiki_stepslib.php
mod/wiki/lib.php
mod/wiki/parser/parser.php
mod/wiki/tests/generator/lib.php [new file with mode: 0644]
mod/wiki/tests/generator_test.php [new file with mode: 0644]
mod/workshop/classes/event/course_module_viewed.php
mod/workshop/eval/best/lib.php
mod/workshop/lib.php
mod/workshop/tests/generator/lib.php [new file with mode: 0644]
mod/workshop/tests/generator_test.php [new file with mode: 0644]
notes/lib.php
notes/tests/events_test.php
pix/a/add_file.svg
pix/a/create_folder.svg
pix/a/download_all.svg
pix/a/help.svg
pix/a/logout.svg
pix/a/refresh.svg
pix/a/search.svg
pix/a/setting.svg
pix/a/view_icon_active.svg
pix/a/view_list_active.svg
pix/a/view_tree_active.svg
question/classes/statistics/responses/analyser.php
question/classes/statistics/responses/analysis_for_actual_response.php
question/classes/statistics/responses/analysis_for_class.php
question/classes/statistics/responses/analysis_for_question.php
question/classes/statistics/responses/analysis_for_subpart.php
question/tests/behat/question_categories.feature [new file with mode: 0644]
report/log/locallib.php
repository/boxnet/lib.php
repository/dropbox/lib.php
repository/equella/lib.php
repository/filepicker.js
repository/filesystem/lib.php
repository/lib.php
theme/base/pix/fp/add_file.svg
theme/base/pix/fp/create_folder.svg
theme/base/pix/fp/download_all.svg
theme/base/pix/fp/help.svg
theme/base/pix/fp/logout.svg
theme/base/pix/fp/refresh.svg
theme/base/pix/fp/search.svg
theme/base/pix/fp/setting.svg
theme/base/pix/fp/view_icon_active.svg
theme/base/pix/fp/view_list_active.svg
theme/base/pix/fp/view_tree_active.svg
theme/base/style/admin.css
theme/base/style/core.css
theme/base/style/filemanager.css
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/filemanager.less
theme/bootstrapbase/less/moodle/responsive.less
theme/bootstrapbase/pix/fp/add_file.svg
theme/bootstrapbase/pix/fp/create_folder.svg
theme/bootstrapbase/pix/fp/download_all.svg
theme/bootstrapbase/pix/fp/help.svg
theme/bootstrapbase/pix/fp/logout.svg
theme/bootstrapbase/pix/fp/refresh.svg
theme/bootstrapbase/pix/fp/search.svg
theme/bootstrapbase/pix/fp/setting.svg
theme/bootstrapbase/pix/fp/view_icon_active.svg
theme/bootstrapbase/pix/fp/view_list_active.svg
theme/bootstrapbase/pix/fp/view_tree_active.svg
theme/bootstrapbase/style/moodle.css
theme/boxxie/style/core.css
theme/brick/style/pagelayout.css
theme/formal_white/style/pagelayout.css
theme/mymobile/config.php [deleted file]
theme/mymobile/jquery/custom131.js [deleted file]
theme/mymobile/jquery/jquery.mobile-1.3.1.js [deleted file]
theme/mymobile/jquery/plugins.php [deleted file]
theme/mymobile/lang/en/theme_mymobile.php [deleted file]
theme/mymobile/layout/embedded.php [deleted file]
theme/mymobile/layout/general.php [deleted file]
theme/mymobile/lib.php [deleted file]
theme/mymobile/pix/07-map-marker.png [deleted file]
theme/mymobile/pix/41-picture-frame.png [deleted file]
theme/mymobile/pix/83-calendar.png [deleted file]
theme/mymobile/pix/ajax-loader.png [deleted file]
theme/mymobile/pix/ajax-loader2.gif [deleted file]
theme/mymobile/pix/ajax-loader2.png [deleted file]
theme/mymobile/pix/clip.png [deleted file]
theme/mymobile/pix/empty.gif [deleted file]
theme/mymobile/pix/favicon.ico [deleted file]
theme/mymobile/pix/form-check-off.png [deleted file]
theme/mymobile/pix/form-check-on.png [deleted file]
theme/mymobile/pix/form-radio-off.png [deleted file]
theme/mymobile/pix/form-radio-on.png [deleted file]
theme/mymobile/pix/header.png [deleted file]
theme/mymobile/pix/ibulb.png [deleted file]
theme/mymobile/pix/icalendar.png [deleted file]
theme/mymobile/pix/icalendarI4.png [deleted file]
theme/mymobile/pix/icon-arrow-white-down.png [deleted file]
theme/mymobile/pix/icon-arrow-white-left.png [deleted file]
theme/mymobile/pix/icon-arrow-white-right.png [deleted file]
theme/mymobile/pix/icon-arrow-white-up.png [deleted file]
theme/mymobile/pix/icon-delete.png [deleted file]
theme/mymobile/pix/icon-minus-white.png [deleted file]
theme/mymobile/pix/icon-plus-white.png [deleted file]
theme/mymobile/pix/icon-search-black.png [deleted file]
theme/mymobile/pix/icons-18-black.png [deleted file]
theme/mymobile/pix/icons-18-white.png [deleted file]
theme/mymobile/pix/icons-36-black.png [deleted file]
theme/mymobile/pix/icons-36-white.png [deleted file]
theme/mymobile/pix/imessage.png [deleted file]
theme/mymobile/pix/imessageI4.png [deleted file]
theme/mymobile/pix/iuser.png [deleted file]
theme/mymobile/pix/iuserI4.png [deleted file]
theme/mymobile/pix/linevert.png [deleted file]
theme/mymobile/pix/m2m.png [deleted file]
theme/mymobile/pix/m2m2x.png [deleted file]
theme/mymobile/pix/m2ms.png [deleted file]
theme/mymobile/pix/navs.png [deleted file]
theme/mymobile/pix/navsI4.png [deleted file]
theme/mymobile/pix/newnav.png [deleted file]
theme/mymobile/pix/newnav_s.png [deleted file]
theme/mymobile/pix/photos.png [deleted file]
theme/mymobile/pix/pinstripes.png [deleted file]
theme/mymobile/pix/power.png [deleted file]
theme/mymobile/pix/req.png [deleted file]
theme/mymobile/pix/screenshot.png [deleted file]
theme/mymobile/pix/texture_05.png [deleted file]
theme/mymobile/pix/texture_075.png [deleted file]
theme/mymobile/pix_core/a/help.png [deleted file]
theme/mymobile/pix_core/a/refresh.png [deleted file]
theme/mymobile/pix_core/a/search.png [deleted file]
theme/mymobile/pix_core/a/setting.png [deleted file]
theme/mymobile/pix_core/c/event.png [deleted file]
theme/mymobile/pix_core/docs.png [deleted file]
theme/mymobile/pix_core/f/audio.png [deleted file]
theme/mymobile/pix_core/f/avi.png [deleted file]
theme/mymobile/pix_core/f/dmg.png [deleted file]
theme/mymobile/pix_core/f/docm.png [deleted file]
theme/mymobile/pix_core/f/docx.png [deleted file]
theme/mymobile/pix_core/f/excel.png [deleted file]
theme/mymobile/pix_core/f/image.png [deleted file]
theme/mymobile/pix_core/f/text.png [deleted file]
theme/mymobile/pix_core/f/video.png [deleted file]
theme/mymobile/pix_core/f/web.png [deleted file]
theme/mymobile/pix_core/help.png [deleted file]
theme/mymobile/pix_core/i/admin.png [deleted file]
theme/mymobile/pix_core/i/backup.png [deleted file]
theme/mymobile/pix_core/i/calc.png [deleted file]
theme/mymobile/pix_core/i/checkpermissions.png [deleted file]
theme/mymobile/pix_core/i/closed.png [deleted file]
theme/mymobile/pix_core/i/cohort.png [deleted file]
theme/mymobile/pix_core/i/completion-auto-n.png [deleted file]
theme/mymobile/pix_core/i/completion-auto-y.png [deleted file]
theme/mymobile/pix_core/i/completion-manual-n.png [deleted file]
theme/mymobile/pix_core/i/completion-manual-y.png [deleted file]
theme/mymobile/pix_core/i/course.png [deleted file]
theme/mymobile/pix_core/i/courseevent.png [deleted file]
theme/mymobile/pix_core/i/db.png [deleted file]
theme/mymobile/pix_core/i/edit.png [deleted file]
theme/mymobile/pix_core/i/email.png [deleted file]
theme/mymobile/pix_core/i/files.png [deleted file]
theme/mymobile/pix_core/i/filter.png [deleted file]
theme/mymobile/pix_core/i/flagged.png [deleted file]
theme/mymobile/pix_core/i/grades.png [deleted file]
theme/mymobile/pix_core/i/group.png [deleted file]
theme/mymobile/pix_core/i/groupevent.png [deleted file]
theme/mymobile/pix_core/i/guest.png [deleted file]
theme/mymobile/pix_core/i/info.png [deleted file]
theme/mymobile/pix_core/i/item.png [deleted file]
theme/mymobile/pix_core/i/lock.png [deleted file]
theme/mymobile/pix_core/i/log.png [deleted file]
theme/mymobile/pix_core/i/mail.png [deleted file]
theme/mymobile/pix_core/i/menu.png [deleted file]
theme/mymobile/pix_core/i/navigationitem.png [deleted file]
theme/mymobile/pix_core/i/new.png [deleted file]
theme/mymobile/pix_core/i/news.png [deleted file]
theme/mymobile/pix_core/i/open.png [deleted file]
theme/mymobile/pix_core/i/payment.png [deleted file]
theme/mymobile/pix_core/i/publish.png [deleted file]
theme/mymobile/pix_core/i/reload.png [deleted file]
theme/mymobile/pix_core/i/report.png [deleted file]
theme/mymobile/pix_core/i/return.png [deleted file]
theme/mymobile/pix_core/i/rss.png [deleted file]
theme/mymobile/pix_core/i/search.png [deleted file]
theme/mymobile/pix_core/i/siteevent.png [deleted file]
theme/mymobile/pix_core/i/stats.png [deleted file]
theme/mymobile/pix_core/i/switch.png [deleted file]
theme/mymobile/pix_core/i/unflagged.png [deleted file]
theme/mymobile/pix_core/i/unlock.png [deleted file]
theme/mymobile/pix_core/i/user.png [deleted file]
theme/mymobile/pix_core/i/userevent.png [deleted file]
theme/mymobile/pix_core/i/users.png [deleted file]
theme/mymobile/pix_core/req.png [deleted file]
theme/mymobile/pix_core/t/add.png [deleted file]
theme/mymobile/pix_core/t/adddir.png [deleted file]
theme/mymobile/pix_core/t/addfile.png [deleted file]
theme/mymobile/pix_core/t/approve.png [deleted file]
theme/mymobile/pix_core/t/check.png [deleted file]
theme/mymobile/pix_core/t/collapsed.png [deleted file]
theme/mymobile/pix_core/t/delete.png [deleted file]
theme/mymobile/pix_core/t/down.png [deleted file]
theme/mymobile/pix_core/t/edit.png [deleted file]
theme/mymobile/pix_core/t/email.png [deleted file]
theme/mymobile/pix_core/t/expanded.png [deleted file]
theme/mymobile/pix_core/t/groupn.png [deleted file]
theme/mymobile/pix_core/t/hiddenuntil.png [deleted file]
theme/mymobile/pix_core/t/hideuntil.png [deleted file]
theme/mymobile/pix_core/t/left.png [deleted file]
theme/mymobile/pix_core/t/lock.png [deleted file]
theme/mymobile/pix_core/t/log.png [deleted file]
theme/mymobile/pix_core/t/message.png [deleted file]
theme/mymobile/pix_core/t/move.png [deleted file]
theme/mymobile/pix_core/t/portfolioadd.png [deleted file]
theme/mymobile/pix_core/t/preview.png [deleted file]
theme/mymobile/pix_core/t/ranges.png [deleted file]
theme/mymobile/pix_core/t/reload.png [deleted file]
theme/mymobile/pix_core/t/restore.png [deleted file]
theme/mymobile/pix_core/t/right.png [deleted file]
theme/mymobile/pix_core/t/unlock.png [deleted file]
theme/mymobile/pix_core/t/up.png [deleted file]
theme/mymobile/pix_core/t/user.png [deleted file]
theme/mymobile/pix_core/u/f1.png [deleted file]
theme/mymobile/pix_core/u/f2.png [deleted file]
theme/mymobile/pix_core/u/user100.png [deleted file]
theme/mymobile/pix_core/u/user35.png [deleted file]
theme/mymobile/pix_plugins/mod/assignment/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/book/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/chat/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/choice/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/data/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/feedback/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/folder/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/forum/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/glossary/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/glossary/print.png [deleted file]
theme/mymobile/pix_plugins/mod/label/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/lams/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/lesson/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/mail/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/page/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/quiz/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/resource/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/scorm/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/survey/icon.png [deleted file]
theme/mymobile/pix_plugins/mod/wiki/icon.png [deleted file]
theme/mymobile/readme_moodle.txt [deleted file]
theme/mymobile/renderers.php [deleted file]
theme/mymobile/renderers/mod_choice_renderer.php [deleted file]
theme/mymobile/settings.php [deleted file]
theme/mymobile/style/core.css [deleted file]
theme/mymobile/style/jmobile131.css [deleted file]
theme/mymobile/style/jmobile131_rtl.css [deleted file]
theme/mymobile/style/media.css [deleted file]
theme/splash/style/pagelayout.css
theme/upgrade.txt
user/profile/field/datetime/define.class.php
version.php

index 364b54e..c5dc276 100644 (file)
@@ -20,6 +20,10 @@ Feature: Page contents assertions
     And I wait "2" seconds
     And I hover ".region-content .generaltable td span" "css_element"
     Then I should see "I'm the description"
+    And "Grouping" "select" in the "region-main" "region" should be visible
+    And "Group" "select" should be visible
+    And "Messaging" "link" in the "Administration" "block" should not be visible
+    And "Change password" "link" should not be visible
     And I should see "Filter groups by"
     And I should not see "Filter groupssss by"
     And I should see "Group members" in the ".region-content table th.c1" "css_element"
index 7d74072..bd2cf9f 100644 (file)
@@ -329,7 +329,7 @@ class tool_generator_course_backend extends tool_generator_backend {
         $number = self::$parampages[$this->size];
         $this->log('createpages', $number, true);
         for ($i = 0; $i < $number; $i++) {
-            $record = array('course' => $this->course->id);
+            $record = array('course' => $this->course);
             $options = array('section' => $this->get_target_section());
             $pagegenerator->create_instance($record, $options);
             $this->dot($i, $number);
@@ -347,7 +347,7 @@ class tool_generator_course_backend extends tool_generator_backend {
 
         // Create resource with default textfile only.
         $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
-        $record = array('course' => $this->course->id,
+        $record = array('course' => $this->course,
                 'name' => get_string('smallfiles', 'tool_generator'));
         $options = array('section' => 0);
         $resource = $resourcegenerator->create_instance($record, $options);
@@ -415,7 +415,7 @@ class tool_generator_course_backend extends tool_generator_backend {
         $resourcegenerator = $this->generator->get_plugin_generator('mod_resource');
         for ($i = 0; $i < $count; $i++) {
             // Create resource.
-            $record = array('course' => $this->course->id,
+            $record = array('course' => $this->course,
                     'name' => get_string('bigfile', 'tool_generator', $i));
             $options = array('section' => $this->get_target_section());
             $resource = $resourcegenerator->create_instance($record, $options);
@@ -458,7 +458,7 @@ class tool_generator_course_backend extends tool_generator_backend {
 
         // Create empty forum.
         $forumgenerator = $this->generator->get_plugin_generator('mod_forum');
-        $record = array('course' => $this->course->id,
+        $record = array('course' => $this->course,
                 'name' => get_string('pluginname', 'forum'));
         $options = array('section' => 0);
         $forum = $forumgenerator->create_instance($record, $options);
index e759c43..be0da1c 100644 (file)
@@ -140,15 +140,32 @@ class tool_installaddon_installer {
         $fp = get_file_packer('application/zip');
         $files = $fp->extract_to_pathname($zipfilepath, $targetdir);
 
-        if ($files) {
-            if (!empty($rootdir)) {
-                $files = $this->rename_extracted_rootdir($targetdir, $rootdir, $files);
-            }
-            return $files;
-
-        } else {
+        if (!$files) {
             return array();
         }
+
+        if (!empty($rootdir)) {
+            $files = $this->rename_extracted_rootdir($targetdir, $rootdir, $files);
+        }
+
+        // Sometimes zip may not contain all parent directories, add them to make it consistent.
+        foreach ($files as $path => $status) {
+            if ($status !== true) {
+                continue;
+            }
+            $parts = explode('/', trim($path, '/'));
+            while (array_pop($parts)) {
+                if (empty($parts)) {
+                    break;
+                }
+                $dir = implode('/', $parts).'/';
+                if (!isset($files[$dir])) {
+                    $files[$dir] = true;
+                }
+            }
+        }
+
+        return $files;
     }
 
     /**
index 948294c..3ebf2f7 100644 (file)
@@ -42,7 +42,7 @@ class tool_installaddon_installer_testcase extends advanced_testcase {
         $this->assertEquals(1, preg_match('~^site=(.+)$~', $query, $matches));
         $site = rawurldecode($matches[1]);
         $site = json_decode(base64_decode($site), true);
-        $this->assertEquals('array', gettype($site));
+        $this->assertInternalType('array', $site);
         $this->assertEquals(3, count($site));
         $this->assertSame('Nasty site', $site['fullname']);
         $this->assertSame('file:///etc/passwd', $site['url']);
@@ -57,8 +57,8 @@ class tool_installaddon_installer_testcase extends advanced_testcase {
 
         $installer = tool_installaddon_installer::instance();
         $files = $installer->extract_installfromzip_file($sourcedir.'/testinvalidroot.zip', $contentsdir, 'fixed_root');
-        $this->assertEquals('array', gettype($files));
-        $this->assertEquals(4, count($files));
+        $this->assertInternalType('array', $files);
+        $this->assertCount(4, $files);
         $this->assertSame(true, $files['fixed_root/']);
         $this->assertSame(true, $files['fixed_root/lang/']);
         $this->assertSame(true, $files['fixed_root/lang/en/']);
index dd55653..d0342f5 100644 (file)
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright 2013 David Mudrak <david@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_installaddon_validator_test extends basic_testcase {
+class tool_installaddon_validator_testcase extends basic_testcase {
 
     public function test_validate_files_layout() {
         $fixtures = dirname(__FILE__).'/fixtures';
@@ -159,8 +159,8 @@ class tool_installaddon_validator_test extends basic_testcase {
         $this->assertEquals('foobar', $validator->get_rootdir());
         $this->assertTrue($this->has_message($validator->get_messages(), $validator::INFO, 'rootdir', 'foobar'));
         $versionphpinfo = $validator->get_versionphp_info();
-        $this->assertEquals('array', gettype($versionphpinfo));
-        $this->assertEquals(4, count($versionphpinfo));
+        $this->assertInternalType('array', $versionphpinfo);
+        $this->assertCount(4, $versionphpinfo);
         $this->assertEquals(2013031900, $versionphpinfo['version']);
         $this->assertEquals(2013031200, $versionphpinfo['requires']);
         $this->assertEquals('local_foobar', $versionphpinfo['component']);
@@ -279,8 +279,8 @@ class tool_installaddon_validator_test extends basic_testcase {
         $this->assertEquals('testable_tool_installaddon_validator', get_class($validator));
 
         $info = $validator->testable_parse_version_php($fixtures.'/version1.php');
-        $this->assertEquals('array', gettype($info));
-        $this->assertEquals(7, count($info));
+        $this->assertInternalType('array', $info);
+        $this->assertCount(7, $info);
         $this->assertEquals('block_foobar', $info['plugin->component']);    // Later in the file.
         $this->assertEquals('2013010100', $info['plugin->version']);        // Numeric wins over strings.
         $this->assertEquals('2012122401', $info['plugin->requires']);       // Commented.
index a9f7afa..82f7ccc 100644 (file)
@@ -85,7 +85,7 @@ $today = time();
 $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
 
 // array of all valid fields for validation
-$STD_FIELDS = array('id', 'firstname', 'lastname', 'username', 'email',
+$STD_FIELDS = array('id', 'username', 'email',
         'city', 'country', 'lang', 'timezone', 'mailformat',
         'maildisplay', 'maildigest', 'htmleditor', 'autosubscribe',
         'institution', 'department', 'idnumber', 'skype',
@@ -97,6 +97,8 @@ $STD_FIELDS = array('id', 'firstname', 'lastname', 'username', 'email',
         'deleted',     // 1 means delete user
         'mnethostid',  // Can not be used for adding, updating or deleting of users - only for enrolments, groups, cohorts and suspending.
     );
+// Include all name fields.
+$STD_FIELDS = array_merge($STD_FIELDS, get_all_user_name_fields());
 
 $PRF_FIELDS = array();
 
index 9dfc288..f922995 100644 (file)
@@ -26,7 +26,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 require_once $CFG->libdir.'/formslib.php';
-
+require_once($CFG->dirroot . '/user/editlib.php');
 
 /**
  * Upload a file CVS file with user information.
@@ -375,24 +375,20 @@ class admin_uploaduser_form2 extends moodleform {
 
         // look for other required data
         if ($optype != UU_USER_UPDATE) {
-            if (!in_array('firstname', $columns)) {
-                $errors['uutype'] = get_string('missingfield', 'error', 'firstname');
-            }
-
-            if (!in_array('lastname', $columns)) {
-                if (isset($errors['uutype'])) {
-                    $errors['uutype'] = '';
-                } else {
-                    $errors['uutype'] = ' ';
+            $requiredusernames = useredit_get_required_name_fields();
+            $missing = array();
+            foreach ($requiredusernames as $requiredusername) {
+                if (!in_array($requiredusername, $columns)) {
+                    $missing[] = get_string('missingfield', 'error', $requiredusername);;
                 }
-                $errors['uutype'] .= get_string('missingfield', 'error', 'lastname');
             }
-
+            if ($missing) {
+                $errors['uutype'] = implode('<br />',  $missing);
+            }
             if (!in_array('email', $columns) and empty($data['email'])) {
                 $errors['email'] = get_string('requiredtemplate', 'tool_uploaduser');
             }
         }
-
         return $errors;
     }
 
index 7439b27..46862a7 100644 (file)
@@ -39,6 +39,7 @@ class create_and_clean_temp_stuff extends backup_execution_step {
         backup_helper::check_and_create_backup_dir($this->get_backupid());// Create backup temp dir
         backup_helper::clear_backup_dir($this->get_backupid(), $progress);           // Empty temp dir, just in case
         backup_helper::delete_old_backup_dirs(time() - (4 * 60 * 60), $progress);    // Delete > 4 hours temp dirs
+        backup_controller_dbops::drop_backup_ids_temp_table($this->get_backupid()); // Drop ids temp table
         backup_controller_dbops::create_backup_ids_temp_table($this->get_backupid()); // Create ids temp table
         $progress->end_progress();
     }
index cb4577c..6d066b4 100644 (file)
@@ -160,8 +160,10 @@ abstract class backup_controller_dbops extends backup_dbops {
         $dbman = $DB->get_manager(); // We are going to use database_manager services
 
         $targettablename = 'backup_ids_temp';
-        $table = new xmldb_table($targettablename);
-        $dbman->drop_table($table); // And drop it
+        if ($dbman->table_exists($targettablename)) {
+            $table = new xmldb_table($targettablename);
+            $dbman->drop_table($table); // And drop it
+        }
     }
 
     /**
index af51a7c..914e4ab 100644 (file)
@@ -559,7 +559,7 @@ abstract class backup_cron_automated_helper {
     public static function set_state_running($running = true) {
         if ($running === true) {
             if (self::get_automated_backup_state() === self::STATE_RUNNING) {
-                throw new backup_exception('backup_automated_already_running');
+                throw new backup_helper_exception('backup_automated_already_running');
             }
             set_config('backup_auto_running', '1', 'backup');
         } else {
index 1ed3a1b..cc1a0d4 100644 (file)
@@ -43,6 +43,8 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
 
         unset_config('noemailever');
 
+        $CFG->enablecompletion = true;
+
         $user = $this->getDataGenerator()->create_user();
 
         $fordb = new stdClass();
@@ -69,7 +71,7 @@ class core_badges_badgeslib_testcase extends advanced_testcase {
         $this->badgeid = $DB->insert_record('badge', $fordb, true);
 
         // Create a course with activity and auto completion tracking.
-        $this->course = $this->getDataGenerator()->create_course();
+        $this->course = $this->getDataGenerator()->create_course(array('enablecompletion' => true));
         $this->user = $this->getDataGenerator()->create_user();
         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
         $this->assertNotEmpty($studentrole);
index 62e63b3..7cdd2dd 100644 (file)
@@ -9,50 +9,242 @@ Feature: Award badges
     And I log in as "admin"
 
   @javascript
-  Scenario: Add criteria
+  Scenario: Award profile badge
     Given I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
     And I fill the moodle form with:
-      | Name | Test Badge |
+      | Name | Profile Badge |
       | Description | Test badge description |
       | issuername | Test Badge Site |
       | issuercontact | testuser@test-badge-site.com |
     And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
     And I press "Create badge"
     And I select "Profile completion" from "type"
-    And I wait "5" seconds
     And I check "First name"
     And I check "Email address"
+    And I check "Phone"
     When I press "Save"
     Then I should see "Profile completion"
     And I should see "First name"
     And I should see "Email address"
     And I should not see "Criteria for this badge have not been set up yet."
+    And I press "Enable access"
+    And I press "Continue"
+    And I expand "My profile settings" node
+    And I follow "Edit profile"
+    And I expand all fieldsets
+    And I fill in "Phone" with "123456789"
+    And I press "Update profile"
+    And I follow "My badges"
+    Then I should see "Profile Badge"
+    And I should not see "There are no badges available."
 
   @javascript
-  Scenario: Earn badge
-    Given I expand "Site administration" node
+  Scenario: Award site badge
+    Given the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher | teacher | 1 | teacher1@asd.com |
+      | student | student | 1 | student1@asd.com |
+    And I expand "Site administration" node
     And I expand "Badges" node
     And I follow "Add a new badge"
     And I fill the moodle form with:
-      | Name | Profile Badge |
-      | Description | Test badge description |
-      | issuername | Test Badge Site |
-      | issuercontact | testuser@test-badge-site.com |
+      | Name | Site Badge |
+      | Description | Site badge description |
+      | issuername | Tester of site badge |
     And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
     And I press "Create badge"
-    And I select "Profile completion" from "type"
-    And I wait "5" seconds
-    And I check "Phone"
+    And I select "Manual issue by role" from "type"
+    And I check "Teacher"
     And I press "Save"
     And I press "Enable access"
     And I press "Continue"
-    And I expand "My profile settings" node
-    And I follow "Edit profile"
-    And I expand all fieldsets
-    And I fill in "Phone" with "123456789"
-    And I press "Update profile"
-    When I follow "My badges"
-    Then I should see "Profile Badge"
-    And I should not see "There are no badges available."
+    And I follow "Recipients (0)"
+    And I press "Award badge"
+    And I select "teacher 1 (teacher1@asd.com)" from "potentialrecipients[]"
+    And I press "Award badge"
+    And I select "student 1 (student1@asd.com)" from "potentialrecipients[]"
+    And I press "Award badge"
+    When I follow "Site Badge"
+    Then I should see "Recipients (2)"
+    And I log out
+    And I log in as "student"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "Site Badge"
+
+  @javascript
+  Scenario: Award course badge
+    Given the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com |
+      | student1 | Student | 1 | student1@asd.com |
+      | student2 | Student | 2 | student2@asd.com |
+    And the following "courses" exists:
+      | fullname | shortname | category | groupmode |
+      | Course 1 | C1 | 0 | 1 |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+      | student2 | C1 | student |
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+    And I follow "Add a new badge"
+    And I fill the moodle form with:
+      | Name | Course Badge |
+      | Description | Course badge description |
+      | issuername | Tester of course badge |
+    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I press "Create badge"
+    And I select "Manual issue by role" from "type"
+    And I check "Teacher"
+    And I press "Save"
+    And I press "Enable access"
+    And I press "Continue"
+    And I follow "Recipients (0)"
+    And I press "Award badge"
+    And I select "Student 2 (student2@asd.com)" from "potentialrecipients[]"
+    And I press "Award badge"
+    And I select "Student 1 (student1@asd.com)" from "potentialrecipients[]"
+    When I press "Award badge"
+    And I follow "Course Badge"
+    Then I should see "Recipients (2)"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "Course Badge"
+
+  @javascript
+  Scenario: Award badge on activity completion
+    Given the following "courses" exists:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | Frist | teacher1@asd.com |
+      | student1 | Student | First | student1@asd.com |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+    And I log out
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable completion tracking | 1 |
+    And I follow "Home"
+    And I follow "Course 1"
+    And I follow "Edit settings"
+    And I fill the moodle form with:
+      | Enable completion tracking | Yes |
+    And I press "Save changes"
+    And I turn editing mode on
+    And I add a "Assignment" to section "1" and I fill the form with:
+      | Assignment name | Test assignment name |
+      | Description | Submit your online text |
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+    And I follow "Add a new badge"
+    And I fill the moodle form with:
+      | Name | Course Badge |
+      | Description | Course badge description |
+      | issuername | Tester of course badge |
+    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I press "Create badge"
+    And I select "Activity completion" from "type"
+    And I check "Test assignment name"
+    And I press "Save"
+    And I press "Enable access"
+    When I press "Continue"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "There are no badges available."
+    And I follow "Home"
+    And I follow "Course 1"
+    And I press "Mark as complete: Test assignment name"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "Course Badge"
+
+  @javascript
+  Scenario: Award badge on course completion
+    Given the following "courses" exists:
+      | fullname | shortname | category |
+      | Course 1 | C1 | 0 |
+    And the following "users" exists:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | Frist | teacher1@asd.com |
+      | student1 | Student | First | student1@asd.com |
+    And the following "course enrolments" exists:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+    And I log out
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable completion tracking | 1 |
+    And I follow "Home"
+    And I follow "Course 1"
+    And I follow "Edit settings"
+    And I fill the moodle form with:
+      | Enable completion tracking | Yes |
+    And I press "Save changes"
+    And I turn editing mode on
+    And I add a "Assignment" to section "1" and I fill the form with:
+      | Assignment name | Test assignment name |
+      | Description | Submit your online text |
+      | assignsubmission_onlinetext_enabled | 1 |
+    And I follow "Course completion"
+    And I select "2" from "id_overall_aggregation"
+    And I click on "Condition: Activity completion" "link"
+    And I check "Assign - Test assignment name"
+    And I press "Save changes"
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I click on "//span[text()='Badges']" "xpath_element" in the "Administration" "block"
+    And I follow "Add a new badge"
+    And I fill the moodle form with:
+      | Name | Course Badge |
+      | Description | Course badge description |
+      | issuername | Tester of course badge |
+    And I upload "badges/tests/behat/badge.png" file to "Image" filepicker
+    And I press "Create badge"
+    And I select "Course completion" from "type"
+    And I fill the moodle form with:
+      | grade_2 | 0 |
+    And I press "Save"
+    And I press "Enable access"
+    When I press "Continue"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "There are no badges available."
+    And I follow "Home"
+    And I follow "Course 1"
+    And I press "Mark as complete: Test assignment name"
+    And I log out
+    And I log in as "admin"
+    # We can't wait for cron to happen, so the admin manually triggers it.
+    And I trigger cron
+    # The admin needs to trigger cron twice to see the completion status as completed.
+    And I trigger cron
+    # Finally the admin goes back to homepage to continue the user story.
+    And I am on homepage
+    And I log out
+    And I log in as "student1"
+    And I expand "My profile" node
+    And I follow "My badges"
+    Then I should see "Course Badge"
\ No newline at end of file
index a7224e5..b87920c 100644 (file)
@@ -130,7 +130,7 @@ $communityid = optional_param('communityid', '', PARAM_INT);
 if ($remove != -1 and !empty($communityid) and confirm_sesskey()) {
     $communitymanager->block_community_remove_course($communityid, $USER->id);
     echo $OUTPUT->header();
-    echo $renderer->remove_success(new moodle_url(get_referer(false)));
+    echo $renderer->remove_success(new moodle_url('/course/view.php', array('id' => $courseid)));
     echo $OUTPUT->footer();
     die();
 }
index b2ec8ab..bbfc989 100644 (file)
@@ -38,6 +38,7 @@ class block_community_manager {
         $community = $this->block_community_get_course($course->url, $userid);
 
         if (empty($community)) {
+            $community = new stdClass();
             $community->userid = $userid;
             $community->coursename = $course->name;
             $community->coursedescription = $course->description;
index 9890661..c236d9f 100644 (file)
@@ -81,8 +81,10 @@ function block_html_pluginfile($course, $birecord_or_cm, $context, $filearea, $a
         $forcedownload = true;
     }
 
+    // NOTE: it woudl be nice to have file revisions here, for now rely on standard file lifetime,
+    //       do not lower it because the files are dispalyed very often.
     \core\session\manager::write_close();
-    send_stored_file($file, 60*60, 0, $forcedownload, $options);
+    send_stored_file($file, null, 0, $forcedownload, $options);
 }
 
 /**
index 2c6e451..eb8b42a 100644 (file)
@@ -31,6 +31,7 @@
 .dir-rtl .block_settings .block_tree .tree_item.branch {background-position: center right;}
 .dir-rtl .block_settings .block_tree .root_node.leaf {padding-right:0px;}
 .dir-rtl .block_settings .block_tree li.item_with_icon > p img { right: 0; left: auto;}
+.jsenabled .block_settings .block_tree .tree_item.branch.loadingbranch {background-image:url([[pix:i/loading_small]]);}
 
 .jsenabled.dir-rtl .block_settings .block_tree .tree_item.emptybranch {background-image: url([[pix:t/collapsed_empty_rtl]]);background-position: center right;}
 .jsenabled.dir-rtl .block_settings .block_tree .collapsed .tree_item.branch {background-image: url([[pix:t/collapsed_rtl]]);}
index a1a4704..a4e4210 100644 (file)
@@ -265,8 +265,7 @@ class blog_entry implements renderable {
         // Trigger an event for the new entry.
         $event = \core\event\blog_entry_created::create(array(
             'objectid'      => $this->id,
-            'relateduserid' => $this->userid,
-            'other'         => array('subject' => $this->subject)
+            'relateduserid' => $this->userid
         ));
         $event->set_custom_data($this);
         $event->trigger();
@@ -308,8 +307,7 @@ class blog_entry implements renderable {
 
         $event = \core\event\blog_entry_updated::create(array(
             'objectid'      => $entry->id,
-            'relateduserid' => $entry->userid,
-            'other'         => array('subject' => $entry->subject)
+            'relateduserid' => $entry->userid
         ));
         $event->set_custom_data($entry);
         $event->trigger();
@@ -333,9 +331,8 @@ class blog_entry implements renderable {
 
         $event = \core\event\blog_entry_deleted::create(array(
             'objectid'      => $this->id,
-            'relateduserid' => $this->userid,
-            'other'         => array('record' => (array) $record)
-        ));
+            'relateduserid' => $this->userid
+            ));
         $event->add_record_snapshot("post", $record);
         $event->set_custom_data($this);
         $event->trigger();
index b470ffc..a7d52fe 100644 (file)
@@ -59,19 +59,40 @@ abstract class type_base {
     public abstract function get_months();
 
     /**
-     * Returns the minimum year of the calendar.
+     * Returns the minimum year for the calendar.
      *
-     * @return int the minumum year
+     * @return int The minimum year
      */
     public abstract function get_min_year();
 
     /**
-     * Returns the maximum year of the calendar.
+     * Returns the maximum year for the calendar
      *
-     * @return int the max year
+     * @return int The maximum year
      */
     public abstract function get_max_year();
 
+    /**
+     * Returns an array of years.
+     *
+     * @param int $minyear
+     * @param int $maxyear
+     * @return array the years
+     */
+    public abstract function get_years($minyear = null, $maxyear = null);
+
+    /**
+     * Returns a multidimensional array with information for day, month, year
+     * and the order they are displayed when selecting a date.
+     * The order in the array will be the order displayed when selecting a date.
+     * Override this function to change the date selector order.
+     *
+     * @param int $minyear The year to start with
+     * @param int $maxyear The year to finish with
+     * @return array Full date information
+     */
+    public abstract function get_date_order($minyear = null, $maxyear = null);
+
     /**
      * Returns the number of days in a week.
      *
@@ -189,4 +210,11 @@ abstract class type_base {
      * @return array the converted date
      */
     public abstract function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0);
+
+    /**
+     * This return locale for windows os.
+     *
+     * @return string locale
+     */
+    public abstract function locale_win_charset();
 }
index 862cdc7..ab725d1 100644 (file)
@@ -76,23 +76,66 @@ class structure extends type_base {
     }
 
     /**
-     * Returns the minimum year of the calendar.
+     * Returns the minimum year for the calendar.
      *
-     * @return int the minumum year
+     * @return int The minimum year
      */
     public function get_min_year() {
-        return 1970;
+        return 1900;
     }
 
     /**
-     * Returns the maximum year of the calendar.
+     * Returns the maximum year for the calendar
      *
-     * @return int the max year
+     * @return int The maximum year
      */
     public function get_max_year() {
         return 2050;
     }
 
+    /**
+     * Returns an array of years.
+     *
+     * @param int $minyear
+     * @param int $maxyear
+     * @return array the years.
+     */
+    public function get_years($minyear = null, $maxyear = null) {
+        if (is_null($minyear)) {
+            $minyear = $this->get_min_year();
+        }
+
+        if (is_null($maxyear)) {
+            $maxyear = $this->get_max_year();
+        }
+
+        $years = array();
+        for ($i = $minyear; $i <= $maxyear; $i++) {
+            $years[$i] = $i;
+        }
+
+        return $years;
+    }
+
+    /**
+     * Returns a multidimensional array with information for day, month, year
+     * and the order they are displayed when selecting a date.
+     * The order in the array will be the order displayed when selecting a date.
+     * Override this function to change the date selector order.
+     *
+     * @param int $minyear The year to start with.
+     * @param int $maxyear The year to finish with.
+     * @return array Full date information.
+     */
+    public function get_date_order($minyear = null, $maxyear = null) {
+        $dateinfo = array();
+        $dateinfo['day'] = $this->get_days();
+        $dateinfo['month'] = $this->get_months();
+        $dateinfo['year'] = $this->get_years($minyear, $maxyear);
+
+        return $dateinfo;
+    }
+
     /**
      * Returns the number of days in a week.
      *
@@ -262,4 +305,13 @@ class structure extends type_base {
             'hour' => (int) $hour,
             'minute' => (int) $minute);
     }
+
+    /**
+     * This return locale for windows os.
+     *
+     * @return string locale
+     */
+    public function locale_win_charset() {
+        return get_string('localewincharset', 'langconfig');
+    }
 }
index cf50cfc..950d859 100644 (file)
@@ -76,23 +76,66 @@ class structure extends type_base {
     }
 
     /**
-     * Returns the minimum year of the calendar.
+     * Returns the minimum year for the calendar.
      *
-     * @return int the minumum year
+     * @return int The minimum year
      */
     public function get_min_year() {
         return 1900;
     }
 
     /**
-     * Returns the maximum year of the calendar.
+     * Returns the maximum year for the calendar
      *
-     * @return int the max year
+     * @return int The maximum year
      */
     public function get_max_year() {
         return 2050;
     }
 
+    /**
+     * Returns an array of years.
+     *
+     * @param int $minyear
+     * @param int $maxyear
+     * @return array the years
+     */
+    public function get_years($minyear = null, $maxyear = null) {
+        if (is_null($minyear)) {
+            $minyear = $this->get_min_year();
+        }
+
+        if (is_null($maxyear)) {
+            $maxyear = $this->get_max_year();
+        }
+
+        $years = array();
+        for ($i = $minyear; $i <= $maxyear; $i++) {
+            $years[$i] = $i;
+        }
+
+        return $years;
+    }
+
+    /**
+     * Returns a multidimensional array with information for day, month, year
+     * and the order they are displayed when selecting a date.
+     * The order in the array will be the order displayed when selecting a date.
+     * Override this function to change the date selector order.
+     *
+     * @param int $minyear The year to start with
+     * @param int $maxyear The year to finish with
+     * @return array Full date information
+     */
+    public function get_date_order($minyear = null, $maxyear = null) {
+        $dateinfo = array();
+        $dateinfo['day'] = $this->get_days();
+        $dateinfo['month'] = $this->get_months();
+        $dateinfo['year'] = $this->get_years($minyear, $maxyear);
+
+        return $dateinfo;
+    }
+
     /**
      * Returns the number of days in a week.
      *
@@ -375,4 +418,13 @@ class structure extends type_base {
 
         return $date;
     }
+
+    /**
+     * This return locale for windows os.
+     *
+     * @return string locale
+     */
+    public function locale_win_charset() {
+        return get_string('localewincharset', 'langconfig');
+    }
 }
index 472d05e..45ff5a8 100644 (file)
@@ -8,6 +8,6 @@
     "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*",
-        "moodlehq/behat-extension": "1.25.6"
+        "moodlehq/behat-extension": "1.26.*"
     }
 }
index 3050973..08a50a0 100644 (file)
@@ -201,7 +201,7 @@ $CFG->admin = 'admin';
 //
 // Seconds for files to remain in caches. Decrease this if you are worried
 // about students being served outdated versions of uploaded files.
-//     $CFG->filelifetime = 86400;
+//     $CFG->filelifetime = 60*60*6;
 //
 // Some web servers can offload the file serving from PHP process,
 // comment out one the following options to enable it in Moodle:
index 8979c8c..ce02da9 100644 (file)
@@ -208,6 +208,20 @@ class helper {
             );
         }
 
+        // Resort.
+        if ($category->can_resort_subcategories() && $category->has_children()) {
+            $actions['resortbyname'] = array(
+                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'name')),
+                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
+                'string' => new \lang_string('resortsubcategoriesbyname', 'moodle')
+            );
+            $actions['resortbyidnumber'] = array(
+                'url' => new \moodle_url($baseurl, array('action' => 'resortcategories', 'resort' => 'idnumber')),
+                'icon' => new \pix_icon('t/sort', new \lang_string('sort')),
+                'string' => new \lang_string('resortsubcategoriesbyidnumber', 'moodle')
+            );
+        }
+
         // Delete.
         if ($category->can_delete_full()) {
             $actions['delete'] = array(
index 943727a..fa26e1b 100644 (file)
@@ -264,7 +264,7 @@ class core_course_management_renderer extends plugin_renderer_base {
         }
 
         $hasitems = false;
-        if ($createtoplevel || $createsubcategory) {
+        if ($createtoplevel) {
             $hasitems = true;
             $menu = new action_menu;
             if ($createtoplevel) {
@@ -294,29 +294,28 @@ class core_course_management_renderer extends plugin_renderer_base {
         if (coursecat::can_approve_course_requests()) {
             $actions[] = html_writer::link(new moodle_url('/course/pending.php'), get_string('coursespending'));
         }
-        if ($category->can_resort_subcategories()) {
+        if (coursecat::get(0)->can_resort_subcategories()) {
             $hasitems = true;
-            $params = $this->page->url->params();
+            $params = array();
             $params['action'] = 'resortcategories';
             $params['sesskey'] = sesskey();
+            if ($this->page->url->get_param('categoryid') !== null) {
+                $params['selectedcategoryid'] = $this->page->url->get_param('categoryid');
+            }
             $baseurl = new moodle_url('/course/management.php', $params);
             $menu = new action_menu(array(
                 new action_menu_link_secondary(
                     new moodle_url($baseurl, array('resort' => 'name')),
                     null,
-                    get_string('resortbyname')
+                    get_string('resortcategoriesbyname')
                 ),
                 new action_menu_link_secondary(
                     new moodle_url($baseurl, array('resort' => 'idnumber')),
                     null,
-                    get_string('resortbyidnumber')
+                    get_string('resortcategoriesbyidnumber')
                 )
             ));
-            if ($category->id === 0) {
-                $menu->actiontext = get_string('resortcategories');
-            } else {
-                $menu->actiontext = get_string('resortsubcategories');
-            }
+            $menu->actiontext = get_string('resortcategories');
             $menu->actionicon = new pix_icon('t/contextmenu', ' ', 'moodle', array('class' => 'iconsmall', 'title' => ''));
             $actions[] = $this->render($menu);
         }
@@ -385,7 +384,11 @@ class core_course_management_renderer extends plugin_renderer_base {
             );
         }
         if (coursecat::can_change_parent_any()) {
-            $options = coursecat::make_categories_list('moodle/category:manage');
+            $options = array();
+            if (has_capability('moodle/category:manage', context_system::instance())) {
+                $options[0] = coursecat::get(0)->get_formatted_name();
+            }
+            $options += coursecat::make_categories_list('moodle/category:manage');
             $select = html_writer::select(
                 $options,
                 'movecategoriesto',
index 6519fb4..3fa6bb2 100644 (file)
@@ -627,15 +627,7 @@ class dndupload_ajax_processor {
         if (!$this->cm->id = add_course_module($this->cm)) {
             throw new coding_exception("Unable to create the course module");
         }
-        // The following are used inside some few core functions, so may as well set them here.
         $this->cm->coursemodule = $this->cm->id;
-        $groupbuttons = ($this->course->groupmode or (!$this->course->groupmodeforce));
-        if ($groupbuttons and plugin_supports('mod', $this->module->name, FEATURE_GROUPS, 0)) {
-            $this->cm->groupmodelink = (!$this->course->groupmodeforce);
-        } else {
-            $this->cm->groupmodelink = false;
-            $this->cm->groupmode = false;
-        }
     }
 
     /**
@@ -702,8 +694,6 @@ class dndupload_ajax_processor {
             throw new moodle_exception('errorcreatingactivity', 'moodle', '', $this->module->name);
         }
         $mod = $info->get_cm($this->cm->id);
-        $mod->groupmodelink = $this->cm->groupmodelink;
-        $mod->groupmode = $this->cm->groupmode;
 
         // Trigger course module created event.
         $event = \core\event\course_module_created::create(array(
index 3ad88b5..b5d03e1 100644 (file)
@@ -3242,6 +3242,7 @@ function course_get_url($courseorid, $section = null, $options = array()) {
  *
  * @param object $module
  * @return object the created module info
+ * @throws moodle_exception if user is not allowed to perform the action or module is not allowed in this course
  */
 function create_module($moduleinfo) {
     global $DB, $CFG;
@@ -3263,9 +3264,6 @@ function create_module($moduleinfo) {
     $course = $DB->get_record('course', array('id'=>$moduleinfo->course), '*', MUST_EXIST);
     list($module, $context, $cw) = can_add_moduleinfo($course, $moduleinfo->modulename, $moduleinfo->section);
 
-    // Load module library.
-    include_modulelib($module->name);
-
     // Add the module.
     $moduleinfo->module = $module->id;
     $moduleinfo = add_moduleinfo($moduleinfo, $course, null);
@@ -3282,6 +3280,7 @@ function create_module($moduleinfo) {
  *
  * @param object $module
  * @return object the updated module info
+ * @throws moodle_exception if current user is not allowed to update the module
  */
 function update_module($moduleinfo) {
     global $DB, $CFG;
@@ -3297,9 +3296,6 @@ function update_module($moduleinfo) {
     // Some checks (capaibility / existing instances).
     list($cm, $context, $module, $data, $cw) = can_update_moduleinfo($cm);
 
-    // Load module library.
-    include_modulelib($module->name);
-
     // Retrieve few information needed by update_moduleinfo.
     $moduleinfo->modulename = $cm->modname;
     if (!isset($moduleinfo->scale)) {
index b787853..301e0a5 100644 (file)
@@ -27,6 +27,7 @@ require_once($CFG->dirroot.'/lib/coursecatlib.php');
 require_once($CFG->dirroot.'/course/lib.php');
 
 $categoryid = optional_param('categoryid', null, PARAM_INT);
+$selectedcategoryid = optional_param('selectedcategoryid', null, PARAM_INT);
 $courseid = optional_param('courseid', null, PARAM_INT);
 $action = optional_param('action', false, PARAM_ALPHA);
 $page = optional_param('page', 0, PARAM_INT);
@@ -79,6 +80,11 @@ if ($courseid) {
     navigation_node::override_active_url($url);
 }
 
+// Check if there is a selected category param, and if there is apply it.
+if ($course === null && $selectedcategoryid !== null && $selectedcategoryid !== $categoryid) {
+    $url->param('categoryid', $selectedcategoryid);
+}
+
 if ($page !== 0) {
     $url->param('page', $page);
 }
@@ -318,12 +324,20 @@ if ($action !== false && confirm_sesskey()) {
                     $a = new stdClass;
                     $a->count = $movecount;
                     $a->to = $movetocat->get_formatted_name();
-                    $notificationspass[] = get_string('movecategoriessuccess', 'moodle', $a);
+                    $movesuccessstrkey = 'movecategoriessuccess';
+                    if ($movetocatid == 0) {
+                        $movesuccessstrkey = 'movecategoriestotopsuccess';
+                    }
+                    $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a);
                 } else if ($movecount === 1) {
                     $a = new stdClass;
                     $a->moved = $cattomove->get_formatted_name();
                     $a->to = $movetocat->get_formatted_name();
-                    $notificationspass[] = get_string('movecategorysuccess', 'moodle', $a);
+                    $movesuccessstrkey = 'movecategorysuccess';
+                    if ($movetocatid == 0) {
+                        $movesuccessstrkey = 'movecategorytotopsuccess';
+                    }
+                    $notificationspass[] = get_string($movesuccessstrkey, 'moodle', $a);
                 }
             } else if ($bulkresortcategories) {
                 // Bulk resort selected categories.
index 3e6ef68..83b33ce 100644 (file)
@@ -252,8 +252,6 @@ if (file_exists($modmoodleform)) {
     print_error('noformdesc');
 }
 
-include_modulelib($module->name);
-
 $mformclassname = 'mod_'.$module->name.'_mod_form';
 $mform = new $mformclassname($data, $cw->section, $cm, $course);
 $mform->set_data($data);
index efbf6a1..42df746 100644 (file)
@@ -44,6 +44,9 @@ require_once($CFG->dirroot.'/course/lib.php');
 function add_moduleinfo($moduleinfo, $course, $mform = null) {
     global $DB, $CFG;
 
+    // Attempt to include module library before we make any changes to DB.
+    include_modulelib($moduleinfo->modulename);
+
     $moduleinfo->course = $course->id;
     $moduleinfo = set_moduleinfo_defaults($moduleinfo);
 
@@ -51,10 +54,6 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
         $moduleinfo->groupmode = 0; // Do not set groupmode.
     }
 
-    if (!course_allowed_module($course, $moduleinfo->modulename)) {
-        print_error('moduledisable', '', '', $moduleinfo->modulename);
-    }
-
     // First add course_module record because we need the context.
     $newcm = new stdClass();
     $newcm->course           = $course->id;
@@ -62,6 +61,9 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
     $newcm->instance         = 0; // Not known yet, will be updated later (this is similar to restore code).
     $newcm->visible          = $moduleinfo->visible;
     $newcm->visibleold       = $moduleinfo->visible;
+    if (isset($moduleinfo->cmidnumber)) {
+        $newcm->idnumber         = $moduleinfo->cmidnumber;
+    }
     $newcm->groupmode        = $moduleinfo->groupmode;
     $newcm->groupingid       = $moduleinfo->groupingid;
     $newcm->groupmembersonly = $moduleinfo->groupmembersonly;
@@ -87,7 +89,8 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
         print_error('cannotaddcoursemodule');
     }
 
-    if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) {
+    if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true) &&
+            isset($moduleinfo->introeditor)) {
         $introeditor = $moduleinfo->introeditor;
         unset($moduleinfo->introeditor);
         $moduleinfo->intro       = $introeditor['text'];
@@ -103,9 +106,9 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
         $DB->delete_records('course_modules', array('id'=>$moduleinfo->coursemodule));
 
         if (!is_number($returnfromfunc)) {
-            print_error('invalidfunction', '', course_get_url($course, $cw->section));
+            print_error('invalidfunction', '', course_get_url($course, $moduleinfo->section));
         } else {
-            print_error('cannotaddnewmodule', '', course_get_url($course, $cw->section), $moduleinfo->modulename);
+            print_error('cannotaddnewmodule', '', course_get_url($course, $moduleinfo->section), $moduleinfo->modulename);
         }
     }
 
@@ -126,15 +129,6 @@ function add_moduleinfo($moduleinfo, $course, $mform = null) {
     // So we have to update one of them twice.
     $sectionid = course_add_cm_to_section($course, $moduleinfo->coursemodule, $moduleinfo->section);
 
-    // Make sure visibility is set correctly (in particular in calendar).
-    // Note: allow them to set it even without moodle/course:activityvisibility.
-    set_coursemodule_visible($moduleinfo->coursemodule, $moduleinfo->visible);
-
-    if (isset($moduleinfo->cmidnumber)) { // Label.
-        // Set cm idnumber - uniqueness is already verified by form validation.
-        set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber);
-    }
-
     // Set up conditions.
     if ($CFG->enableavailability) {
         condition_info::update_cm_from_form((object)array('id'=>$moduleinfo->coursemodule), $moduleinfo, false);
@@ -177,9 +171,11 @@ function edit_module_post_actions($moduleinfo, $course) {
     global $CFG;
 
     $modcontext = context_module::instance($moduleinfo->coursemodule);
+    $hasgrades = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_HAS_GRADE, false);
+    $hasoutcomes = plugin_supports('mod', $moduleinfo->modulename, FEATURE_GRADE_OUTCOMES, true);
 
     // Sync idnumber with grade_item.
-    if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename,
+    if ($hasgrades && $grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename,
                  'iteminstance'=>$moduleinfo->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) {
         if ($grade_item->idnumber != $moduleinfo->cmidnumber) {
             $grade_item->idnumber = $moduleinfo->cmidnumber;
@@ -187,8 +183,12 @@ function edit_module_post_actions($moduleinfo, $course) {
         }
     }
 
-    $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename,
+    if ($hasgrades) {
+        $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename,
                                          'iteminstance'=>$moduleinfo->instance, 'courseid'=>$course->id));
+    } else {
+        $items = array();
+    }
 
     // Create parent category if requested and move to correct parent category.
     if ($items and isset($moduleinfo->gradecat)) {
@@ -213,7 +213,7 @@ function edit_module_post_actions($moduleinfo, $course) {
     }
 
     // Add outcomes if requested.
-    if ($outcomes = grade_outcome::fetch_all_available($course->id)) {
+    if ($hasoutcomes && $outcomes = grade_outcome::fetch_all_available($course->id)) {
         $grade_items = array();
 
         // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes.
@@ -292,8 +292,10 @@ function edit_module_post_actions($moduleinfo, $course) {
         $moduleinfo->showgradingmanagement = $showgradingmanagement;
     }
 
-    rebuild_course_cache($course->id);
-    grade_regrade_final_grades($course->id);
+    rebuild_course_cache($course->id, true);
+    if ($hasgrades) {
+        grade_regrade_final_grades($course->id);
+    }
     require_once($CFG->libdir.'/plagiarismlib.php');
     plagiarism_save_form_elements($moduleinfo);
 
@@ -308,7 +310,6 @@ function edit_module_post_actions($moduleinfo, $course) {
  * @return object the completed module info
  */
 function set_moduleinfo_defaults($moduleinfo) {
-    global $DB;
 
     if (empty($moduleinfo->coursemodule)) {
         // Add.
@@ -361,6 +362,7 @@ function set_moduleinfo_defaults($moduleinfo) {
  * @param object $modulename the module name
  * @param object $section the section of the module
  * @return array list containing module, context, course section.
+ * @throws moodle_exception if user is not allowed to perform the action or module is not allowed in this course
  */
 function can_add_moduleinfo($course, $modulename, $section) {
     global $DB;
@@ -385,6 +387,7 @@ function can_add_moduleinfo($course, $modulename, $section) {
  *
  * @param object $cm course module
  * @return array - list of course module, context, module, moduleinfo, and course section.
+ * @throws moodle_exception if user is not allowed to perform the action
  */
 function can_update_moduleinfo($cm) {
     global $DB;
@@ -420,6 +423,9 @@ function can_update_moduleinfo($cm) {
 function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
     global $DB, $CFG;
 
+    // Attempt to include module library before we make any changes to DB.
+    include_modulelib($moduleinfo->modulename);
+
     $moduleinfo->course = $course->id;
     $moduleinfo = set_moduleinfo_defaults($moduleinfo);
 
@@ -515,6 +521,7 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
  * Include once the module lib file.
  *
  * @param string $modulename module name of the lib to include
+ * @throws moodle_exception if lib.php file for the module does not exist
  */
 function include_modulelib($modulename) {
     global $CFG;
index c1a464e..a47affe 100644 (file)
@@ -363,7 +363,7 @@ class core_course_renderer extends plugin_renderer_base {
 
         $menu = new action_menu();
         $menu->set_owner_selector($ownerselector);
-        $menu->set_contraint($constraint);
+        $menu->set_constraint($constraint);
         $menu->set_alignment(action_menu::TL, action_menu::TR);
         if (isset($CFG->modeditingmenu) && !$CFG->modeditingmenu || !empty($displayoptions['donotenhance'])) {
             $menu->do_not_enhance();
index 2a80d75..13c97cd 100644 (file)
@@ -974,6 +974,80 @@ class behat_course extends behat_base {
         $node->click();
     }
 
+    /**
+     * Clicks on a category checkbox in the management interface.
+     *
+     * @Given /^I select category "(?P<name>[^"]*)" in the management interface$/
+     * @param string $name
+     */
+    public function i_select_category_in_the_management_interface($name) {
+        $node = $this->get_management_category_listing_node_by_name($name);
+        $node->checkField('bcat[]');
+    }
+
+    /**
+     * Clicks course checkbox in the management interface.
+     *
+     * @Given /^I select course "(?P<name>[^"]*)" in the management interface$/
+     * @param string $name
+     */
+    public function i_select_course_in_the_management_interface($name) {
+        $node = $this->get_management_course_listing_node_by_name($name);
+        $node->checkField('bc[]');
+    }
+
+    /**
+     * Move selected categories to top level in the management interface.
+     *
+     * @Given /^I move category "(?P<idnumber>[^"]*)" to top level in the management interface$/
+     * @param string $idnumber
+     * @return Given[]
+     */
+    public function i_move_category_to_top_level_in_the_management_interface($idnumber) {
+        $id = $this->get_category_id($idnumber);
+        $selector = sprintf('.listitem-category[data-id="%d"] > div', $id);
+        $node = $this->find('css', $selector);
+        $node->checkField('bcat[]');
+        return array(
+            new Given('I select "' .  coursecat::get(0)->get_formatted_name() . '" from "menumovecategoriesto"'),
+            new Given('I press "bulkmovecategories"'),
+        );
+    }
+
+    /**
+     * Checks that a category is a subcategory of specific category.
+     *
+     * @Given /^I should see category "(?P<subcatidnumber>[^"]*)" as subcategory of "(?P<catidnumber>[^"]*)" in the management interface$/
+     * @throws ExpectationException
+     * @param string $subcatidnumber
+     * @param string $catidnumber
+     */
+    public function i_should_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber) {
+        $categorynodeid = $this->get_category_id($catidnumber);
+        $subcategoryid = $this->get_category_id($subcatidnumber);
+        $exception = new ExpectationException('The category '.$subcatidnumber.' is not a subcategory of '.$catidnumber, $this->getSession());
+        $selector = sprintf('#category-listing .listitem-category[data-id="%d"] .listitem-category[data-id="%d"]', $categorynodeid, $subcategoryid);
+        $this->find('css', $selector, $exception);
+    }
+
+    /**
+     * Checks that a category is not a subcategory of specific category.
+     *
+     * @Given /^I should not see category "(?P<subcatidnumber>[^"]*)" as subcategory of "(?P<catidnumber>[^"]*)" in the management interface$/
+     * @throws ExpectationException
+     * @param string $subcatidnumber
+     * @param string $catidnumber
+     */
+    public function i_should_not_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber) {
+        try {
+            $this->i_should_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber);
+        } catch (ExpectationException $e) {
+            // ExpectedException means that it is not highlighted.
+            return;
+        }
+        throw new ExpectationException('The category '.$subcatidnumber.' is a subcategory of '.$catidnumber, $this->getSession());
+    }
+
     /**
      * Click to expand a category revealing its sub categories within the management UI.
      *
@@ -1261,15 +1335,13 @@ class behat_course extends behat_base {
             throw new ExpectationException("Could not find the actions for $listingtype", $this->getSession());
         }
         $actionnode = $actionsnode->find('css', '.action-'.$action);
-        if ($this->running_javascript() && !$actionnode->isVisible()) {
-            $actionsnode->find('css', 'a.toggle-display')->click();
-            if ($actionnode) {
-                $actionnode = $listingnode->find('css', '.action-'.$action);
-            }
-        }
         if (!$actionnode) {
             throw new ExpectationException("Expected action was not available or not found ($action)", $this->getSession());
         }
+        if ($this->running_javascript() && !$actionnode->isVisible()) {
+            $actionsnode->find('css', 'a.toggle-display')->click();
+            $actionnode = $actionsnode->find('css', '.action-'.$action);
+        }
         $actionnode->click();
     }
 }
index 5dbc4f9..f435dce 100644 (file)
@@ -5,6 +5,7 @@ Feature: Test category management actions
   Test we can create a sub category
   Test we can edit a category
   Test we can delete a category
+  Test we can move a category
   Test we can assign roles within a category
   Test we can set permissions on a category
   Test we can manage cohorts within a category
@@ -210,3 +211,29 @@ Feature: Test category management actions
     And I should see category listing "Cat 1" before "Test category 2"
     And I should see "No courses in this category"
 
+  @javascript
+  Scenario: Test moving a categories through the management interface.
+    Given the following "categories" exists:
+      | name | category | idnumber |
+      | Cat 1 | 0 | CAT1 |
+      | Cat 2 | 0 | CAT2 |
+      | Cat 3 | 0 | CAT3 |
+
+    And I log in as "admin"
+    And I go to the courses management page
+    And I should see the "Course categories" management page
+    And I should see "Cat 1" in the "#category-listing ul.ml" "css_element"
+    And I should see "Cat 2" in the "#category-listing ul.ml" "css_element"
+    And I should see "Cat 3" in the "#category-listing ul.ml" "css_element"
+    And I select category "Cat 2" in the management interface
+    And I select category "Cat 3" in the management interface
+    And I select "Cat 1" from "menumovecategoriesto"
+    When I press "bulkmovecategories"
+    # Redirect
+    And I click on category "Cat 1" in the management interface
+    # Redirect
+    Then I should see category "CAT3" as subcategory of "CAT1" in the management interface
+    And I move category "CAT3" to top level in the management interface
+    # Redirect
+    And I should not see category "CAT3" as subcategory of "CAT1" in the management interface
+    Then I should see category "CAT2" as subcategory of "CAT1" in the management interface
index 147617f..ef6b7f8 100644 (file)
@@ -16,8 +16,8 @@ Feature: Test we can resort categories in the management interface.
     And I go to the courses management page
     And I should see the "Course categories" management page
     And I should see "Re-sort categories" in the ".category-listing-actions" "css_element"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by name" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by idnumber" in the ".category-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories" management page
@@ -27,8 +27,8 @@ Feature: Test we can resort categories in the management interface.
   Examples:
     | sortby | cat1 | cat2 | cat3 |
     | "Re-sort categories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Re-sort the top level categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Re-sort the top level categories by idnumber"   | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   @javascript
   Scenario Outline: Test resorting categories with JS enabled.
@@ -45,8 +45,8 @@ Feature: Test we can resort categories in the management interface.
     And I should not see "By name" in the ".category-listing-actions" "css_element"
     And I should not see "By idnumber" in the ".category-listing-actions" "css_element"
     And I click on "Re-sort categories" "link"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by name" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by idnumber" in the ".category-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories" management page
@@ -56,8 +56,8 @@ Feature: Test we can resort categories in the management interface.
   Examples:
     | sortby | cat1 | cat2 | cat3 |
     | "Re-sort categories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Re-sort the top level categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Re-sort the top level categories by idnumber"   | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   Scenario Outline: Test resorting subcategories.
     Given the following "categories" exists:
@@ -73,10 +73,7 @@ Feature: Test we can resort categories in the management interface.
     And I click on "Master cat" "link"
     # Redirect.
     And I should see the "Course categories and courses" management page
-    And I should see "Re-sort subcategories" in the ".category-listing-actions" "css_element"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
-    And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
+    And I click on <sortby> action for "Master cat" in management category listing
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see category listing <cat1> before <cat2>
@@ -84,9 +81,8 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Re-sort subcategories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   @javascript
   Scenario Outline: Test resorting subcategories with JS enabled.
@@ -103,13 +99,7 @@ Feature: Test we can resort categories in the management interface.
     And I click on "Master cat" "link"
   # Redirect.
     And I should see the "Course categories and courses" management page
-    And I should see "Re-sort subcategories" in the ".category-listing-actions" "css_element"
-    And I should not see "By name" in the ".category-listing-actions" "css_element"
-    And I should not see "By idnumber" in the ".category-listing-actions" "css_element"
-    And I click on "Re-sort subcategories" "link"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
-    And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
+    And I click on <sortby> action for "Master cat" in management category listing
   # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see category listing <cat1> before <cat2>
@@ -117,9 +107,8 @@ Feature: Test we can resort categories in the management interface.
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Re-sort subcategories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   # The scenario below this is the same but with JS enabled.
   Scenario: Test moving categories up and down by one.
index e9edfda..6dc9774 100644 (file)
@@ -246,8 +246,8 @@ Feature: Course category management interface performs as expected
     And I go to the courses management page
     And I should see the "Course categories" management page
     And I click on "Re-sort categories" "link"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by name" in the ".category-listing-actions" "css_element"
+    And I should see "Re-sort the top level categories by idnumber" in the ".category-listing-actions" "css_element"
     And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
     # Redirect.
     And I should see the "Course categories" management page
@@ -257,8 +257,8 @@ Feature: Course category management interface performs as expected
   Examples:
     | sortby | cat1 | cat2 | cat3 |
     | "Re-sort categories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "Re-sort the top level categories by name"       | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "Re-sort the top level categories by idnumber"   | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   @javascript
   Scenario Outline: Sub categories are displayed correctly when resorted
@@ -275,10 +275,7 @@ Feature: Course category management interface performs as expected
     And I click on "Master cat" "link"
     # Redirect.
     And I should see the "Course categories and courses" management page
-    And I click on "Re-sort subcategories" "link"
-    And I should see "By name" in the ".category-listing-actions" "css_element"
-    And I should see "By idnumber" in the ".category-listing-actions" "css_element"
-    And I click on <sortby> "link" in the ".category-listing-actions" "css_element"
+    And I click on <sortby> action for "Master cat" in management category listing
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see category listing <cat1> before <cat2>
@@ -286,9 +283,8 @@ Feature: Course category management interface performs as expected
 
   Examples:
     | sortby | cat1 | cat2 | cat3 |
-    | "Re-sort subcategories" | "Social studies"          | "Applied sciences"        | "Extended social studies" |
-    | "By name"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
-    | "By idnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
+    | "resortbyname"            | "Applied sciences"        | "Extended social studies" | "Social studies" |
+    | "resortbyidnumber"        | "Extended social studies" | "Social studies" | "Applied sciences" |
 
   @javascript
   Scenario Outline: Test courses are displayed correctly after being resorted.
@@ -745,8 +741,7 @@ Feature: Course category management interface performs as expected
     And I should see "Cat 2-1-2" in the "#course-category-listings ul.ml" "css_element"
     And I should not see "Cat 2-1-1-1" in the "#course-category-listings ul.ml" "css_element"
     And I should see "Cat 2-1-2-1" in the "#course-category-listings ul.ml" "css_element"
-    And I click on "Re-sort subcategories" "link" in the ".category-listing-actions" "css_element"
-    And I click on "By idnumber" "link" in the ".category-listing-actions" "css_element"
+    And I click on "resortbyidnumber" action for "Cat 1" in management category listing
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see "Cat 1" in the "#course-category-listings ul.ml" "css_element"
@@ -798,4 +793,4 @@ Feature: Course category management interface performs as expected
     # Redirect.
     And I should see the "Course categories and courses" management page
     And I should see "Cat A (1)" in the "#course-category-listings ul.ml" "css_element"
-    And I should not see "Cat B (2)" in the "#course-category-listings ul.ml" "css_element"
\ No newline at end of file
+    And I should not see "Cat B (2)" in the "#course-category-listings ul.ml" "css_element"
index 2971198..8f18f7d 100644 (file)
@@ -873,6 +873,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
 
 },
 '@VERSION@', {
-    requires : ['base', 'node', 'io', 'moodle-course-coursebase']
+    requires : ['base', 'node', 'io', 'moodle-course-coursebase', 'moodle-course-util']
 }
 );
index bfbe870..3dc8449 100644 (file)
--- a/file.php
+++ b/file.php
@@ -36,12 +36,6 @@ define('NO_DEBUG_DISPLAY', true);
 require_once('config.php');
 require_once('lib/filelib.php');
 
-if (!isset($CFG->filelifetime)) {
-    $lifetime = 86400;     // Seconds for files to remain in caches
-} else {
-    $lifetime = $CFG->filelifetime;
-}
-
 $relativepath  = get_file_argument();
 $forcedownload = optional_param('forcedownload', 0, PARAM_BOOL);
 
@@ -112,6 +106,6 @@ if ($file->get_filename() == '.') {
 // finally send the file
 // ========================================
 \core\session\manager::write_close(); // Unlock session during file serving.
-send_stored_file($file, $lifetime, $CFG->filteruploadedfiles, $forcedownload);
+send_stored_file($file, null, $CFG->filteruploadedfiles, $forcedownload);
 
 
index 4b2e8c8..6c97879 100644 (file)
@@ -201,9 +201,9 @@ class core_files_renderer extends plugin_renderer_base {
     <div class="fp-navbar">
         <div class="filemanager-toolbar">
             <div class="fp-toolbar">
-                <div class="{!}fp-btn-add"><a role="button" href="#"><img src="'.$this->pix_url('a/add_file').'" alt="'.$straddfile.'" /></a></div>
-                <div class="{!}fp-btn-mkdir"><a role="button" href="#"><img src="'.$this->pix_url('a/create_folder').'" alt="'.$strmakedir.'" /></a></div>
-                <div class="{!}fp-btn-download"><a role="button" href="#"><img src="'.$this->pix_url('a/download_all').'" alt="'.$strdownload.'" /></a></div>
+                <div class="{!}fp-btn-add"><a role="button" title="'.$straddfile.'" href="#"><img src="'.$this->pix_url('a/add_file').'" alt="" /></a></div>
+                <div class="{!}fp-btn-mkdir"><a role="button" title="'.$strmakedir.'" href="#"><img src="'.$this->pix_url('a/create_folder').'" alt="" /></a></div>
+                <div class="{!}fp-btn-download"><a role="button" title="'.$strdownload.'" href="#"><img src="'.$this->pix_url('a/download_all').'" alt="" /></a></div>
             </div>
             <div class="{!}fp-viewbar">
                 <a title="'. get_string('displayicons', 'repository') .'" class="{!}fp-vb-icons" href="#"><img alt="" src="'. $this->pix_url('fp/view_icon_active', 'theme') .'" /></a>
@@ -534,10 +534,10 @@ class core_files_renderer extends plugin_renderer_base {
                 <div class="{!}fp-toolbar">
                     <div class="{!}fp-tb-back"><a href="#">'.get_string('back', 'repository').'</a></div>
                     <div class="{!}fp-tb-search"><form></form></div>
-                    <div class="{!}fp-tb-refresh"><a href="#"><img alt="'. get_string('refresh', 'repository') .'"  src="'.$this->pix_url('a/refresh').'" /></a></div>
-                    <div class="{!}fp-tb-logout"><img alt="'. get_string('logout', 'repository') .'" src="'.$this->pix_url('a/logout').'" /><a href="#"></a></div>
-                    <div class="{!}fp-tb-manage"><a href="#"><img alt="'. get_string('settings', 'repository') .'" src="'.$this->pix_url('a/setting').'" /></a></div>
-                    <div class="{!}fp-tb-help"><a href="#"><img alt="'. get_string('help', 'repository') .'" src="'.$this->pix_url('a/help').'" /></a></div>
+                    <div class="{!}fp-tb-refresh"><a title="'. get_string('refresh', 'repository') .'" href="#"><img alt=""  src="'.$this->pix_url('a/refresh').'" /></a></div>
+                    <div class="{!}fp-tb-logout"><a title="'. get_string('logout', 'repository') .'" href="#"><img alt="" src="'.$this->pix_url('a/logout').'" /></a></div>
+                    <div class="{!}fp-tb-manage"><a title="'. get_string('settings', 'repository') .'" href="#"><img alt="" src="'.$this->pix_url('a/setting').'" /></a></div>
+                    <div class="{!}fp-tb-help"><a title="'. get_string('help', 'repository') .'" href="#"><img alt="" src="'.$this->pix_url('a/help').'" /></a></div>
                     <div class="{!}fp-tb-message"></div>
                 </div>
                 <div class="{!}fp-viewbar">
index 161b816..314db1e 100644 (file)
@@ -18,8 +18,8 @@ Feature: Course files
     Then I should see "Legacy course files"
     And I follow "Legacy course files"
     And I press "Edit legacy course files"
-    And I should see "Add..."
-    And I should see "Create folder"
+    And "Add..." "link" should be visible
+    And "Create folder" "link" should be visible
 
   @javascript
   Scenario: Add legacy file disabled
@@ -35,5 +35,5 @@ Feature: Course files
     Then I should see "Legacy course files"
     And I follow "Legacy course files"
     And I press "Edit legacy course files"
-    And I should not see "Add..."
-    And I should not see "Create folder"
+    And "Add..." "link" should not be visible
+    And "Create folder" "link" should not be visible
index 5c57e2c..2f4283d 100644 (file)
@@ -134,6 +134,11 @@ class filter_glossary extends moodle_text_filter {
                 foreach ($concepts as $key => $concept) {
                     // Trim empty or unlinkable concepts
                     $currentconcept = trim(strip_tags($concept->concept));
+
+                    // Concept must be HTML-escaped, so do the same as format_string
+                    // to turn ampersands into &amp;.
+                    $currentconcept = replace_ampersands_not_followed_by_entity($currentconcept);
+
                     if (empty($currentconcept)) {
                         unset($concepts[$key]);
                         continue;
@@ -171,10 +176,14 @@ class filter_glossary extends moodle_text_filter {
                                       '&amp;mode=cat&amp;hook='.$concept->id.'">';
                 } else { // Link to entry or alias
                     if (!empty($concept->originalconcept)) {  // We are dealing with an alias (so show and point to original)
-                        $title = str_replace('"', "'", strip_tags($glossaryname.': '.$concept->originalconcept));
+                        $title = str_replace('"', "'", html_entity_decode(
+                                strip_tags($glossaryname.': '.$concept->originalconcept)));
                         $concept->id = $concept->entryid;
                     } else { // This is an entry
-                        $title = str_replace('"', "'", strip_tags($glossaryname.': '.$concept->concept));
+                        // We need to remove entities from the content here because it
+                        // will be escaped by html_writer below.
+                        $title = str_replace('"', "'", html_entity_decode(
+                                strip_tags($glossaryname.': '.$concept->concept)));
                     }
                     // hardcoding dictionary format in the URL rather than defaulting
                     // to the current glossary format which may not work in a popup.
diff --git a/filter/glossary/tests/filter_test.php b/filter/glossary/tests/filter_test.php
new file mode 100644 (file)
index 0000000..c8f3966
--- /dev/null
@@ -0,0 +1,80 @@
+<?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/>.
+
+/**
+ * Unit tests.
+ *
+ * @package filter_glossary
+ * @category test
+ * @copyright 2013 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/filter/glossary/filter.php'); // Include the code to test.
+
+/**
+ * Test case for glossary.
+ */
+class filter_glossary_filter_testcase extends advanced_testcase {
+
+    /**
+     * Test ampersands.
+     */
+    public function test_ampersands() {
+        global $CFG;
+        $this->resetAfterTest(true);
+
+        // Enable glossary filter at top level.
+        filter_set_global_state('glossary', TEXTFILTER_ON);
+        $CFG->glossary_linkentries = 1;
+
+        // Create a test course.
+        $course = $this->getDataGenerator()->create_course();
+        $context = context_course::instance($course->id);
+
+        // Create a glossary.
+        $glossary = $this->getDataGenerator()->create_module('glossary',
+                array('course' => $course->id, 'mainglossary' => 1));
+
+        // Create two entries with ampersands and one normal entry.
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_glossary');
+        $normal = $generator->create_content($glossary, array('concept' => 'normal'));
+        $amp1 = $generator->create_content($glossary, array('concept' => 'A&B'));
+        $amp2 = $generator->create_content($glossary, array('concept' => 'C&amp;D'));
+
+        // Format text with all three entries in HTML.
+        $html = '<p>A&amp;B C&amp;D normal</p>';
+        $filtered = format_text($html, FORMAT_HTML, array('context' => $context));
+
+        // Find all the glossary links in the result.
+        $matches = array();
+        preg_match_all('~courseid=' . $course->id . '&amp;eid=([0-9]+).*?title="(.*?)"~', $filtered, $matches);
+
+        // There should be 3 glossary links.
+        $this->assertEquals(3, count($matches[1]));
+        $this->assertEquals($amp1->id, $matches[1][0]);
+        $this->assertEquals($amp2->id, $matches[1][1]);
+        $this->assertEquals($normal->id, $matches[1][2]);
+
+        // Check text and escaping of title attribute.
+        $this->assertEquals($glossary->name . ': A&amp;B', $matches[2][0]);
+        $this->assertEquals($glossary->name . ': C&amp;D', $matches[2][1]);
+        $this->assertEquals($glossary->name . ': normal', $matches[2][2]);
+    }
+}
index 6e21e46..5062056 100644 (file)
@@ -33,6 +33,7 @@ $action   = optional_param('action', '', PARAM_ALPHA);
 $scope    = optional_param('scope', 'custom', PARAM_ALPHA);
 
 $PAGE->set_url('/grade/edit/outcome/import.php', array('courseid' => $courseid));
+$PAGE->set_pagelayout('admin');
 
 /// Make sure they can even access this course
 if ($courseid) {
index 163e0f0..46ec518 100644 (file)
@@ -38,7 +38,7 @@ class import_outcomes_form extends moodleform {
         $mform->addElement('hidden', 'action', 'upload');
         $mform->setType('action', PARAM_ALPHANUMEXT);
         $mform->addElement('hidden', 'courseid', $PAGE->course->id);
-        $mform->setType('id', PARAM_INT);
+        $mform->setType('courseid', PARAM_INT);
 
         $scope = array();
         if (($PAGE->course->id > 1) && has_capability('moodle/grade:manage', context_system::instance())) {
similarity index 53%
rename from theme/mymobile/version.php
rename to install/lang/ckb/moodle.php
index 2e8d230..7507e8d 100644 (file)
@@ -1,4 +1,5 @@
 <?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/>.
 
 /**
- * version for mymobile theme
+ * 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    theme_mymobile
- * @copyright  John Stabinger
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-defined('MOODLE_INTERNAL') || die;
+defined('MOODLE_INTERNAL') || die();
 
-$plugin->version = 2013050100;              // The current module version (Date: YYYYMMDDXX)
-$plugin->requires = 2013050100;             // Requires this Moodle version
-$plugin->component = 'theme_mymobile';
-$plugin->maturity = MATURITY_STABLE;
-$plugin->dependencies = array(
-    'theme_canvas'  => 2013050100,
-);
+$string['language'] = 'زمان';
+$string['reload'] = 'بارکردنەوە';
index e90362c..2ecc8bf 100644 (file)
@@ -31,7 +31,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['admindirname'] = 'Admin-map';
-$string['availablelangs'] = 'Lijst met beschikbare talen';
+$string['availablelangs'] = 'Beschikbare taalpakketten';
 $string['chooselanguagehead'] = 'Kies een taal';
 $string['chooselanguagesub'] = 'Kies een taal voor de installatie. Deze taal zal ook als standaardtaal voor de site gebruikt worden, maar die instelling kun je later nog wijzigen.';
 $string['clialreadyconfigured'] = 'Bestand config.php bestaat al, maak aub gebruik van admin/cli/install_database.php indien je deze site wenst te installeren.';
index a97ebb5..461edde 100644 (file)
@@ -46,6 +46,7 @@ $string['cachedef_coursemodinfo'] = 'Accumulated information about modules and s
 $string['cachedef_databasemeta'] = 'Database meta information';
 $string['cachedef_eventinvalidation'] = 'Event invalidation';
 $string['cachedef_externalbadges'] = 'External badges for particular user';
+$string['cachedef_gradecondition'] = 'User grades cached for evaluating conditional availability';
 $string['cachedef_groupdata'] = 'Course group information';
 $string['cachedef_htmlpurifier'] = 'HTML Purifier - cleaned content';
 $string['cachedef_langmenu'] = 'List of available languages';
index 684ffbd..7ea4073 100644 (file)
@@ -1145,6 +1145,8 @@ $string['movecontent'] = 'Move {$a}';
 $string['movecategorycontentto'] = 'Move into';
 $string['movecategorysuccess'] = 'Successfully moved category \'{$a->moved}\' into category \'{$a->to}\'';
 $string['movecategoriessuccess'] = 'Successfully moved {$a->count} categories into category \'{$a->to}\'';
+$string['movecategorytotopsuccess'] = 'Successfully moved category \'{$a->moved}\' to top level';
+$string['movecategoriestotopsuccess'] = 'Successfully moved {$a->count} categories to top level';
 $string['movecategoryto'] = 'Move category to:';
 $string['movecontentstoanothercategory'] = 'Move contents to another category';
 $string['movecourseto'] = 'Move course to:';
@@ -1470,7 +1472,10 @@ $string['resetstatus'] = 'Status';
 $string['resettask'] = 'Task';
 $string['resettodefaults'] = 'Reset to defaults';
 $string['resortcategories'] = 'Re-sort categories';
-$string['resortsubcategories'] = 'Re-sort subcategories';
+$string['resortcategoriesbyname'] = 'Re-sort the top level categories by name';
+$string['resortcategoriesbyidnumber'] = 'Re-sort the top level categories by idnumber';
+$string['resortsubcategoriesbyname'] = 'Re-sort subcategories by name';
+$string['resortsubcategoriesbyidnumber'] = 'Re-sort subcategories by idnumber';
 $string['resortcourses'] = 'Re-sort courses';
 $string['resortcoursesbyname'] = 'Re-sort courses by name';
 $string['resortbyname'] = 'By name';
@@ -1664,6 +1669,7 @@ $string['skypeid'] = 'Skype ID';
 $string['socialheadline'] = 'Social forum - latest topics';
 $string['someallowguest'] = 'Some courses may allow guest access';
 $string['someerrorswerefound'] = 'Some information was missing or incorrect. Look below for details.';
+$string['sort'] = 'Sort';
 $string['sortby'] = 'Sort by';
 $string['sortbyx'] = 'Sort by {$a} ascending';
 $string['sortbyxreverse'] = 'Sort by {$a} descending';
index 7ba305e..4e1518a 100644 (file)
@@ -55,7 +55,7 @@ class blog_entry_created extends \core\event\base {
      *
      * @param \blog_entry $data A reference to the active blog_entry object.
      */
-    public function set_custom_data($data) {
+    public function set_custom_data(\blog_entry $data) {
         $this->customobject = $data;
     }
 
@@ -74,7 +74,7 @@ class blog_entry_created extends \core\event\base {
      * @return string
      */
     public function get_description() {
-        return 'Blog entry "'. $this->other['subject']. '" was created by user with id '. $this->userid;
+        return 'Blog entry id '. $this->objectid. ' was created by userid '. $this->userid;
     }
 
     /**
index b632082..b7275d5 100644 (file)
@@ -63,7 +63,7 @@ class blog_entry_deleted extends \core\event\base {
      *
      * @param \blog_entry $data A reference to the active blog_entry object.
      */
-    public function set_custom_data($data) {
+    public function set_custom_data(\blog_entry $data) {
         $this->customobject = $data;
     }
 
@@ -73,7 +73,7 @@ class blog_entry_deleted extends \core\event\base {
      * @return string
      */
     public function get_description() {
-        return "Blog entry ".$this->other['record']['subject']." was deleted by user with id ".$this->userid;
+        return 'Blog entry id '. $this->objectid. ' was deleted by userid '. $this->userid;
     }
 
     /**
index 5859f4b..777ac2e 100644 (file)
@@ -53,7 +53,7 @@ class blog_entry_updated extends base {
      *
      * @param \blog_entry $data A reference to the active blog_entry object.
      */
-    public function set_custom_data($data) {
+    public function set_custom_data(\blog_entry $data) {
         $this->customobject = $data;
     }
 
@@ -72,7 +72,7 @@ class blog_entry_updated extends base {
      * @return string
      */
     public function get_description() {
-        return 'User with id {$this->userid} updated blog entry {$this->other["subject"]';
+        return 'Blog entry id '. $this->objectid. ' was updated by userid '. $this->userid;
     }
 
     /**
@@ -108,7 +108,7 @@ class blog_entry_updated extends base {
      */
     protected function get_legacy_logdata() {
         return array(SITEID, 'blog', 'update', 'index.php?userid=' . $this->relateduserid . '&entryid=' . $this->objectid,
-                 $this->other['subject']);
+                 $this->customobject->subject);
     }
 }
 
index 4c03cf0..7492bd1 100644 (file)
@@ -87,7 +87,7 @@ abstract class content_viewed extends base {
      * @return string
      */
     public function get_description() {
-        return 'User with id ' . $this->userid . ' viewed content ' . $this->get_url();
+        return 'User with id ' . $this->userid . ' viewed content';
     }
 
     /**
index b77219a..6e42b60 100644 (file)
@@ -60,7 +60,7 @@ class course_completed extends base {
      * @return \moodle_url
      */
     public function get_url() {
-        return new moodle_url('/report/completion/index.php', array('course' => $this->courseid));
+        return new \moodle_url('/report/completion/index.php', array('course' => $this->courseid));
     }
 
     /**
index 1637054..36a3d80 100644 (file)
@@ -67,7 +67,7 @@ class course_completion_updated extends base {
      * @return \moodle_url
      */
     public function get_url() {
-        return new moodle_url('/course/completion.php', array('id' => $this->courseid));
+        return new \moodle_url('/course/completion.php', array('id' => $this->courseid));
     }
 
     /**
index 72c0276..7436e83 100644 (file)
@@ -60,7 +60,7 @@ class course_module_completion_updated extends base {
      * @return \moodle_url
      */
     public function get_url() {
-        return new moodle_url('/report/completion/index.php', array('course' => $this->courseid));
+        return new \moodle_url('/report/completion/index.php', array('course' => $this->courseid));
     }
 
     /**
index 442c02e..9ed6fc8 100644 (file)
@@ -60,8 +60,8 @@ class course_module_created extends base {
      * @return string
      */
     public function get_description() {
-        return 'The '. $this->other['modulename'] . ' module ' . $this->other['name']. ' was created by user with id '.
-               $this->userid;
+        return 'The '. $this->other['modulename'] . ' module with instance id ' . $this->other['instanceid'] .
+                ' was created by user with id ' . $this->userid;
     }
 
     /**
index e6a7b19..ced4210 100644 (file)
@@ -60,8 +60,8 @@ class course_module_updated extends base {
      * @return string
      */
     public function get_description() {
-        return 'The ' . $this->other['modulename'] . ' module ' . $this->other['name']. ' was updated by user with id '.
-               $this->userid;
+        return 'The ' . $this->other['modulename'] . ' module with instance id ' . $this->other['instanceid'] .
+                ' was updated by user with id ' . $this->userid;
     }
 
     /**
index fc9e9a8..5213900 100644 (file)
@@ -56,7 +56,7 @@ class role_assigned extends base {
      * @return \moodle_url
      */
     public function get_url() {
-        return new moodle_url('/admin/roles/assign.php', array('contextid' => $this->contextid, 'roleid' => $this->objectid));
+        return new \moodle_url('/admin/roles/assign.php', array('contextid' => $this->contextid, 'roleid' => $this->objectid));
     }
 
     /**
index 035e42a..2e5a986 100644 (file)
@@ -56,7 +56,7 @@ class role_unassigned extends base {
      * @return \moodle_url
      */
     public function get_url() {
-        return new moodle_url('/admin/roles/assign.php', array('contextid' => $this->contextid, 'roleid' => $this->objectid));
+        return new \moodle_url('/admin/roles/assign.php', array('contextid' => $this->contextid, 'roleid' => $this->objectid));
     }
 
     /**
index 9457519..e04ee16 100644 (file)
@@ -59,7 +59,7 @@ class user_deleted extends base {
      */
     public function get_description() {
         $user = $this->get_record_snapshot('user', $this->data['objectid']);
-        return 'User profile deleted for user '.$user->firstname.' '.$user->lastname.' id ('.$user->id.')';
+        return 'User profile deleted for userid ' . $user->id;
     }
 
     /**
index d45ca6d..064ccf5 100644 (file)
@@ -40,7 +40,7 @@ class user_enrolment_created extends base {
     protected function init() {
         $this->data['objecttable'] = 'user_enrolments';
         $this->data['crud'] = 'c';
-        $this->data['level'] = self::LEVEL_TEACHING;
+        $this->data['level'] = self::LEVEL_OTHER;
     }
 
     /**
index 82e523f..8992347 100644 (file)
@@ -40,7 +40,7 @@ class user_enrolment_deleted extends base {
     protected function init() {
         $this->data['objecttable'] = 'user_enrolments';
         $this->data['crud'] = 'd';
-        $this->data['level'] = self::LEVEL_TEACHING;
+        $this->data['level'] = self::LEVEL_OTHER;
     }
 
     /**
index 786f519..8e53236 100644 (file)
@@ -40,7 +40,7 @@ class user_enrolment_updated extends base {
     protected function init() {
         $this->data['objecttable'] = 'user_enrolments';
         $this->data['crud'] = 'u';
-        $this->data['level'] = self::LEVEL_TEACHING;
+        $this->data['level'] = self::LEVEL_OTHER;
     }
 
     /**
index 9ad0ca4..b5ca099 100644 (file)
@@ -356,6 +356,13 @@ class core_plugin_manager {
 
         $types = core_component::get_plugin_types();
 
+        if (!isset($types[$type])) {
+            // Orphaned subplugins!
+            $plugintypeclass = self::resolve_plugininfo_class($type);
+            $this->pluginsinfo[$type] = $plugintypeclass::get_plugins($type, null, $plugintypeclass);
+            return $this->pluginsinfo[$type];
+        }
+
         /** @var \core\plugininfo\base $plugintypeclass */
         $plugintypeclass = self::resolve_plugininfo_class($type);
         $plugins = $plugintypeclass::get_plugins($type, $types[$type], $plugintypeclass);
@@ -386,6 +393,14 @@ class core_plugin_manager {
         foreach ($plugintypes as $plugintype => $plugintyperootdir) {
             $this->pluginsinfo[$plugintype] = null;
         }
+
+        // Add orphaned subplugin types.
+        $this->load_installed_plugins();
+        foreach ($this->installedplugins as $plugintype => $unused) {
+            if (!isset($plugintypes[$plugintype])) {
+                $this->pluginsinfo[$plugintype] = null;
+            }
+        }
     }
 
     /**
@@ -395,6 +410,11 @@ class core_plugin_manager {
      * @return string name of pluginfo class for give plugin type
      */
     public static function resolve_plugininfo_class($type) {
+        $plugintypes = core_component::get_plugin_types();
+        if (!isset($plugintypes[$type])) {
+            return '\core\plugininfo\orphaned';
+        }
+
         $parent = core_component::get_subtype_parent($type);
 
         if ($parent) {
@@ -604,9 +624,9 @@ class core_plugin_manager {
      */
     public function get_plugin_info($component) {
         list($type, $name) = core_component::normalize_component($component);
-        $plugins = $this->get_plugins();
-        if (isset($plugins[$type][$name])) {
-            return $plugins[$type][$name];
+        $plugins = $this->get_plugins_of_type($type);
+        if (isset($plugins[$name])) {
+            return $plugins[$name];
         } else {
             return null;
         }
@@ -1083,7 +1103,7 @@ class core_plugin_manager {
             'theme' => array(
                 'afterburner', 'anomaly', 'arialist', 'base', 'binarius', 'bootstrapbase',
                 'boxxie', 'brick', 'canvas', 'clean', 'formal_white', 'formfactor',
-                'fusion', 'leatherbound', 'magazine', 'mymobile', 'nimble',
+                'fusion', 'leatherbound', 'magazine', 'nimble',
                 'nonzero', 'overlay', 'serenity', 'sky_high', 'splash',
                 'standard', 'standardold'
             ),
diff --git a/lib/classes/plugininfo/orphaned.php b/lib/classes/plugininfo/orphaned.php
new file mode 100644 (file)
index 0000000..ec78d39
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines class used for orphaned subplugins.
+ *
+ * @package    core
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace core\plugininfo;
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Orphaned subplugins class.
+ */
+class orphaned extends base {
+    public function is_uninstall_allowed() {
+        return true;
+    }
+
+    /**
+     * We do not know if orphaned subplugins are enabled.
+     * @return bool
+     */
+    public function is_enabled() {
+        return null;
+    }
+
+    /**
+     * No lang strings are present.
+     */
+    public function init_display_name() {
+        $this->displayname = $this->component;
+    }
+
+    /**
+     * Oprhaned plugins can not be enabled.
+     * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown
+     */
+    public static function get_enabled_plugins() {
+        return null;
+    }
+
+    /**
+     * Gathers and returns the information about all plugins of the given type,
+     * either on disk or previously installed.
+     *
+     * @param string $type the name of the plugintype, eg. mod, auth or workshopform
+     * @param string $typerootdir full path to the location of the plugin dir
+     * @param string $typeclass the name of the actually called class
+     * @return array of plugintype classes, indexed by the plugin name
+     */
+    public static function get_plugins($type, $typerootdir, $typeclass) {
+        $return = array();
+        $manager = \core_plugin_manager::instance();
+        $plugins = $manager->get_installed_plugins($type);
+
+        foreach ($plugins as $name => $version) {
+            $plugin              = new $typeclass();
+            $plugin->type        = $type;
+            $plugin->typerootdir = $typerootdir;
+            $plugin->name        = $name;
+            $plugin->rootdir     = null;
+            $plugin->displayname = $name;
+            $plugin->versiondb   = $version;
+            $plugin->init_is_standard();
+
+            $return[$name] = $plugin;
+        }
+
+        return $return;
+    }
+}
index f253c43..620475f 100644 (file)
@@ -820,6 +820,7 @@ abstract class condition_info_base {
                 $a = new stdclass;
                 // Display the fieldname into current lang.
                 if (is_numeric($field)) {
+                    // Is a custom profile field (will use multilang).
                     $translatedfieldname = $details->fieldname;
                 } else {
                     $translatedfieldname = get_user_field_name($details->fieldname);
@@ -1044,8 +1045,15 @@ abstract class condition_info_base {
                 if (!$this->is_field_condition_met($details->operator, $uservalue, $details->value)) {
                     // Set available to false
                     $available = false;
+                    // Display the fieldname into current lang.
+                    if (is_numeric($field)) {
+                        // Is a custom profile field (will use multilang).
+                        $translatedfieldname = $details->fieldname;
+                    } else {
+                        $translatedfieldname = get_user_field_name($details->fieldname);
+                    }
                     $a = new stdClass();
-                    $a->field = format_string($details->fieldname, true, array('context' => $context));
+                    $a->field = format_string($translatedfieldname, true, array('context' => $context));
                     $a->value = s($details->value);
                     $information .= html_writer::start_tag('li');
                     $information .= get_string('requires_user_field_'.$details->operator, 'condition', $a) . ' ';
@@ -1151,7 +1159,6 @@ abstract class condition_info_base {
      *
      * @global stdClass $USER
      * @global moodle_database $DB
-     * @global stdClass $SESSION
      * @param int $gradeitemid Grade item ID we're interested in
      * @param bool $grabthelot If true, grabs all scores for current user on
      *   this course, so that later ones come from cache
@@ -1161,71 +1168,73 @@ abstract class condition_info_base {
      *   or 37.21), or false if user does not have a grade yet
      */
     private function get_cached_grade_score($gradeitemid, $grabthelot=false, $userid=0) {
-        global $USER, $DB, $SESSION;
-        if ($userid==0 || $userid==$USER->id) {
-            // For current user, go via cache in session
-            if (empty($SESSION->gradescorecache) || $SESSION->gradescorecacheuserid!=$USER->id) {
-                $SESSION->gradescorecache = array();
-                $SESSION->gradescorecacheuserid = $USER->id;
-            }
-            if (!array_key_exists($gradeitemid, $SESSION->gradescorecache)) {
-                if ($grabthelot) {
-                    // Get all grades for the current course
-                    $rs = $DB->get_recordset_sql('
-                            SELECT
-                                gi.id,gg.finalgrade,gg.rawgrademin,gg.rawgrademax
-                            FROM
-                                {grade_items} gi
-                                LEFT JOIN {grade_grades} gg ON gi.id=gg.itemid AND gg.userid=?
-                            WHERE
-                                gi.courseid = ?', array($USER->id, $this->item->course));
-                    foreach ($rs as $record) {
-                        $SESSION->gradescorecache[$record->id] =
-                            is_null($record->finalgrade)
-                                // No grade = false
-                                ? false
-                                // Otherwise convert grade to percentage
-                                : (($record->finalgrade - $record->rawgrademin) * 100) /
-                                    ($record->rawgrademax - $record->rawgrademin);
+        global $USER, $DB;
+        if (!$userid) {
+            $userid = $USER->id;
+        }
+        $cache = cache::make('core', 'gradecondition');
+        if (($cachedgrades = $cache->get($userid)) === false) {
+            $cachedgrades = array();
+        }
+        if (!array_key_exists($gradeitemid, $cachedgrades)) {
+            if ($grabthelot) {
+                // Get all grades for the current course
+                $rs = $DB->get_recordset_sql('
+                        SELECT
+                            gi.id,gg.finalgrade,gg.rawgrademin,gg.rawgrademax
+                        FROM
+                            {grade_items} gi
+                            LEFT JOIN {grade_grades} gg ON gi.id=gg.itemid AND gg.userid=?
+                        WHERE
+                            gi.courseid = ?', array($userid, $this->item->course));
+                foreach ($rs as $record) {
+                    $cachedgrades[$record->id] =
+                        is_null($record->finalgrade)
+                            // No grade = false
+                            ? false
+                            // Otherwise convert grade to percentage
+                            : (($record->finalgrade - $record->rawgrademin) * 100) /
+                                ($record->rawgrademax - $record->rawgrademin);
 
-                    }
-                    $rs->close();
-                    // And if it's still not set, well it doesn't exist (eg
-                    // maybe the user set it as a condition, then deleted the
-                    // grade item) so we call it false
-                    if (!array_key_exists($gradeitemid, $SESSION->gradescorecache)) {
-                        $SESSION->gradescorecache[$gradeitemid] = false;
-                    }
-                } else {
-                    // Just get current grade
-                    $record = $DB->get_record('grade_grades', array(
-                        'userid'=>$USER->id, 'itemid'=>$gradeitemid));
-                    if ($record && !is_null($record->finalgrade)) {
-                        $score = (($record->finalgrade - $record->rawgrademin) * 100) /
-                            ($record->rawgrademax - $record->rawgrademin);
-                    } else {
-                        // Treat the case where row exists but is null, same as
-                        // case where row doesn't exist
-                        $score = false;
-                    }
-                    $SESSION->gradescorecache[$gradeitemid]=$score;
                 }
-            }
-            return $SESSION->gradescorecache[$gradeitemid];
-        } else {
-            // Not the current user, so request the score individually
-            $record = $DB->get_record('grade_grades', array(
-                'userid'=>$userid, 'itemid'=>$gradeitemid));
-            if ($record && !is_null($record->finalgrade)) {
-                $score = (($record->finalgrade - $record->rawgrademin) * 100) /
-                    ($record->rawgrademax - $record->rawgrademin);
+                $rs->close();
+                // And if it's still not set, well it doesn't exist (eg
+                // maybe the user set it as a condition, then deleted the
+                // grade item) so we call it false
+                if (!array_key_exists($gradeitemid, $cachedgrades)) {
+                    $cachedgrades[$gradeitemid] = false;
+                }
             } else {
-                // Treat the case where row exists but is null, same as
-                // case where row doesn't exist
-                $score = false;
+                // Just get current grade
+                $record = $DB->get_record('grade_grades', array(
+                    'userid'=>$userid, 'itemid'=>$gradeitemid));
+                if ($record && !is_null($record->finalgrade)) {
+                    $score = (($record->finalgrade - $record->rawgrademin) * 100) /
+                        ($record->rawgrademax - $record->rawgrademin);
+                } else {
+                    // Treat the case where row exists but is null, same as
+                    // case where row doesn't exist
+                    $score = false;
+                }
+                $cachedgrades[$gradeitemid]=$score;
             }
-            return $score;
+            $cache->set($userid, $cachedgrades);
         }
+        return $cachedgrades[$gradeitemid];
+    }
+
+    /**
+     * Called by grade code to inform the completion system when a grade has
+     * been changed. Grades can be used to determine condition for
+     * the course-module or section.
+     *
+     * Note that this function may be called twice for one changed grade object.
+     *
+     * @param grade_grade $grade
+     * @param bool $deleted
+     */
+    public static function inform_grade_changed($grade, $deleted) {
+        cache::make('core', 'gradecondition')->delete($grade->userid);
     }
 
     /**
@@ -1389,16 +1398,12 @@ abstract class condition_info_base {
     }
 
     /**
-     * For testing only. Wipes information cached in user session.
-     *
-     * @global stdClass $SESSION
+     * For testing only. Wipes information cached in cache.
+     * Replaced with {@link core_conditionlib_testcase::wipe_condition_cache()}
+     * @deprecated since 2.6
      */
     static function wipe_session_cache() {
-        global $SESSION;
-        unset($SESSION->gradescorecache);
-        unset($SESSION->gradescorecacheuserid);
-        unset($SESSION->userfieldcache);
-        unset($SESSION->userfieldcacheuserid);
+        cache::make('core', 'gradecondition')->purge();
     }
 
     /**
index 8f941ea..a30f58e 100644 (file)
@@ -2043,7 +2043,7 @@ class coursecat implements renderable, cacheable_object, IteratorAggregate {
             $context = $this->get_context();
             return format_string($this->name, true, array('context' => $context) + $options);
         } else {
-            return ''; // TODO 'Top'?.
+            return get_string('top');
         }
     }
 
index a728d55..c882073 100644 (file)
@@ -205,4 +205,11 @@ $definitions = array(
         'simplekeys' => true,
         'simpledata' => true
     ),
+    // Used to cache user grades for conditional availability purposes.
+    'gradecondition' => array(
+        'mode' => cache_store::MODE_APPLICATION,
+        'staticacceleration' => true,
+        'staticaccelerationsize' => 2, // Should not be required for more than one user at a time.
+        'ttl' => 3600,
+    ),
 );
index 99e5f32..d3ef19f 100644 (file)
@@ -42,9 +42,10 @@ $handlers = array(
  *       (it is just abusing cron to do very time consuming things which is wrong any way)
  *
  * TODO: this has to be moved into separate queueing framework....
+ * TODO: MDL-25508, MDL-41541
  */
     'portfolio_send' => array (
-        'handlerfile'      => '/lib/portfolio.php',
+        'handlerfile'      => '/lib/portfoliolib.php',
         'handlerfunction'  => 'portfolio_handle_event',    // argument to call_user_func(), could be an array
         'schedule'         => 'cron',
         'internal'         => 0,
index 2bf05c5..7604f3e 100644 (file)
@@ -2739,5 +2739,73 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2013102201.00);
     }
 
+    if ($oldversion < 2013102500.01) {
+        // Find all fileareas that have missing root folder entry and add the root folder entry.
+        if (empty($CFG->filesrootrecordsfixed)) {
+            $sql = "SELECT distinct f1.contextid, f1.component, f1.filearea, f1.itemid
+                FROM {files} f1 left JOIN {files} f2
+                    ON f1.contextid = f2.contextid
+                    AND f1.component = f2.component
+                    AND f1.filearea = f2.filearea
+                    AND f1.itemid = f2.itemid
+                    AND f2.filename = '.'
+                    AND f2.filepath = '/'
+                WHERE (f1.component <> 'user' or f1.filearea <> 'draft')
+                and f2.id is null";
+            $rs = $DB->get_recordset_sql($sql);
+            $defaults = array('filepath' => '/',
+                            'filename' => '.',
+                            'userid' => $USER->id,
+                            'filesize' => 0,
+                            'timecreated' => time(),
+                            'timemodified' => time(),
+                            'contenthash' => sha1(''));
+            foreach ($rs as $r) {
+                $pathhash = sha1("/$r->contextid/$r->component/$r->filearea/$r->itemid".'/.');
+                $DB->insert_record('files', (array)$r + $defaults +
+                        array('pathnamehash' => $pathhash));
+            }
+            $rs->close();
+            // To skip running the same script on the upgrade to the next major release.
+            set_config('filesrootrecordsfixed', 1);
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013102500.01);
+    }
+
+    if ($oldversion < 2013110400.00) {
+
+        if (!check_dir_exists($CFG->dirroot . '/theme/mymobile', false)) {
+            // Delete from config_plugins.
+            $DB->delete_records('config_plugins', array('plugin' => 'theme_mymobile'));
+            // Delete the config logs.
+            $DB->delete_records('config_log', array('plugin' => 'theme_mymobile'));
+
+            // Replace the mymobile settings.
+            $DB->set_field('course', 'theme', 'clean', array('theme' => 'mymobile'));
+            $DB->set_field('course_categories', 'theme', 'clean', array('theme' => 'mymobile'));
+            $DB->set_field('user', 'theme', 'clean', array('theme' => 'mymobile'));
+            $DB->set_field('mnet_host', 'theme', 'clean', array('theme' => 'mymobile'));
+
+            // Replace the theme configs.
+            if (get_config('core', 'theme') == 'mymobile') {
+                set_config('theme', 'clean');
+            }
+            if (get_config('core', 'thememobile') == 'mymobile') {
+                set_config('thememobile', 'clean');
+            }
+            if (get_config('core', 'themelegacy') == 'mymobile') {
+                set_config('themelegacy', 'clean');
+            }
+            if (get_config('core', 'themetablet') == 'mymobile') {
+                set_config('themetablet', 'clean');
+            }
+        }
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2013110400.00);
+    }
+
     return true;
 }
index 8487e9c..7147da3 100644 (file)
@@ -122,5 +122,18 @@ fontselect,fontsizeselect,wrap,code,search,replace,|,cleanup,removeformat,pastet
         upgrade_plugin_savepoint(true, 2013070500, 'editor', 'tinymce');
     }
 
+    if ($oldversion < 2013102900) {
+        // Reset redesigned editor toolbar setting.
+        $currentorder = get_config('editor_tinymce', 'customtoolbar');
+        // Start with a wrap.
+        $neworder = "wrap,". $currentorder;
+        // Replace all separators with wraps to allow for proper display of groups.
+        $neworder = preg_replace('/\|\|*/', "wrap", $neworder);
+        // Insert a wrap between the format selector and the bold button.
+        $neworder = str_replace("formatselect,bold", "formatselect,wrap,bold", $neworder);
+        set_config('customtoolbar', $neworder, 'editor_tinymce');
+        upgrade_plugin_savepoint(true, 2013102900, 'editor', 'tinymce');
+    }
+
     return true;
 }
index 9e17c83..87bc00a 100644 (file)
@@ -30,7 +30,7 @@ $string['availablebuttons'] = 'Available buttons';
 $string['customconfig'] = 'Custom configuration';
 $string['customconfig_desc'] = 'Custom advanced TinyMCE configuration in JSON format, for example: {"option1" : "value2", "option2" : "value2"}. Any options specified here override standard and plugin settings.';
 $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.<br />The first row will always be shown, where as the visibility of second and third toolbars can be toggled';
+$string['customtoolbar_desc'] = 'Each line contains a list of comma separated button names, use "wrap" as a group separator, empty lines are ignored. See <a href="{$a}" target="_blank">{$a}</a> for the list of default TinyMCE buttons.<br />The first row will always be shown, where as the visibility of second and third toolbars can be toggled.';
 $string['fontselectlist'] = 'Available fonts list';
 $string['pluginname'] = 'TinyMCE HTML editor';
 $string['settings'] = 'General settings';
index 57ce3d9..2efdfbc 100644 (file)
@@ -59,7 +59,7 @@ header('Content-Type: text/html; charset=utf-8');
 header('X-UA-Compatible: IE=edge');
 ?>
 <!DOCTYPE html>
-<html <?php echo $htmllang ?>
+<html <?php echo $htmllang ?>>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 <title><?php print_string('title', 'tinymce_dragmath')?></title>
@@ -78,8 +78,8 @@ header('X-UA-Compatible: IE=edge');
 </object>
 <form name="form" action="#">
     <div class="mceActionPanel">
-        <input type="submit" id="insert" name="insert" value="{#insert}" onclick="return DragMathDialog.insert();" />
-        <input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="return tinyMCEPopup.close();" />
+        <input type="submit" id="insert" name="insert" value="<?php print_string('common:insert', 'editor_tinymce'); ?>" onclick="return DragMathDialog.insert();" />
+        <input type="button" id="cancel" name="cancel" value="<?php print_string('cancel'); ?>" onclick="return tinyMCEPopup.close();" />
     </div>
 </form>
 
index 7505977..e3d7c1b 100644 (file)
Binary files a/lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.png and b/lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.png differ
diff --git a/lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.svg b/lib/editor/tinymce/plugins/moodlemedia/tinymce/img/icon.svg
new file mode 100644 (file)
index 0000000..69c89a6
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M0,0v16h16V0H0z M3,15H1v-2h2V15z M3,11H1V9h2V11z M3,7H1V5h2V7z M3,3H1V1h2V3z M12,15H4V1h8V15z\r
+        M15,15h-2v-2h2V15z M15,11h-2V9h2V11z M15,7h-2V5h2V7z M15,3h-2V1h2V3z M10,8l-4,3V5L10,8z"/>\r
+</svg>\r
index e23f60a..4a0148c 100644 (file)
@@ -36,8 +36,6 @@ class tinymce_pdw extends editor_tinymce_plugin {
 
         $rowsnumber = $this->count_button_rows($params);
         if ($rowsnumber > 1) {
-            // Add button before 'undo' in advancedbuttons1.
-            $this->add_button_before($params, 1, '|', '');
             $this->add_button_before($params, 1, 'pdw_toggle', '');
             $params['pdw_toggle_on'] = 1;
             $params['pdw_toggle_toolbars'] = join(',', range(2, $rowsnumber));
index 9f0021a..b65b192 100644 (file)
@@ -25,7 +25,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 // The current plugin version (Date: YYYYMMDDXX).
-$plugin->version   = 2013070500;
+$plugin->version   = 2013100700;
 // Required Moodle version.
 $plugin->requires  = 2013050100;
 // Full name of the plugin (used for diagnostics).
index af3d992..d8d57df 100644 (file)
          */
         Wrap : function(id, s) {
             this.parent(id, s);
-            this.groupEndClass = 'mceToolbarEnd';
-            this.groupStartClass = 'mceToolbarStart';
+            this.groupEndClass = 'mceGroupEnd';
+            this.toolbarEndClass = 'mceLast';
+            this.groupEndPlaceholder = 'mceToolbarEndPlaceholder';
+            this.groupStartClass = 'mceGroupStart';
             this.wrapClass = 'mceWrap';
+            this.noWrapClass = 'mceNoWrap';
+            this.toolbarClass = 'mceToolbar';
+            this.selectListArrowClass = 'mceOpen';
             this.setDisabled(true);
         },
 
                                                             'aria-orientation' : 'vertical',
                                                             tabindex : '-1'});
             return '</td>' +
-                   '<td style="position: relative" class="' + this.groupEndClass + '">' + separator + '</td>' +
+                   '<td style="position: relative" class="' + this.groupEndPlaceholder + '">' + separator + '</td>' +
                    '<td style="position: relative" class="' + this.groupStartClass + ' ' + this.wrapClass + '">' + separator + '</td>';
-        }
+        },
+
+        postRender : function() {
+            var self = this;
+            // Add a class to the item prior to the wrap.
+            YUI().use('node', function(Y) {
+                var endGroupElements = tinymce.DOM.select('td.' + self.groupEndPlaceholder),
+                    index = 0, curElement, endOfLast,
+                    endBarElements = tinymce.DOM.select('td.' + self.toolbarEndClass);
 
+                for (index = 0; index < endGroupElements.length; index++) {
+                    if (!endGroupElements.hasOwnProperty(index)) {
+                          continue;
+                    }
+                    curElement = Y.one(endGroupElements[index]);
+                    endOfLast = curElement.previous('td').previous('td');
+                    if (endOfLast) {
+                        endOfLast.addClass(self.groupEndClass);
+                    }
+                }
+                for (index = 0; index < endBarElements.length; index++) {
+                    if (!endBarElements.hasOwnProperty(index)) {
+                        continue;
+                    }
+                    curElement = Y.one(endBarElements[index]);
+                    endOfLast = curElement.previous('td');
+                    if (endOfLast) {
+                        endOfLast.addClass(self.groupEndClass);
+                    }
+                }
+                // Any separators closer together than 5 buttons get the noWrapClass.
+                var toolbars = Y.all('table.' + self.toolbarClass),
+                    buttonWrapPoint = 5;
 
+                toolbars.each(function(toolbar) {
+                    var count = 0;
+                    widgets = toolbar.all('td.' + self.wrapClass + ', td > a');
+                    widgets.each(function(widget) {
+                        if (widget.hasClass(self.wrapClass)) {
+                            if (count >= buttonWrapPoint) {
+                                count = 0;
+                            } else {
+                                widget.addClass(self.noWrapClass);
+                            }
+                        } else {
+                            if (widget.hasClass(self.selectListArrowClass) ||
+                                    (widget.getAttribute('role') === 'button')) {
+                                count++;
+                            } else {
+                                // Count select inputs as 3 buttons. The down arrow on the select also gets counted so 2+1 = 3.
+                                count += 2;
+                            }
+                        }
+                    });
+                });
+
+            });
+        }
     });
 
     tinymce.create('tinymce.plugins.wrapPlugin', {
index 3699c51..b8ca304 100644 (file)
@@ -31,11 +31,11 @@ if ($ADMIN->fulltree) {
     require_once(__DIR__.'/adminlib.php');
     $settings->add(new tiynce_subplugins_settings());
     $settings->add(new admin_setting_heading('tinymcegeneralheader', new lang_string('settings'), ''));
-    $default = "formatselect,bold,italic,wrap,bullist,numlist,|,link,unlink,|,image
+    $default = "wrap,formatselect,wrap,bold,italic,wrap,bullist,numlist,wrap,link,unlink,wrap,image
 
-undo,redo,|,underline,strikethrough,sub,sup,|,justifyleft,justifycenter,justifyright,wrap,outdent,indent,|,forecolor,backcolor,|,ltr,rtl,|,nonbreaking,charmap,table
+undo,redo,wrap,underline,strikethrough,sub,sup,wrap,justifyleft,justifycenter,justifyright,wrap,outdent,indent,wrap,forecolor,backcolor,wrap,ltr,rtl,wrap,nonbreaking,charmap,table
 
-fontselect,fontsizeselect,wrap,code,search,replace,|,cleanup,removeformat,pastetext,pasteword,|,fullscreen";
+fontselect,fontsizeselect,wrap,code,search,replace,wrap,cleanup,removeformat,pastetext,pasteword,wrap,fullscreen";
     $settings->add(new admin_setting_configtextarea('editor_tinymce/customtoolbar',
         get_string('customtoolbar', 'editor_tinymce'), get_string('customtoolbar_desc', 'editor_tinymce', 'http://www.tinymce.com/wiki.php/TinyMCE3x:Buttons/controls'), $default, PARAM_RAW, 100, 8));
     $settings->add(new admin_setting_configtextarea('editor_tinymce/fontselectlist',
index 9398912..86703ef 100644 (file)
@@ -3,8 +3,14 @@
         float: left;
         display: inline-block;
     }
-    .mceToolbar .mceWrap {
+    .moodleSkin .mceLayout .mceToolbar .mceWrap {
         clear: left;
+        width: 100%;
+        height: 8px;
+    }
+    .moodleSkin .mceLayout .mceToolbar .mceNoWrap {
+        clear: none;
+        width: 0px;
     }
 
     .o2k7Skin tr.mceLast .mceToolbar tr td.mceWrap,
index 93c2f08..a3c8e6a 100644 (file)
Binary files a/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.png and b/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/abbr.png differ
index be8883c..3bc7d46 100644 (file)
@@ -5,16 +5,16 @@
 ]>\r
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
-        x="0px" y="0px" width="16px" height="16px" viewBox="-0.3 -3.6 16 16"\r
-        style="overflow:visible;enable-background:new -0.3 -3.6 16 16;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 -3.9 16 16" style="overflow:visible;enable-background:new 0 -3.9 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
-<path style="fill:#999999;" d="M0,8c0.3,0,0.5-0.2,0.7-0.4c0.1-0.2,0.3-0.5,0.5-1L3.8,0h0.3l2.7,6.3C7.2,7,7.4,7.5,7.5,7.7\r
-       S7.9,8,8.2,8v0.3H4.3V8c0.4,0,0.6,0,0.8-0.1s0.2-0.2,0.2-0.4c0-0.1,0-0.2-0.1-0.4c0-0.1-0.1-0.2-0.2-0.4L4.7,5.9H2\r
-       C1.8,6.4,1.7,6.7,1.6,6.8C1.5,7.2,1.5,7.4,1.5,7.5c0,0.2,0.1,0.3,0.3,0.4C2,7.9,2.2,8,2.4,8v0.3H0V8z M4.5,5.4L3.4,2.7H3.2L2.2,5.4\r
-       H4.5z M9.8,0.2v3.1C10,3.1,10.1,3,10.3,2.9c0.3-0.2,0.6-0.2,0.9-0.2c0.7,0,1.2,0.3,1.6,0.8s0.6,1.2,0.6,1.9c0,1-0.3,1.8-0.8,2.3\r
-       s-1.2,0.8-2,0.8c-0.3,0-0.6-0.1-0.8-0.2S9.4,8,9.2,7.8L8.3,8.4H8.1V1.1c0-0.3,0-0.4-0.1-0.5C7.9,0.5,7.7,0.5,7.5,0.5V0.2H9.8z\r
-        M9.9,7.6c0.1,0.3,0.4,0.4,0.7,0.4c0.4,0,0.7-0.2,0.9-0.7c0.2-0.4,0.3-1,0.3-1.8c0-0.6-0.1-1.2-0.2-1.6s-0.4-0.7-0.9-0.7\r
-       c-0.3,0-0.5,0.1-0.6,0.3C9.9,3.7,9.8,3.9,9.8,4v3.2C9.8,7.3,9.8,7.5,9.9,7.6z M14,6.7c0.2-0.2,0.4-0.3,0.7-0.3s0.5,0.1,0.7,0.3\r
-       s0.3,0.4,0.3,0.7S15.6,8,15.4,8.1S15,8.4,14.7,8.4S14.2,8.3,14,8.1s-0.3-0.4-0.3-0.7S13.8,6.9,14,6.7z"/>\r
+<path style="fill:#999999;" d="M15,6.2c0.3,0,0.5,0.1,0.7,0.3S16,6.9,16,7.1s-0.1,0.5-0.3,0.7S15.3,8.1,15,8.1S14.5,8,14.3,7.8\r
+       S14,7.4,14,7.1s0.1-0.5,0.3-0.7S14.8,6.2,15,6.2z M10.3,0v3c0.5-0.5,1-0.7,1.5-0.7c0.4,0,0.7,0.1,1.1,0.3s0.6,0.5,0.8,0.9\r
+       s0.3,0.9,0.3,1.4c0,0.6-0.1,1.1-0.4,1.6s-0.6,0.9-1,1.1s-0.9,0.4-1.4,0.4c-0.3,0-0.6,0-0.8-0.1S10,7.7,9.8,7.5L8.8,8.1H8.7v-7\r
+       c0-0.3,0-0.5,0-0.6c0-0.1-0.1-0.2-0.2-0.2S8.2,0.2,8,0.2V0H10.3z M10.3,3.5V6c0,0.5,0,0.8,0,0.9c0,0.2,0.1,0.4,0.3,0.6\r
+       s0.3,0.2,0.6,0.2c0.2,0,0.4-0.1,0.5-0.2s0.3-0.3,0.4-0.7s0.1-0.9,0.1-1.8c0-0.8-0.1-1.4-0.3-1.7C11.7,3.1,11.5,3,11.2,3\r
+       C10.9,3,10.6,3.2,10.3,3.5z M4.9,5.8H2.1L1.8,6.6C1.6,6.9,1.6,7.1,1.6,7.3c0,0.2,0.1,0.4,0.3,0.5C2,7.8,2.2,7.9,2.6,7.9v0.2H0V7.9\r
+       c0.3,0,0.5-0.2,0.7-0.4s0.4-0.6,0.7-1.2L4.2,0h0.1l2.9,6.5c0.3,0.6,0.5,1,0.7,1.2C8,7.8,8.2,7.9,8.4,7.9v0.2H4.6V7.9h0.2\r
+       c0.3,0,0.5,0,0.7-0.1c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2c0,0-0.1-0.2-0.2-0.4L4.9,5.8z M4.7,5.4L3.5,2.7L2.3,5.4H4.7z"/>\r
 </svg>\r
index 73488d8..40b68a4 100644 (file)
Binary files a/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.png and b/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/acronym.png differ
index 3d4dc46..bde1557 100644 (file)
@@ -5,20 +5,17 @@
 ]>\r
 <svg version="1.1"\r
         xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
-        x="0px" y="0px" width="16px" height="16px" viewBox="0 -4.6 16 16" style="overflow:visible;enable-background:new 0 -4.6 16 16;"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 -3.9 16 16" style="overflow:visible;enable-background:new 0 -3.9 16 16;"\r
         xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
-<path style="fill:#999999;" d="M0,6c0.2,0,0.4-0.1,0.5-0.3c0.1-0.1,0.2-0.4,0.4-0.8l2-4.9h0.3l2,4.7c0.2,0.5,0.4,0.9,0.5,1\r
-       C5.8,5.9,5.9,6,6.1,6v0.2H3.2V6c0.3,0,0.5,0,0.6-0.1c0.1,0,0.1-0.1,0.1-0.3c0-0.1,0-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3L3.5,4.4h-2\r
-       C1.4,4.8,1.3,5,1.2,5.1C1.2,5.4,1.1,5.5,1.1,5.6c0,0.1,0.1,0.2,0.3,0.3C1.5,6,1.6,6,1.8,6v0.2H0V6z M3.4,4.1L2.5,2H2.4L1.6,4.1H3.4z\r
-        M5.6,6c0.2,0,0.4,0,0.5-0.1c0.2-0.1,0.3-0.3,0.3-0.5V1c0-0.3-0.1-0.4-0.3-0.5C6,0.4,5.8,0.4,5.6,0.4V0.1h2.8c0.5,0,1,0.1,1.3,0.2\r
-       c0.7,0.2,1,0.7,1,1.3c0,0.4-0.1,0.7-0.4,0.9C10,2.7,9.7,2.8,9.4,2.9V3C9.7,3,10,3.1,10.3,3.3C10.8,3.6,11,4,11,4.5\r
-       c0,0.5-0.2,0.9-0.7,1.2C9.9,6.1,9.2,6.2,8.5,6.2H5.6V6z M8.9,2.6c0.2-0.2,0.3-0.5,0.3-1C9.2,1.3,9.2,1,9,0.7\r
-       C8.9,0.5,8.7,0.4,8.3,0.4c-0.2,0-0.3,0-0.4,0.1S7.8,0.7,7.8,0.8v2C8.4,2.9,8.7,2.8,8.9,2.6z M7.9,5.7C8,5.9,8.1,5.9,8.3,5.9\r
-       c0.4,0,0.7-0.1,0.9-0.3c0.2-0.2,0.3-0.6,0.3-1c0-0.6-0.2-1.1-0.6-1.3C8.7,3.2,8.3,3.1,7.8,3.1v2.2C7.8,5.5,7.8,5.6,7.9,5.7z\r
-        M15.2,0.2c0.4,0.1,0.6,0.2,0.6,0.2c0.1,0,0.2,0,0.3-0.1c0.1-0.1,0.1-0.2,0.1-0.3h0.3v2.1h-0.2C16,1.7,15.8,1.3,15.6,1\r
-       c-0.4-0.5-0.9-0.7-1.4-0.7c-0.6,0-1,0.3-1.3,0.8c-0.3,0.5-0.4,1.2-0.4,2.1c0,0.6,0.1,1.2,0.2,1.6c0.3,0.8,0.8,1.2,1.5,1.2\r
-       c0.5,0,0.9-0.1,1.3-0.4c0.2-0.1,0.5-0.4,0.8-0.7l0.3,0.2c-0.4,0.4-0.7,0.7-0.9,0.9c-0.5,0.3-1,0.5-1.6,0.5c-0.9,0-1.6-0.3-2.2-0.8\r
-       c-0.7-0.6-1-1.4-1-2.4c0-1,0.3-1.8,1-2.4C12.4,0.3,13.2,0,14,0C14.4,0,14.8,0.1,15.2,0.2z"/>\r
+<path style="fill:#999999;" d="M13.8,3.8C14.5,4,15,4.2,15.3,4.5C15.8,4.8,16,5.3,16,5.9c0,0.6-0.2,1.1-0.7,1.5\r
+       c-0.6,0.5-1.4,0.7-2.6,0.7h-4V7.8c0.4,0,0.6,0,0.7-0.1s0.2-0.2,0.3-0.3s0.1-0.4,0.1-0.8V1.5c0-0.4,0-0.7-0.1-0.8S9.5,0.5,9.4,0.4\r
+       S9,0.3,8.7,0.3V0.1h3.8c0.9,0,1.6,0.1,1.9,0.2s0.7,0.4,0.9,0.7s0.3,0.7,0.3,1c0,0.4-0.1,0.7-0.4,1S14.5,3.7,13.8,3.8z M11.6,4.1v2.5\r
+       l0,0.3c0,0.2,0.1,0.4,0.2,0.5s0.3,0.2,0.5,0.2c0.3,0,0.6-0.1,0.9-0.2s0.5-0.3,0.6-0.6s0.2-0.6,0.2-0.9c0-0.4-0.1-0.7-0.3-1\r
+       S13.3,4.4,13,4.3S12.2,4.1,11.6,4.1z M11.6,3.7c0.6,0,1-0.1,1.2-0.2s0.5-0.3,0.6-0.5s0.2-0.5,0.2-0.9s-0.1-0.6-0.2-0.9\r
+       s-0.3-0.4-0.6-0.5s-0.7-0.2-1.2-0.2V3.7z M4.9,5.8H2.1L1.8,6.6C1.6,6.9,1.6,7.1,1.6,7.3c0,0.2,0.1,0.4,0.3,0.5\r
+       C2,7.8,2.2,7.9,2.6,7.9v0.2H0V7.9c0.3,0,0.5-0.2,0.7-0.4s0.4-0.6,0.7-1.2L4.2,0h0.1l2.9,6.5c0.3,0.6,0.5,1,0.7,1.2\r
+       C8,7.8,8.2,7.9,8.4,7.9v0.2H4.6V7.9h0.2c0.3,0,0.5,0,0.7-0.1c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2c0,0-0.1-0.2-0.2-0.4\r
+       L4.9,5.8z M4.7,5.4L3.5,2.7L2.3,5.4H4.7z"/>\r
 </svg>\r
index 2d76266..19bf15b 100644 (file)
Binary files a/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.png and b/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_center.png differ
index f6eae29..5b4bea9 100644 (file)
@@ -9,5 +9,5 @@
         xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
-<path style="fill:#999999;" d="M0,0h16v2H0V0z M0,6h16v2H0V6z M0,12h16v2H0V12z M3,3h10v2H3V3z M3,9h10v2H3V9z"/>\r
+<path style="fill:#999999;" d="M0,12h16v2H0V12z M13,9H3v2h10V9z M0,8h16V6H0V8z M13,3H3v2h10V3z M16,0H0v2h16V0z"/>\r
 </svg>\r
index 1d87f1c..524002d 100644 (file)
Binary files a/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.png and b/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_left.png differ
index b792e41..5e79a65 100644 (file)
@@ -9,5 +9,5 @@
         xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
 <defs>\r
 </defs>\r
-<path style="fill:#999999;" d="M0,0h16v2H0V0z M0,3h10v2H0V3z M0,6h16v2H0V6z M0,9h10v2H0V9z M0,12h16v2H0V12z"/>\r
+<path style="fill:#999999;" d="M0,12h16v2H0V12z M10,9H0v2h10V9z M0,8h16V6H0V8z M10,3H0v2h10V3z M16,2V0H0v2H16z"/>\r
 </svg>\r
index 113b735..ab92fb6 100644 (file)
Binary files a/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.png and b/lib/editor/tinymce/tiny_mce/3.5.8/themes/advanced/skins/moodle/img/align_right.png differ