Merge branch 'MDL-32895' of git://github.com/netspotau/moodle-mod_assign
authorDan Poltawski <dan@moodle.com>
Wed, 23 May 2012 03:13:03 +0000 (11:13 +0800)
committerDan Poltawski <dan@moodle.com>
Wed, 23 May 2012 03:13:03 +0000 (11:13 +0800)
886 files changed:
admin/index.php
admin/renderer.php
admin/repository.php
admin/repositoryinstance.php
admin/roles/usersroles.php
admin/settings/development.php
admin/settings/plugins.php
admin/settings/server.php
admin/timezone.php
admin/tool/dbtransfer/database_transfer_form.php
admin/tool/innodb/index.php
admin/tool/unittest/coveragefile.php
admin/user/user_bulk_download.php
admin/webservice/testclient_forms.php
backup/backupfilesedit_form.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_final_task.class.php
backup/moodle2/restore_stepslib.php
backup/util/dbops/backup_controller_dbops.class.php
backup/util/dbops/restore_dbops.class.php
backup/util/helper/backup_file_manager.class.php
backup/util/helper/backup_general_helper.class.php
backup/util/helper/convert_helper.class.php
backup/util/plan/restore_structure_step.class.php
backup/util/ui/backup_ui_stage.class.php
backup/util/ui/renderer.php
blocks/activity_modules/block_activity_modules.php
blocks/private_files/edit.php
blocks/private_files/renderer.php
blog/locallib.php
calendar/lib.php
config-dist.php
course/dndupload.js [new file with mode: 0644]
course/dndupload.php [new file with mode: 0644]
course/dnduploadlib.php [new file with mode: 0644]
course/edit.php
course/editcategory.php
course/editsection.php
course/editsection_form.php
course/externallib.php
course/format/renderer.php
course/format/topics/format.js
course/format/topics/renderer.php
course/format/weeks/format.js
course/lib.php
course/moodleform_mod.php
course/renderer.php
course/rest.php
course/style.css [new file with mode: 0644]
course/view.php
course/yui/coursebase/coursebase.js
course/yui/dragdrop/dragdrop.js
course/yui/modchooser/modchooser.js [new file with mode: 0644]
course/yui/toolboxes/toolboxes.js
draftfile.php
enrol/mnet/enrol.php
files/coursefilesedit_form.php
files/filebrowser_ajax.php
files/renderer.php
grade/export/grade_export_form.php
grade/export/lib.php
grade/export/ods/export.php
grade/export/ods/grade_export_ods.php
grade/export/ods/index.php
grade/export/txt/export.php
grade/export/txt/grade_export_txt.php
grade/export/txt/index.php
grade/export/xls/export.php
grade/export/xls/grade_export_xls.php
grade/export/xls/index.php
grade/export/xml/export.php
grade/export/xml/grade_export_xml.php
grade/export/xml/index.php
grade/lib.php
grade/report/grader/lib.php
group/delete.php
group/externallib.php
group/group.php
group/group_form.php
group/grouping.php
group/grouping_form.php
group/groupings.php
group/import.php
group/index.php
group/lib.php
group/module.js
index.php
install/lang/ar/admin.php
install/lang/et/admin.php
install/lang/rm_surs/admin.php [new file with mode: 0644]
install/lang/sr_cr/install.php
install/lang/sr_lt/install.php
install/lang/zh_cn/install.php
iplookup/lib.php
iplookup/tests/geoip_test.php [new file with mode: 0644]
iplookup/tests/geoplugin_test.php [new file with mode: 0644]
lang/en/admin.php
lang/en/backup.php
lang/en/completion.php
lang/en/condition.php
lang/en/error.php
lang/en/grades.php
lang/en/group.php
lang/en/install.php
lang/en/mimetypes.php
lang/en/moodle.php
lang/en/question.php
lang/en/repository.php
lang/en/webservice.php
lib/conditionlib.php
lib/cronlib.php
lib/db/install.xml
lib/db/services.php
lib/db/upgrade.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/pgsql_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/dml/tests/dml_test.php
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/settings.php
lib/editor/tinymce/version.php
lib/excel/test.php
lib/externallib.php
lib/filebrowser/file_info.php
lib/filebrowser/file_info_context_user.php
lib/filebrowser/file_info_stored.php
lib/filelib.php
lib/filestorage/file_exceptions.php
lib/filestorage/file_storage.php
lib/filestorage/file_types.mm [deleted file]
lib/filestorage/stored_file.php
lib/filestorage/tests/file_storage_test.php
lib/form/dndupload.js
lib/form/editor.php
lib/form/filemanager.js
lib/form/filemanager.php
lib/form/filepicker.js
lib/form/filepicker.php
lib/form/yui/dateselector/dateselector.js
lib/formslib.php
lib/grouplib.php
lib/medialib.php
lib/modinfolib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/phpunit/classes/advanced_testcase.php [new file with mode: 0644]
lib/phpunit/classes/arraydataset.php [new file with mode: 0644]
lib/phpunit/classes/basic_testcase.php [new file with mode: 0644]
lib/phpunit/classes/block_generator.php [new file with mode: 0644]
lib/phpunit/classes/data_generator.php [new file with mode: 0644]
lib/phpunit/classes/database_driver_testcase.php [new file with mode: 0644]
lib/phpunit/classes/hint_resultprinter.php [new file with mode: 0644]
lib/phpunit/classes/module_generator.php [new file with mode: 0644]
lib/phpunit/classes/unittestcase.php [new file with mode: 0644]
lib/phpunit/classes/util.php [new file with mode: 0644]
lib/phpunit/generatorlib.php
lib/phpunit/lib.php
lib/phpunit/tests/advanced_test.php [moved from lib/tests/phpunit_test.php with 63% similarity]
lib/phpunit/tests/basic_test.php [new file with mode: 0644]
lib/phpunit/tests/fixtures/sample_dataset.csv [moved from lib/tests/fixtures/sample_dataset.csv with 100% similarity]
lib/phpunit/tests/fixtures/sample_dataset.xml [moved from lib/tests/fixtures/sample_dataset.xml with 100% similarity]
lib/phpunit/tests/generator_test.php [new file with mode: 0644]
lib/pluginlib.php
lib/portfolio/formats.php
lib/setup.php
lib/setuplib.php
lib/tests/code_test.php
lib/tests/conditionlib_test.php
lib/tests/filelib_test.php
lib/tests/grouplib_test.php [new file with mode: 0644]
lib/tests/medialib_test.php
lib/tests/moodlelib_test.php
lib/tests/pagelib_test.php
lib/tests/repositorylib_test.php [deleted file]
lib/weblib.php
lib/yui/chooserdialogue/chooserdialogue.js [new file with mode: 0644]
mod/assign/adminlib.php
mod/assign/assignmentplugin.php
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/db/install.xml
mod/assign/db/messages.php
mod/assign/db/upgrade.php
mod/assign/gradingbatchoperationsform.php
mod/assign/gradingoptionsform.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/lib.php
mod/assign/locallib.php
mod/assign/mod_form.php
mod/assign/module.js
mod/assign/quickgradingform.php [new file with mode: 0644]
mod/assign/renderable.php
mod/assign/renderer.php
mod/assign/settings.php
mod/assign/styles.css
mod/assign/upgradelib.php
mod/assign/version.php
mod/assignment/lang/en/assignment.php
mod/assignment/lib.php
mod/assignment/renderer.php
mod/assignment/tests/generator_test.php
mod/assignment/type/online/assignment.class.php
mod/assignment/type/upload/assignment.class.php
mod/assignment/type/uploadsingle/assignment.class.php
mod/book/README.md [new file with mode: 0644]
mod/book/backup/moodle1/lib.php [new file with mode: 0644]
mod/book/backup/moodle2/backup_book_activity_task.class.php [new file with mode: 0644]
mod/book/backup/moodle2/backup_book_settingslib.php [new file with mode: 0644]
mod/book/backup/moodle2/backup_book_stepslib.php [new file with mode: 0644]
mod/book/backup/moodle2/restore_book_activity_task.class.php [new file with mode: 0644]
mod/book/backup/moodle2/restore_book_stepslib.php [new file with mode: 0644]
mod/book/db/access.php [new file with mode: 0644]
mod/book/db/install.xml [new file with mode: 0644]
mod/book/db/log.php [new file with mode: 0644]
mod/book/db/subplugins.php [new file with mode: 0644]
mod/book/db/upgrade.php [new file with mode: 0644]
mod/book/delete.php [new file with mode: 0644]
mod/book/edit.php [new file with mode: 0644]
mod/book/edit_form.php [new file with mode: 0644]
mod/book/index.php [new file with mode: 0644]
mod/book/lang/en/book.php [new file with mode: 0644]
mod/book/lib.php [new file with mode: 0644]
mod/book/locallib.php [new file with mode: 0644]
mod/book/mod_form.php [new file with mode: 0644]
mod/book/move.php [new file with mode: 0644]
mod/book/pix/add.png [new file with mode: 0644]
mod/book/pix/chapter.png [new file with mode: 0644]
mod/book/pix/icon.png [new file with mode: 0644]
mod/book/pix/nav_exit.png [new file with mode: 0644]
mod/book/pix/nav_next.png [new file with mode: 0644]
mod/book/pix/nav_prev.png [new file with mode: 0644]
mod/book/pix/nav_prev_dis.png [new file with mode: 0644]
mod/book/pix/nav_sep.png [new file with mode: 0644]
mod/book/settings.php [new file with mode: 0644]
mod/book/show.php [new file with mode: 0644]
mod/book/styles.css [new file with mode: 0644]
mod/book/tool/exportimscp/db/access.php [new file with mode: 0644]
mod/book/tool/exportimscp/db/log.php [new file with mode: 0644]
mod/book/tool/exportimscp/imscp.css [new file with mode: 0644]
mod/book/tool/exportimscp/index.php [new file with mode: 0644]
mod/book/tool/exportimscp/lang/en/booktool_exportimscp.php [new file with mode: 0644]
mod/book/tool/exportimscp/lib.php [new file with mode: 0644]
mod/book/tool/exportimscp/locallib.php [new file with mode: 0644]
mod/book/tool/exportimscp/pix/generate.png [new file with mode: 0644]
mod/book/tool/exportimscp/version.php [new file with mode: 0644]
mod/book/tool/importhtml/db/access.php [new file with mode: 0644]
mod/book/tool/importhtml/import_form.php [new file with mode: 0644]
mod/book/tool/importhtml/index.php [new file with mode: 0644]
mod/book/tool/importhtml/lang/en/booktool_importhtml.php [new file with mode: 0644]
mod/book/tool/importhtml/lib.php [new file with mode: 0644]
mod/book/tool/importhtml/locallib.php [new file with mode: 0644]
mod/book/tool/importhtml/version.php [new file with mode: 0644]
mod/book/tool/print/db/access.php [new file with mode: 0644]
mod/book/tool/print/db/log.php [new file with mode: 0644]
mod/book/tool/print/index.php [new file with mode: 0644]
mod/book/tool/print/lang/en/booktool_print.php [new file with mode: 0644]
mod/book/tool/print/lib.php [new file with mode: 0644]
mod/book/tool/print/locallib.php [new file with mode: 0644]
mod/book/tool/print/pix/book.png [new file with mode: 0644]
mod/book/tool/print/pix/chapter.png [new file with mode: 0644]
mod/book/tool/print/print.css [new file with mode: 0644]
mod/book/tool/print/version.php [new file with mode: 0644]
mod/book/version.php [new file with mode: 0644]
mod/book/view.php [new file with mode: 0644]
mod/chat/gui_basic/index.php
mod/chat/lang/en/chat.php
mod/chat/lib.php
mod/choice/lang/en/choice.php
mod/data/field/file/field.class.php
mod/data/field/picture/field.class.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/data/locallib.php
mod/data/view.php
mod/feedback/edit_form.php
mod/feedback/export.php
mod/feedback/lang/en/feedback.php
mod/feedback/lib.php
mod/folder/edit.php
mod/folder/edit_form.php
mod/folder/lang/en/folder.php
mod/folder/lib.php
mod/folder/mod_form.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/locallib.php
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/locallib.php
mod/imscp/lang/en/imscp.php
mod/label/lang/en/label.php
mod/lesson/lang/en/lesson.php
mod/lesson/lib.php
mod/lesson/locallib.php
mod/lti/lang/en/lti.php
mod/lti/lib.php
mod/lti/typessettings.php
mod/page/lang/en/page.php
mod/page/lib.php
mod/quiz/db/upgrade.php
mod/quiz/editlib.php
mod/quiz/lang/en/quiz.php
mod/quiz/lib.php
mod/quiz/locallib.php
mod/resource/lang/en/resource.php
mod/resource/lib.php
mod/resource/locallib.php
mod/resource/mod_form.php
mod/scorm/lang/en/scorm.php
mod/scorm/module.js
mod/survey/lang/en/survey.php
mod/survey/lib.php
mod/url/lang/en/url.php
mod/url/lib.php
mod/url/locallib.php
mod/wiki/edit_form.php
mod/wiki/editors/wikifiletable.php
mod/wiki/filesedit.php
mod/wiki/lang/en/wiki.php
mod/wiki/lib.php
mod/wiki/locallib.php
mod/wiki/renderer.php
mod/workshop/form/rubric/lib.php
mod/workshop/form/rubric/tests/lib_test.php
mod/workshop/lang/en/workshop.php
mod/workshop/lib.php
mod/workshop/renderer.php
mod/workshop/submission.php
phpunit.xml.dist
pix/a/add_file.png [new file with mode: 0644]
pix/a/create_folder.png [new file with mode: 0644]
pix/a/download_all.png [new file with mode: 0644]
pix/f/FileTypesIcons-LICENSE.txt [new file with mode: 0644]
pix/f/Oxygen-LICENSE.txt [new file with mode: 0644]
pix/f/audio-128.png [new file with mode: 0644]
pix/f/audio-24.png [new file with mode: 0644]
pix/f/audio-256.png [new file with mode: 0644]
pix/f/audio-32.png
pix/f/audio-48.png [new file with mode: 0644]
pix/f/audio-64.png [new file with mode: 0644]
pix/f/audio-72.png [new file with mode: 0644]
pix/f/audio-80.png [new file with mode: 0644]
pix/f/audio-96.png [new file with mode: 0644]
pix/f/audio.gif [deleted file]
pix/f/audio.png [new file with mode: 0644]
pix/f/avi-128.png [new file with mode: 0644]
pix/f/avi-24.png [new file with mode: 0644]
pix/f/avi-256.png [new file with mode: 0644]
pix/f/avi-32.png
pix/f/avi-48.png [new file with mode: 0644]
pix/f/avi-64.png [new file with mode: 0644]
pix/f/avi-72.png [new file with mode: 0644]
pix/f/avi-80.png [new file with mode: 0644]
pix/f/avi-96.png [new file with mode: 0644]
pix/f/avi.gif [deleted file]
pix/f/avi.png [new file with mode: 0644]
pix/f/bmp-128.png [new file with mode: 0644]
pix/f/bmp-24.png [new file with mode: 0644]
pix/f/bmp-256.png [new file with mode: 0644]
pix/f/bmp-32.png [new file with mode: 0644]
pix/f/bmp-48.png [new file with mode: 0644]
pix/f/bmp-64.png [new file with mode: 0644]
pix/f/bmp-72.png [new file with mode: 0644]
pix/f/bmp-80.png [new file with mode: 0644]
pix/f/bmp-96.png [new file with mode: 0644]
pix/f/bmp.png [new file with mode: 0644]
pix/f/database-128.png [new file with mode: 0644]
pix/f/database-24.png [new file with mode: 0644]
pix/f/database-256.png [new file with mode: 0644]
pix/f/database-32.png [new file with mode: 0644]
pix/f/database-48.png [new file with mode: 0644]
pix/f/database-64.png [new file with mode: 0644]
pix/f/database-72.png [new file with mode: 0644]
pix/f/database-80.png [new file with mode: 0644]
pix/f/database-96.png [new file with mode: 0644]
pix/f/database.png [new file with mode: 0644]
pix/f/docm-32.png [deleted file]
pix/f/docm.gif [deleted file]
pix/f/document-128.png [new file with mode: 0644]
pix/f/document-24.png [new file with mode: 0644]
pix/f/document-256.png [new file with mode: 0644]
pix/f/document-32.png [new file with mode: 0644]
pix/f/document-48.png [new file with mode: 0644]
pix/f/document-64.png [new file with mode: 0644]
pix/f/document-72.png [new file with mode: 0644]
pix/f/document-80.png [new file with mode: 0644]
pix/f/document-96.png [new file with mode: 0644]
pix/f/document.png [new file with mode: 0644]
pix/f/docx-128.png [new file with mode: 0644]
pix/f/docx-24.png [new file with mode: 0644]
pix/f/docx-256.png [new file with mode: 0644]
pix/f/docx-32.png
pix/f/docx-48.png [new file with mode: 0644]
pix/f/docx-64.png [new file with mode: 0644]
pix/f/docx-72.png [new file with mode: 0644]
pix/f/docx-80.png [new file with mode: 0644]
pix/f/docx-96.png [new file with mode: 0644]
pix/f/docx.gif [deleted file]
pix/f/docx.png [new file with mode: 0644]
pix/f/dotm-32.png [deleted file]
pix/f/dotm.gif [deleted file]
pix/f/dotx-32.png [deleted file]
pix/f/dotx.gif [deleted file]
pix/f/eps-128.png [new file with mode: 0644]
pix/f/eps-24.png [new file with mode: 0644]
pix/f/eps-256.png [new file with mode: 0644]
pix/f/eps-32.png [new file with mode: 0644]
pix/f/eps-48.png [new file with mode: 0644]
pix/f/eps-64.png [new file with mode: 0644]
pix/f/eps-72.png [new file with mode: 0644]
pix/f/eps-80.png [new file with mode: 0644]
pix/f/eps-96.png [new file with mode: 0644]
pix/f/eps.png [new file with mode: 0644]
pix/f/excel-32.png [deleted file]
pix/f/excel.gif [deleted file]
pix/f/flash-128.png [new file with mode: 0644]
pix/f/flash-24.png [new file with mode: 0644]
pix/f/flash-256.png [new file with mode: 0644]
pix/f/flash-32.png [new file with mode: 0644]
pix/f/flash-48.png [new file with mode: 0644]
pix/f/flash-64.png [new file with mode: 0644]
pix/f/flash-72.png [new file with mode: 0644]
pix/f/flash-80.png [new file with mode: 0644]
pix/f/flash-96.png [new file with mode: 0644]
pix/f/flash.gif [deleted file]
pix/f/flash.png [new file with mode: 0644]
pix/f/folder-128.png [new file with mode: 0644]
pix/f/folder-24.png [new file with mode: 0644]
pix/f/folder-32.png
pix/f/folder-48.png [new file with mode: 0644]
pix/f/folder-64.png [new file with mode: 0644]
pix/f/folder-open-128.png [new file with mode: 0644]
pix/f/folder-open-24.png [new file with mode: 0644]
pix/f/folder-open-32.png [new file with mode: 0644]
pix/f/folder-open-48.png [new file with mode: 0644]
pix/f/folder-open-64.png [new file with mode: 0644]
pix/f/folder-open.png [new file with mode: 0644]
pix/f/folder.gif [deleted file]
pix/f/folder.png [new file with mode: 0644]
pix/f/gif-128.png [new file with mode: 0644]
pix/f/gif-24.png [new file with mode: 0644]
pix/f/gif-256.png [new file with mode: 0644]
pix/f/gif-32.png [new file with mode: 0644]
pix/f/gif-48.png [new file with mode: 0644]
pix/f/gif-64.png [new file with mode: 0644]
pix/f/gif-72.png [new file with mode: 0644]
pix/f/gif-80.png [new file with mode: 0644]
pix/f/gif-96.png [new file with mode: 0644]
pix/f/gif.png [new file with mode: 0644]
pix/f/html-128.png [new file with mode: 0644]
pix/f/html-24.png [new file with mode: 0644]
pix/f/html-256.png [new file with mode: 0644]
pix/f/html-32.png
pix/f/html-48.png [new file with mode: 0644]
pix/f/html-64.png [new file with mode: 0644]
pix/f/html-72.png [new file with mode: 0644]
pix/f/html-80.png [new file with mode: 0644]
pix/f/html-96.png [new file with mode: 0644]
pix/f/html.gif [deleted file]
pix/f/html.png [new file with mode: 0644]
pix/f/image-128.png [new file with mode: 0644]
pix/f/image-24.png [new file with mode: 0644]
pix/f/image-256.png [new file with mode: 0644]
pix/f/image-32.png
pix/f/image-48.png [new file with mode: 0644]
pix/f/image-64.png [new file with mode: 0644]
pix/f/image-72.png [new file with mode: 0644]
pix/f/image-80.png [new file with mode: 0644]
pix/f/image-96.png [new file with mode: 0644]
pix/f/image.gif [deleted file]
pix/f/image.png [new file with mode: 0644]
pix/f/jbc.gif [deleted file]
pix/f/jcl.gif [deleted file]
pix/f/jcw.gif [deleted file]
pix/f/jmt.gif [deleted file]
pix/f/jmx.gif [deleted file]
pix/f/jpeg-128.png [new file with mode: 0644]
pix/f/jpeg-24.png [new file with mode: 0644]
pix/f/jpeg-256.png [new file with mode: 0644]
pix/f/jpeg-32.png [new file with mode: 0644]
pix/f/jpeg-48.png [new file with mode: 0644]
pix/f/jpeg-64.png [new file with mode: 0644]
pix/f/jpeg-72.png [new file with mode: 0644]
pix/f/jpeg-80.png [new file with mode: 0644]
pix/f/jpeg-96.png [new file with mode: 0644]
pix/f/jpeg.png [new file with mode: 0644]
pix/f/jqz.gif [deleted file]
pix/f/moodle-128.png [new file with mode: 0644]
pix/f/moodle-24.png [new file with mode: 0644]
pix/f/moodle-256.png [new file with mode: 0644]
pix/f/moodle-32.png [new file with mode: 0644]
pix/f/moodle-48.png [new file with mode: 0644]
pix/f/moodle-64.png [new file with mode: 0644]
pix/f/moodle-72.png [new file with mode: 0644]
pix/f/moodle-80.png [new file with mode: 0644]
pix/f/moodle-96.png [new file with mode: 0644]
pix/f/moodle.png [new file with mode: 0644]
pix/f/mov-128.png [new file with mode: 0644]
pix/f/mov-24.png [new file with mode: 0644]
pix/f/mov-256.png [new file with mode: 0644]
pix/f/mov-32.png [new file with mode: 0644]
pix/f/mov-48.png [new file with mode: 0644]
pix/f/mov-64.png [new file with mode: 0644]
pix/f/mov-72.png [new file with mode: 0644]
pix/f/mov-80.png [new file with mode: 0644]
pix/f/mov-96.png [new file with mode: 0644]
pix/f/mov.png [new file with mode: 0644]
pix/f/mp3-128.png [new file with mode: 0644]
pix/f/mp3-24.png [new file with mode: 0644]
pix/f/mp3-256.png [new file with mode: 0644]
pix/f/mp3-32.png [new file with mode: 0644]
pix/f/mp3-48.png [new file with mode: 0644]
pix/f/mp3-64.png [new file with mode: 0644]
pix/f/mp3-72.png [new file with mode: 0644]
pix/f/mp3-80.png [new file with mode: 0644]
pix/f/mp3-96.png [new file with mode: 0644]
pix/f/mp3.png [new file with mode: 0644]
pix/f/mpeg-128.png [new file with mode: 0644]
pix/f/mpeg-24.png [new file with mode: 0644]
pix/f/mpeg-256.png [new file with mode: 0644]
pix/f/mpeg-32.png [new file with mode: 0644]
pix/f/mpeg-48.png [new file with mode: 0644]
pix/f/mpeg-64.png [new file with mode: 0644]
pix/f/mpeg-72.png [new file with mode: 0644]
pix/f/mpeg-80.png [new file with mode: 0644]
pix/f/mpeg-96.png [new file with mode: 0644]
pix/f/mpeg.png [new file with mode: 0644]
pix/f/odb-128.png [new file with mode: 0644]
pix/f/odb-24.png [new file with mode: 0644]
pix/f/odb-32.png [new file with mode: 0644]
pix/f/odb-48.png [new file with mode: 0644]
pix/f/odb-64.png [new file with mode: 0644]
pix/f/odb-72.png [new file with mode: 0644]
pix/f/odb-80.png [new file with mode: 0644]
pix/f/odb-96.png [new file with mode: 0644]
pix/f/odb.gif [deleted file]
pix/f/odb.png [new file with mode: 0644]
pix/f/odc-128.png [new file with mode: 0644]
pix/f/odc-24.png [new file with mode: 0644]
pix/f/odc-32.png [new file with mode: 0644]
pix/f/odc-48.png [new file with mode: 0644]
pix/f/odc-64.png [new file with mode: 0644]
pix/f/odc-72.png [new file with mode: 0644]
pix/f/odc-80.png [new file with mode: 0644]
pix/f/odc-96.png [new file with mode: 0644]
pix/f/odc.gif [deleted file]
pix/f/odc.png [new file with mode: 0644]
pix/f/odf-128.png [new file with mode: 0644]
pix/f/odf-24.png [new file with mode: 0644]
pix/f/odf-32.png [new file with mode: 0644]
pix/f/odf-48.png [new file with mode: 0644]
pix/f/odf-64.png [new file with mode: 0644]
pix/f/odf-72.png [new file with mode: 0644]
pix/f/odf-80.png [new file with mode: 0644]
pix/f/odf-96.png [new file with mode: 0644]
pix/f/odf.gif [deleted file]
pix/f/odf.png [new file with mode: 0644]
pix/f/odg-128.png [new file with mode: 0644]
pix/f/odg-24.png [new file with mode: 0644]
pix/f/odg-32.png [new file with mode: 0644]
pix/f/odg-48.png [new file with mode: 0644]
pix/f/odg-64.png [new file with mode: 0644]
pix/f/odg-72.png [new file with mode: 0644]
pix/f/odg-80.png [new file with mode: 0644]
pix/f/odg-96.png [new file with mode: 0644]
pix/f/odg.gif [deleted file]
pix/f/odg.png [new file with mode: 0644]
pix/f/odi.gif [deleted file]
pix/f/odm.gif [deleted file]
pix/f/odp-128.png [new file with mode: 0644]
pix/f/odp-24.png [new file with mode: 0644]
pix/f/odp-32.png
pix/f/odp-48.png [new file with mode: 0644]
pix/f/odp-64.png [new file with mode: 0644]
pix/f/odp-72.png [new file with mode: 0644]
pix/f/odp-80.png [new file with mode: 0644]
pix/f/odp-96.png [new file with mode: 0644]
pix/f/odp.gif [deleted file]
pix/f/odp.png [new file with mode: 0644]
pix/f/ods-128.png [new file with mode: 0644]
pix/f/ods-24.png [new file with mode: 0644]
pix/f/ods-32.png [new file with mode: 0644]
pix/f/ods-48.png [new file with mode: 0644]
pix/f/ods-64.png [new file with mode: 0644]
pix/f/ods-72.png [new file with mode: 0644]
pix/f/ods-80.png [new file with mode: 0644]
pix/f/ods-96.png [new file with mode: 0644]
pix/f/ods.gif [deleted file]
pix/f/ods.png [new file with mode: 0644]
pix/f/odt-128.png [new file with mode: 0644]
pix/f/odt-24.png [new file with mode: 0644]
pix/f/odt-32.png
pix/f/odt-48.png [new file with mode: 0644]
pix/f/odt-64.png [new file with mode: 0644]
pix/f/odt-72.png [new file with mode: 0644]
pix/f/odt-80.png [new file with mode: 0644]
pix/f/odt-96.png [new file with mode: 0644]
pix/f/odt.gif [deleted file]
pix/f/odt.png [new file with mode: 0644]
pix/f/oth-128.png [new file with mode: 0644]
pix/f/oth-24.png [new file with mode: 0644]
pix/f/oth-32.png [new file with mode: 0644]
pix/f/oth-48.png [new file with mode: 0644]
pix/f/oth-64.png [new file with mode: 0644]
pix/f/oth-72.png [new file with mode: 0644]
pix/f/oth-80.png [new file with mode: 0644]
pix/f/oth-96.png [new file with mode: 0644]
pix/f/oth.png [new file with mode: 0644]
pix/f/pdf-128.png [new file with mode: 0644]
pix/f/pdf-24.png [new file with mode: 0644]
pix/f/pdf-256.png [new file with mode: 0644]
pix/f/pdf-32.png [new file with mode: 0644]
pix/f/pdf-48.png [new file with mode: 0644]
pix/f/pdf-64.png [new file with mode: 0644]
pix/f/pdf-72.png [new file with mode: 0644]
pix/f/pdf-80.png [new file with mode: 0644]
pix/f/pdf-96.png [new file with mode: 0644]
pix/f/pdf.gif [deleted file]
pix/f/pdf.png [new file with mode: 0644]
pix/f/png-128.png [new file with mode: 0644]
pix/f/png-24.png [new file with mode: 0644]
pix/f/png-256.png [new file with mode: 0644]
pix/f/png-32.png [new file with mode: 0644]
pix/f/png-48.png [new file with mode: 0644]
pix/f/png-64.png [new file with mode: 0644]
pix/f/png-72.png [new file with mode: 0644]
pix/f/png-80.png [new file with mode: 0644]
pix/f/png-96.png [new file with mode: 0644]
pix/f/png.png [new file with mode: 0644]
pix/f/potm.gif [deleted file]
pix/f/potx.gif [deleted file]
pix/f/powerpoint-128.png [new file with mode: 0644]
pix/f/powerpoint-24.png [new file with mode: 0644]
pix/f/powerpoint-256.png [new file with mode: 0644]
pix/f/powerpoint-32.png
pix/f/powerpoint-48.png [new file with mode: 0644]
pix/f/powerpoint-64.png [new file with mode: 0644]
pix/f/powerpoint-72.png [new file with mode: 0644]
pix/f/powerpoint-80.png [new file with mode: 0644]
pix/f/powerpoint-96.png [new file with mode: 0644]
pix/f/powerpoint.gif [deleted file]
pix/f/powerpoint.png [new file with mode: 0644]
pix/f/ppam.gif [deleted file]
pix/f/pps-32.png [deleted file]
pix/f/ppsm.gif [deleted file]
pix/f/ppsx-32.png [deleted file]
pix/f/ppsx.gif [deleted file]
pix/f/pptm.gif [deleted file]
pix/f/pptx-128.png [new file with mode: 0644]
pix/f/pptx-24.png [new file with mode: 0644]
pix/f/pptx-256.png [new file with mode: 0644]
pix/f/pptx-32.png
pix/f/pptx-48.png [new file with mode: 0644]
pix/f/pptx-64.png [new file with mode: 0644]
pix/f/pptx-72.png [new file with mode: 0644]
pix/f/pptx-80.png [new file with mode: 0644]
pix/f/pptx-96.png [new file with mode: 0644]
pix/f/pptx.gif [deleted file]
pix/f/pptx.png [new file with mode: 0644]
pix/f/psd-128.png [new file with mode: 0644]
pix/f/psd-24.png [new file with mode: 0644]
pix/f/psd-256.png [new file with mode: 0644]
pix/f/psd-32.png [new file with mode: 0644]
pix/f/psd-48.png [new file with mode: 0644]
pix/f/psd-64.png [new file with mode: 0644]
pix/f/psd-72.png [new file with mode: 0644]
pix/f/psd-80.png [new file with mode: 0644]
pix/f/psd-96.png [new file with mode: 0644]
pix/f/psd.png [new file with mode: 0644]
pix/f/spreadsheet-128.png [new file with mode: 0644]
pix/f/spreadsheet-24.png [new file with mode: 0644]
pix/f/spreadsheet-256.png [new file with mode: 0644]
pix/f/spreadsheet-32.png [new file with mode: 0644]
pix/f/spreadsheet-48.png [new file with mode: 0644]
pix/f/spreadsheet-64.png [new file with mode: 0644]
pix/f/spreadsheet-72.png [new file with mode: 0644]
pix/f/spreadsheet-80.png [new file with mode: 0644]
pix/f/spreadsheet-96.png [new file with mode: 0644]
pix/f/spreadsheet.png [new file with mode: 0644]
pix/f/text-128.png [new file with mode: 0644]
pix/f/text-24.png [new file with mode: 0644]
pix/f/text-256.png [new file with mode: 0644]
pix/f/text-32.png
pix/f/text-48.png [new file with mode: 0644]
pix/f/text-64.png [new file with mode: 0644]
pix/f/text-72.png [new file with mode: 0644]
pix/f/text-80.png [new file with mode: 0644]
pix/f/text-96.png [new file with mode: 0644]
pix/f/text.gif [deleted file]
pix/f/text.png [new file with mode: 0644]
pix/f/tiff-128.png [new file with mode: 0644]
pix/f/tiff-24.png [new file with mode: 0644]
pix/f/tiff-256.png [new file with mode: 0644]
pix/f/tiff-32.png [new file with mode: 0644]
pix/f/tiff-48.png [new file with mode: 0644]
pix/f/tiff-64.png [new file with mode: 0644]
pix/f/tiff-72.png [new file with mode: 0644]
pix/f/tiff-80.png [new file with mode: 0644]
pix/f/tiff-96.png [new file with mode: 0644]
pix/f/tiff.png [new file with mode: 0644]
pix/f/unknown-128.png [new file with mode: 0644]
pix/f/unknown-24.png [new file with mode: 0644]
pix/f/unknown-256.png [new file with mode: 0644]
pix/f/unknown-32.png
pix/f/unknown-48.png [new file with mode: 0644]
pix/f/unknown-64.png [new file with mode: 0644]
pix/f/unknown-72.png [new file with mode: 0644]
pix/f/unknown-80.png [new file with mode: 0644]
pix/f/unknown-96.png [new file with mode: 0644]
pix/f/unknown.gif [deleted file]
pix/f/unknown.png [new file with mode: 0644]
pix/f/video-128.png [new file with mode: 0644]
pix/f/video-24.png [new file with mode: 0644]
pix/f/video-256.png [new file with mode: 0644]
pix/f/video-32.png
pix/f/video-48.png [new file with mode: 0644]
pix/f/video-64.png [new file with mode: 0644]
pix/f/video-72.png [new file with mode: 0644]
pix/f/video-80.png [new file with mode: 0644]
pix/f/video-96.png [new file with mode: 0644]
pix/f/video.gif [deleted file]
pix/f/video.png [new file with mode: 0644]
pix/f/wav-128.png [new file with mode: 0644]
pix/f/wav-24.png [new file with mode: 0644]
pix/f/wav-256.png [new file with mode: 0644]
pix/f/wav-32.png [new file with mode: 0644]
pix/f/wav-48.png [new file with mode: 0644]
pix/f/wav-64.png [new file with mode: 0644]
pix/f/wav-72.png [new file with mode: 0644]
pix/f/wav-80.png [new file with mode: 0644]
pix/f/wav-96.png [new file with mode: 0644]
pix/f/wav.png [new file with mode: 0644]
pix/f/web-32.png [deleted file]
pix/f/web.gif [deleted file]
pix/f/wma-128.png [new file with mode: 0644]
pix/f/wma-24.png [new file with mode: 0644]
pix/f/wma-256.png [new file with mode: 0644]
pix/f/wma-32.png [new file with mode: 0644]
pix/f/wma-48.png [new file with mode: 0644]
pix/f/wma-64.png [new file with mode: 0644]
pix/f/wma-72.png [new file with mode: 0644]
pix/f/wma-80.png [new file with mode: 0644]
pix/f/wma-96.png [new file with mode: 0644]
pix/f/wma.png [new file with mode: 0644]
pix/f/wmv-128.png [new file with mode: 0644]
pix/f/wmv-24.png [new file with mode: 0644]
pix/f/wmv-256.png [new file with mode: 0644]
pix/f/wmv-32.png [new file with mode: 0644]
pix/f/wmv-48.png [new file with mode: 0644]
pix/f/wmv-64.png [new file with mode: 0644]
pix/f/wmv-72.png [new file with mode: 0644]
pix/f/wmv-80.png [new file with mode: 0644]
pix/f/wmv-96.png [new file with mode: 0644]
pix/f/wmv.png [new file with mode: 0644]
pix/f/word-32.png [deleted file]
pix/f/word.gif [deleted file]
pix/f/xlam.gif [deleted file]
pix/f/xlsb.gif [deleted file]
pix/f/xlsm.gif [deleted file]
pix/f/xlsx-128.png [new file with mode: 0644]
pix/f/xlsx-24.png [new file with mode: 0644]
pix/f/xlsx-256.png [new file with mode: 0644]
pix/f/xlsx-32.png [new file with mode: 0644]
pix/f/xlsx-48.png [new file with mode: 0644]
pix/f/xlsx-64.png [new file with mode: 0644]
pix/f/xlsx-72.png [new file with mode: 0644]
pix/f/xlsx-80.png [new file with mode: 0644]
pix/f/xlsx-96.png [new file with mode: 0644]
pix/f/xlsx.gif [deleted file]
pix/f/xlsx.png [new file with mode: 0644]
pix/f/xltm.gif [deleted file]
pix/f/xltx.gif [deleted file]
pix/f/xml-128.png [new file with mode: 0644]
pix/f/xml-24.png [new file with mode: 0644]
pix/f/xml-256.png [new file with mode: 0644]
pix/f/xml-32.png
pix/f/xml-48.png [new file with mode: 0644]
pix/f/xml-64.png [new file with mode: 0644]
pix/f/xml-72.png [new file with mode: 0644]
pix/f/xml-80.png [new file with mode: 0644]
pix/f/xml-96.png [new file with mode: 0644]
pix/f/xml.gif [deleted file]
pix/f/xml.png [new file with mode: 0644]
pix/f/zip-128.png [new file with mode: 0644]
pix/f/zip-24.png [new file with mode: 0644]
pix/f/zip-256.png [new file with mode: 0644]
pix/f/zip-32.png
pix/f/zip-48.png [new file with mode: 0644]
pix/f/zip-64.png [new file with mode: 0644]
pix/f/zip-72.png [new file with mode: 0644]
pix/f/zip-80.png [new file with mode: 0644]
pix/f/zip-96.png [new file with mode: 0644]
pix/f/zip.gif [deleted file]
pix/f/zip.png [new file with mode: 0644]
pix/t/editstring.png [new file with mode: 0644]
pix/y/lm.gif [deleted file]
pix/y/lm.png [new file with mode: 0644]
pix/y/lmh.gif [deleted file]
pix/y/ln.png [new file with mode: 0644]
pix/y/lp.gif [deleted file]
pix/y/lp.png [new file with mode: 0644]
pix/y/lph.gif [deleted file]
pix/y/tm.gif [deleted file]
pix/y/tm.png [new file with mode: 0644]
pix/y/tmh.gif [deleted file]
pix/y/tp.gif [deleted file]
pix/y/tp.png [new file with mode: 0644]
pix/y/tph.gif [deleted file]
question/editlib.php
question/engine/simpletest/helpers.php [new file with mode: 0644]
question/engine/tests/helpers.php
question/engine/upgrade/tests/helper.php
question/preview.php
question/type/calculated/questiontype.php
question/type/essay/renderer.php
question/type/match/tests/walkthrough_test.php
question/type/multianswer/tests/upgradelibnewqe_test.php
report/completion/index.php
report/progress/index.php
repository/alfresco/lib.php
repository/boxnet/lib.php
repository/coursefiles/lib.php
repository/draftfiles_ajax.php
repository/draftfiles_manager.php
repository/dropbox/lib.php
repository/dropbox/locallib.php
repository/filepicker.js
repository/filesystem/lang/en/repository_filesystem.php
repository/filesystem/lib.php
repository/flickr/lib.php
repository/flickr_public/lib.php
repository/googledocs/lib.php
repository/lib.php
repository/local/lib.php
repository/merlot/lib.php
repository/picasa/lib.php
repository/recent/lib.php
repository/repository_ajax.php
repository/s3/lib.php
repository/tests/repository_test.php [new file with mode: 0644]
repository/upload/lib.php
repository/url/db/install.php [new file with mode: 0644]
repository/url/lib.php
repository/user/lib.php
repository/webdav/lib.php
repository/wikimedia/db/install.php [new file with mode: 0644]
repository/wikimedia/lib.php
repository/wikimedia/wikimedia.php
repository/youtube/db/install.php [new file with mode: 0644]
repository/youtube/lib.php
theme/afterburner/style/afterburner_styles.css
theme/base/config.php
theme/base/pix/fp/alias.png [new file with mode: 0644]
theme/base/pix/fp/check.png [new file with mode: 0644]
theme/base/pix/fp/cross.png [new file with mode: 0644]
theme/base/pix/fp/dnd_arrow.png [new file with mode: 0644]
theme/base/pix/fp/link.png [new file with mode: 0644]
theme/base/pix/fp/path_folder.png [new file with mode: 0644]
theme/base/pix/fp/view_icon_active.png [new file with mode: 0644]
theme/base/pix/fp/view_icon_inactive.png [new file with mode: 0644]
theme/base/pix/fp/view_icon_selected.png [new file with mode: 0644]
theme/base/pix/fp/view_list_active.png [new file with mode: 0644]
theme/base/pix/fp/view_list_inactive.png [new file with mode: 0644]
theme/base/pix/fp/view_list_selected.png [new file with mode: 0644]
theme/base/pix/fp/view_tree_active.png [new file with mode: 0644]
theme/base/pix/fp/view_tree_inactive.png [new file with mode: 0644]
theme/base/pix/fp/view_tree_selected.png [new file with mode: 0644]
theme/base/style/core.css
theme/base/style/course.css
theme/base/style/filemanager.css [new file with mode: 0644]
theme/base/style/question.css
theme/formal_white/config.php
theme/formal_white/style/core.css
theme/formal_white/style/formal_white.css
theme/mymobile/layout/embedded.php
user/filesedit.php
user/filesedit_form.php
user/renderer.php
version.php
webservice/rest/locallib.php
webservice/soap/locallib.php
webservice/xmlrpc/locallib.php

index 6ef4b20..5b01be2 100644 (file)
@@ -240,7 +240,9 @@ if ($version > $CFG->version) {  // upgrade
 
         if ($fetchupdates) {
             // no sesskey support guaranteed here
-            available_update_checker::instance()->fetch();
+            if (empty($CFG->disableupdatenotifications)) {
+                available_update_checker::instance()->fetch();
+            }
             redirect($reloadurl);
         }
 
index f6a2c11..b7a1bab 100644 (file)
@@ -174,6 +174,7 @@ class core_admin_renderer extends plugin_renderer_base {
      */
     public function upgrade_plugin_check_page(plugin_manager $pluginman, available_update_checker $checker,
             $version, $showallplugins, $reloadurl, $continueurl) {
+        global $CFG;
 
         $output = '';
 
@@ -181,14 +182,16 @@ class core_admin_renderer extends plugin_renderer_base {
         $output .= $this->box_start('generalbox');
         $output .= $this->container_start('generalbox', 'notice');
         $output .= html_writer::tag('p', get_string('pluginchecknotice', 'core_plugin'));
-        $output .= $this->container_start('checkforupdates');
-        $output .= $this->single_button(new moodle_url($reloadurl, array('fetchupdates' => 1)), get_string('checkforupdates', 'core_plugin'));
-        if ($timefetched = $checker->get_last_timefetched()) {
-            $output .= $this->container(get_string('checkforupdateslast', 'core_plugin',
-                userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'))));
+        if (empty($CFG->disableupdatenotifications)) {
+            $output .= $this->container_start('checkforupdates');
+            $output .= $this->single_button(new moodle_url($reloadurl, array('fetchupdates' => 1)), get_string('checkforupdates', 'core_plugin'));
+            if ($timefetched = $checker->get_last_timefetched()) {
+                $output .= $this->container(get_string('checkforupdateslast', 'core_plugin',
+                    userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'))));
+            }
+            $output .= $this->container_end();
         }
         $output .= $this->container_end();
-        $output .= $this->container_end();
 
         $output .= $this->plugins_check_table($pluginman, $version, array('full' => $showallplugins));
         $output .= $this->box_end();
@@ -227,11 +230,12 @@ class core_admin_renderer extends plugin_renderer_base {
      */
     public function admin_notifications_page($maturity, $insecuredataroot, $errorsdisplayed,
             $cronoverdue, $dbproblems, $maintenancemode, $availableupdates, $availableupdatesfetch) {
+        global $CFG;
         $output = '';
 
         $output .= $this->header();
         $output .= $this->maturity_info($maturity);
-        $output .= $this->available_updates($availableupdates, $availableupdatesfetch);
+        $output .= empty($CFG->disableupdatenotifications) ? $this->available_updates($availableupdates, $availableupdatesfetch) : '';
         $output .= $this->insecure_dataroot_warning($insecuredataroot);
         $output .= $this->display_errors_warning($errorsdisplayed);
         $output .= $this->cron_overdue_warning($cronoverdue);
@@ -256,19 +260,23 @@ class core_admin_renderer extends plugin_renderer_base {
      * @return string HTML to output.
      */
     public function plugin_management_page(plugin_manager $pluginman, available_update_checker $checker) {
+        global $CFG;
+
         $output = '';
 
         $output .= $this->header();
         $output .= $this->heading(get_string('pluginsoverview', 'core_admin'));
         $output .= $this->plugins_overview_panel($pluginman);
 
-        $output .= $this->container_start('checkforupdates');
-        $output .= $this->single_button(new moodle_url($this->page->url, array('fetchremote' => 1)), get_string('checkforupdates', 'core_plugin'));
-        if ($timefetched = $checker->get_last_timefetched()) {
-            $output .= $this->container(get_string('checkforupdateslast', 'core_plugin',
-                userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'))));
+        if (empty($CFG->disableupdatenotifications)) {
+            $output .= $this->container_start('checkforupdates');
+            $output .= $this->single_button(new moodle_url($this->page->url, array('fetchremote' => 1)), get_string('checkforupdates', 'core_plugin'));
+            if ($timefetched = $checker->get_last_timefetched()) {
+                $output .= $this->container(get_string('checkforupdateslast', 'core_plugin',
+                    userdate($timefetched, get_string('strftimedatetime', 'core_langconfig'))));
+            }
+            $output .= $this->container_end();
         }
-        $output .= $this->container_end();
 
         $output .= $this->box($this->plugins_control_panel($pluginman), 'generalbox');
         $output .= $this->footer();
@@ -466,7 +474,10 @@ class core_admin_renderer extends plugin_renderer_base {
                 $updateinfo .= $this->moodle_available_update_info($update);
             }
         } else {
-            $updateinfo .= $this->heading(get_string('updateavailablenot', 'core_admin'), 3);
+            $now = time();
+            if ($fetch and ($fetch <= $now) and ($now - $fetch < HOURSECS)) {
+                $updateinfo .= $this->heading(get_string('updateavailablenot', 'core_admin'), 3);
+            }
         }
 
         $updateinfo .= $this->container_start('checkforupdates');
@@ -559,6 +570,8 @@ class core_admin_renderer extends plugin_renderer_base {
      * @return string HTML code
      */
     public function plugins_check_table(plugin_manager $pluginman, $version, array $options = null) {
+        global $CFG;
+
         $plugininfo = $pluginman->get_plugins();
 
         if (empty($plugininfo)) {
@@ -641,7 +654,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 $status = get_string('status_' . $statuscode, 'core_plugin');
 
                 $availableupdates = $plugin->available_updates();
-                if (!empty($availableupdates)) {
+                if (!empty($availableupdates) and empty($CFG->disableupdatenotifications)) {
                     foreach ($availableupdates as $availableupdate) {
                         $status .= $this->plugin_available_update_info($availableupdate);
                     }
@@ -771,6 +784,8 @@ class core_admin_renderer extends plugin_renderer_base {
      * @return string as usually
      */
     public function plugins_overview_panel(plugin_manager $pluginman) {
+        global $CFG;
+
         $plugininfo = $pluginman->get_plugins();
 
         $numtotal = $numdisabled = $numextension = $numupdatable = 0;
@@ -787,7 +802,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 if (!$plugin->is_standard()) {
                     $numextension++;
                 }
-                if ($plugin->available_updates()) {
+                if (empty($CFG->disableupdatenotifications) and $plugin->available_updates()) {
                     $numupdatable++;
                 }
             }
@@ -813,6 +828,8 @@ class core_admin_renderer extends plugin_renderer_base {
      * @return string HTML code
      */
     public function plugins_control_panel(plugin_manager $pluginman) {
+        global $CFG;
+
         $plugininfo = $pluginman->get_plugins();
 
         if (empty($plugininfo)) {
@@ -916,7 +933,7 @@ class core_admin_renderer extends plugin_renderer_base {
                 }
 
                 $updateinfo = '';
-                if (is_array($plugin->available_updates())) {
+                if (empty($CFG->disableupdatenotifications) and is_array($plugin->available_updates())) {
                     foreach ($plugin->available_updates() as $availableupdate) {
                         $updateinfo .= $this->plugin_available_update_info($availableupdate);
                     }
index c3409a5..b3494ca 100644 (file)
@@ -1,12 +1,27 @@
 <?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/>.
 
 require_once(dirname(dirname(__FILE__)) . '/config.php');
 require_once($CFG->dirroot . '/repository/lib.php');
 require_once($CFG->libdir . '/adminlib.php');
 
-$repository    = optional_param('repos', '', PARAM_FORMAT);
-$action        = optional_param('action', '', PARAM_ALPHA);
-$sure          = optional_param('sure', '', PARAM_ALPHA);
+$repository       = optional_param('repos', '', PARAM_ALPHANUMEXT);
+$action           = optional_param('action', '', PARAM_ACTION);
+$sure             = optional_param('sure', '', PARAM_ALPHA);
+$downloadcontents = optional_param('downloadcontents', false, PARAM_BOOL);
 
 $display = true; // fall through to normal display
 
@@ -42,6 +57,10 @@ $configstr  = get_string('manage', 'repository');
 
 $return = true;
 
+if (!empty($action)) {
+    require_sesskey();
+}
+
 /**
  * Helper function that generates a moodle_url object
  * relevant to the repository
@@ -152,10 +171,10 @@ if (($action == 'edit') || ($action == 'new')) {
 
         // Display instances list and creation form
         if ($action == 'edit') {
-           $instanceoptionnames = repository::static_function($repository, 'get_instance_option_names');
-           if (!empty($instanceoptionnames)) {
-               repository::display_instances_list(get_context_instance(CONTEXT_SYSTEM), $repository);
-           }
+            $instanceoptionnames = repository::static_function($repository, 'get_instance_option_names');
+            if (!empty($instanceoptionnames)) {
+                repository::display_instances_list(context_system::instance(), $repository);
+            }
         }
     }
 } else if ($action == 'show') {
@@ -185,7 +204,8 @@ if (($action == 'edit') || ($action == 'new')) {
         if (!confirm_sesskey()) {
             print_error('confirmsesskeybad', '', $baseurl);
         }
-        if ($repositorytype->delete()) {
+
+        if ($repositorytype->delete($downloadcontents)) {
             redirect($baseurl);
         } else {
             print_error('instancenotdeleted', 'repository', $baseurl);
@@ -193,7 +213,34 @@ if (($action == 'edit') || ($action == 'new')) {
         exit;
     } else {
         echo $OUTPUT->header();
-        echo $OUTPUT->confirm(get_string('confirmremove', 'repository', $repositorytype->get_readablename()), $sesskeyurl . '&action=delete&repos=' . $repository . '&sure=yes', $baseurl);
+
+        $message = get_string('confirmremove', 'repository', $repositorytype->get_readablename());
+
+        $output = $OUTPUT->box_start('generalbox', 'notice');
+        $output .= html_writer::tag('p', $message);
+
+        $removeurl = new moodle_url($sesskeyurl);
+        $removeurl->params(array(
+            'action' =>'delete',
+            'repos' => $repository,
+            'sure' => 'yes',
+        ));
+
+        $removeanddownloadurl = new moodle_url($sesskeyurl);
+        $removeanddownloadurl->params(array(
+            'action' =>'delete',
+            'repos'=> $repository,
+            'sure' => 'yes',
+            'downloadcontents' => 1,
+        ));
+
+        $output .= $OUTPUT->single_button($removeurl, get_string('continueuninstall', 'repository'));
+        $output .= $OUTPUT->single_button($removeanddownloadurl, get_string('continueuninstallanddownload', 'repository'));
+        $output .= $OUTPUT->single_button($baseurl, get_string('cancel'));
+        $output .= $OUTPUT->box_end();
+
+        echo $output;
+
         $return = false;
     }
 } else if ($action == 'moveup') {
@@ -255,7 +302,7 @@ if (($action == 'edit') || ($action == 'new')) {
                 // Calculate number of instances in order to display them for the Moodle administrator
                 if (!empty($instanceoptionnames)) {
                     $params = array();
-                    $params['context'] = array(get_system_context());
+                    $params['context'] = array(context_system::instance());
                     $params['onlyvisible'] = false;
                     $params['type'] = $typename;
                     $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
index e75193d..fe75146 100644 (file)
@@ -1,18 +1,35 @@
 <?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/>.
 
 require_once(dirname(dirname(__FILE__)) . '/config.php');
 require_once($CFG->dirroot . '/repository/lib.php');
 require_once($CFG->libdir . '/adminlib.php');
 
+require_sesskey();
+
 // id of repository
 $edit    = optional_param('edit', 0, PARAM_INT);
-$new     = optional_param('new', '', PARAM_FORMAT);
+$new     = optional_param('new', '', PARAM_PLUGIN);
 $hide    = optional_param('hide', 0, PARAM_INT);
 $delete  = optional_param('delete', 0, PARAM_INT);
 $sure    = optional_param('sure', '', PARAM_ALPHA);
 $type    = optional_param('type', '', PARAM_PLUGIN);
+$downloadcontents = optional_param('downloadcontents', false, PARAM_BOOL);
 
-$context = get_context_instance(CONTEXT_SYSTEM);
+$context = context_system::instance();
 
 $pagename = 'repositorycontroller';
 
@@ -24,16 +41,20 @@ if ($edit){
     $pagename = 'repositoryinstancenew';
 }
 
-admin_externalpage_setup($pagename);
-require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+admin_externalpage_setup($pagename, '', null, new moodle_url('/admin/repositoryinstances.php'));
+require_capability('moodle/site:config', $context);
+
+$baseurl = new moodle_url("/$CFG->admin/repositoryinstance.php", array('sesskey'=>sesskey()));
+
+$parenturl = new moodle_url("/$CFG->admin/repository.php", array(
+    'sesskey'=>sesskey(),
+    'action'=>'edit',
+));
 
-$sesskeyurl = "$CFG->wwwroot/$CFG->admin/repositoryinstance.php?sesskey=" . sesskey();
-$baseurl    = "$CFG->wwwroot/$CFG->admin/repository.php?session=". sesskey() .'&action=edit&repos=';
 if ($new) {
-    $baseurl .= $new;
-}
-else {
-    $baseurl .= $type;
+    $parenturl->param('repos', $new);
+} else {
+    $parenturl->param('repos', $type);
 }
 
 $return = true;
@@ -48,7 +69,7 @@ if (!empty($edit) || !empty($new)) {
         $typeid = $instance->options['typeid'];
     } else {
         $plugin = $new;
-        $typeid = $new;
+        $typeid = null;
         $instance = null;
     }
 
@@ -57,12 +78,9 @@ if (!empty($edit) || !empty($new)) {
     // end setup, begin output
 
     if ($mform->is_cancelled()){
-        redirect($baseurl);
+        redirect($parenturl);
         exit;
     } else if ($fromform = $mform->get_data()){
-        if (!confirm_sesskey()) {
-            print_error('confirmsesskeybad', '', $baseurl);
-        }
         if ($edit) {
             $settings = array();
             $settings['name'] = $fromform->name;
@@ -77,13 +95,13 @@ if (!empty($edit) || !empty($new)) {
             }
             $success = $instance->set_option($settings);
         } else {
-            $success = repository::static_function($plugin, 'create', $plugin, 0, get_system_context(), $fromform);
+            $success = repository::static_function($plugin, 'create', $plugin, 0, $context, $fromform);
             $data = data_submitted();
         }
         if ($success) {
-            redirect($baseurl);
+            redirect($parenturl);
         } else {
-            print_error('instancenotsaved', 'repository', $baseurl);
+            print_error('instancenotsaved', 'repository', $parenturl);
         }
         exit;
     } else {
@@ -95,9 +113,6 @@ if (!empty($edit) || !empty($new)) {
         $return = false;
     }
 } else if (!empty($hide)) {
-    if (!confirm_sesskey()) {
-        print_error('confirmsesskeybad', '', $baseurl);
-    }
     $instance = repository::get_type_by_typename($hide);
     $instance->hide();
     $return = true;
@@ -108,25 +123,38 @@ if (!empty($edit) || !empty($new)) {
             throw new repository_exception('readonlyinstance', 'repository');
      }
     if ($sure) {
-        if (!confirm_sesskey()) {
-            print_error('confirmsesskeybad', '', $baseurl);
-        }
-        if ($instance->delete()) {
+        if ($instance->delete($downloadcontents)) {
             $deletedstr = get_string('instancedeleted', 'repository');
-            redirect($baseurl, $deletedstr, 3);
+            redirect($parenturl, $deletedstr, 3);
         } else {
-            print_error('instancenotdeleted', 'repository', $baseurl);
+            print_error('instancenotdeleted', 'repository', $parenturl);
         }
         exit;
     }
 
     echo $OUTPUT->header();
-    echo $OUTPUT->confirm(get_string('confirmdelete', 'repository', $instance->name), "$sesskeyurl&type=$type'&delete=$delete'&sure=yes", "$CFG->wwwroot/$CFG->admin/repositoryinstance.php?session=". sesskey());
+    echo $OUTPUT->box_start('generalbox', 'notice');
+    $continueurl = new moodle_url($baseurl, array(
+        'type' => $type,
+        'delete' => $delete,
+        'sure' => 'yes',
+    ));
+    $continueanddownloadurl = new moodle_url($continueurl, array(
+        'downloadcontents' => 1
+    ));
+    $message = get_string('confirmdelete', 'repository', $instance->name);
+    echo html_writer::tag('p', $message);
+
+    echo $OUTPUT->single_button($continueurl, get_string('continueuninstall', 'repository'));
+    echo $OUTPUT->single_button($continueanddownloadurl, get_string('continueuninstallanddownload', 'repository'));
+    echo $OUTPUT->single_button($parenturl, get_string('cancel'));
+
+    echo $OUTPUT->box_end();
+
     $return = false;
 }
 
 if (!empty($return)) {
-
-    redirect($baseurl);
+    redirect($parenturl);
 }
 echo $OUTPUT->footer();
index 0c4b157..968f025 100644 (file)
@@ -54,6 +54,17 @@ if (!$canview) {
     print_error('nopermissions', 'error', '', get_string('checkpermissions', 'role'));
 }
 
+if ($userid != $USER->id) {
+    // If its not the current user we need to extend the navigation for that user to ensure
+    // their navigation is loaded and this page found upon it.
+    $PAGE->navigation->extend_for_user($user);
+}
+if ($course->id != $SITE->id || $userid != $USER->id) {
+    // If we're within a course OR if we're viewing another user then we need to include the
+    // settings base on the navigation to ensure that the navbar will contain the users name.
+    $PAGE->navbar->includesettingsbase = true;
+}
+
 /// Now get the role assignments for this user.
 $sql = "SELECT
         ra.id, ra.userid, ra.contextid, ra.roleid, ra.component, ra.itemid,
@@ -120,14 +131,9 @@ $title = get_string('xroleassignments', 'role', $fullname);
 $PAGE->set_title($title);
 if ($courseid != SITEID) {
     $PAGE->set_heading($fullname);
-    if (has_capability('moodle/course:viewparticipants', $coursecontext)) {
-        $PAGE->navbar->add(get_string('participants'),new moodle_url('/user/index.php', array('id'=>$courseid)));
-    }
 } else {
     $PAGE->set_heading($course->fullname);
 }
-$PAGE->navbar->add($fullname, new moodle_url("$CFG->wwwroot/user/view.php", array('id'=>$userid,'course'=>$courseid)));
-$PAGE->navbar->add($straction);
 echo $OUTPUT->header();
 echo $OUTPUT->heading($title, 3);
 echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
index 9d7b5a0..e55e1f2 100644 (file)
@@ -13,6 +13,8 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('enablesafebrowserintegration', new lang_string('enablesafebrowserintegration', 'admin'), new lang_string('configenablesafebrowserintegration', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('enablegroupmembersonly', new lang_string('enablegroupmembersonly', 'admin'), new lang_string('configenablegroupmembersonly', 'admin'), 0));
 
+    $temp->add(new admin_setting_configcheckbox('dndallowtextandlinks', new lang_string('dndallowtextandlinks', 'admin'), new lang_string('configdndallowtextandlinks', 'admin'), 0));
+
     $ADMIN->add('experimental', $temp);
 
     // "debugging" settingpage
index 6d192b6..96d0455 100644 (file)
@@ -330,12 +330,12 @@ if ($hassiteconfig) {
       $typeoptionnames = repository::static_function($repositorytype->get_typename(), 'get_type_option_names');
       $instanceoptionnames = repository::static_function($repositorytype->get_typename(), 'get_instance_option_names');
       if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
-            $ADMIN->add('repositorysettings',
-                new admin_externalpage('repositorysettings'.$repositorytype->get_typename(),
-                        $repositorytype->get_readablename(),
-                        $url . '?action=edit&repos=' . $repositorytype->get_typename()),
-                        'moodle/site:config');
-        }
+
+          $params = array('action'=>'edit', 'sesskey'=>sesskey(), 'repos'=>$repositorytype->get_typename());
+          $settingsurl = new moodle_url("/$CFG->admin/repository.php", $params);
+          $repositoryexternalpage = new admin_externalpage('repositorysettings'.$repositorytype->get_typename(), $repositorytype->get_readablename(), $settingsurl);
+          $ADMIN->add('repositorysettings', $repositoryexternalpage);
+      }
     }
 }
 
index b8b674c..0188b6d 100644 (file)
@@ -222,19 +222,21 @@ $ADMIN->add('server', $temp);
 $ADMIN->add('server', new admin_externalpage('adminregistration', new lang_string('registration','admin'), "$CFG->wwwroot/$CFG->admin/registration/index.php"));
 
 // "update notifications" settingpage
-$temp = new admin_settingpage('updatenotifications', new lang_string('updatenotifications', 'core_admin'));
-$temp->add(new admin_setting_configcheckbox('updateautocheck', new lang_string('updateautocheck', 'core_admin'),
-                                            new lang_string('updateautocheck_desc', 'core_admin'), 1));
-$temp->add(new admin_setting_configselect('updateminmaturity', new lang_string('updateminmaturity', 'core_admin'),
-                                          new lang_string('updateminmaturity_desc', 'core_admin'), MATURITY_STABLE,
-                                          array(
-                                              MATURITY_ALPHA  => new lang_string('maturity'.MATURITY_ALPHA, 'core_admin'),
-                                              MATURITY_BETA   => new lang_string('maturity'.MATURITY_BETA, 'core_admin'),
-                                              MATURITY_RC     => new lang_string('maturity'.MATURITY_RC, 'core_admin'),
-                                              MATURITY_STABLE => new lang_string('maturity'.MATURITY_STABLE, 'core_admin'),
-                                          )));
-$temp->add(new admin_setting_configcheckbox('updatenotifybuilds', new lang_string('updatenotifybuilds', 'core_admin'),
-                                            new lang_string('updatenotifybuilds_desc', 'core_admin'), 0));
-$ADMIN->add('server', $temp);
+if (empty($CFG->disableupdatenotifications)) {
+    $temp = new admin_settingpage('updatenotifications', new lang_string('updatenotifications', 'core_admin'));
+    $temp->add(new admin_setting_configcheckbox('updateautocheck', new lang_string('updateautocheck', 'core_admin'),
+                                                new lang_string('updateautocheck_desc', 'core_admin'), 1));
+    $temp->add(new admin_setting_configselect('updateminmaturity', new lang_string('updateminmaturity', 'core_admin'),
+                                              new lang_string('updateminmaturity_desc', 'core_admin'), MATURITY_STABLE,
+                                              array(
+                                                  MATURITY_ALPHA  => new lang_string('maturity'.MATURITY_ALPHA, 'core_admin'),
+                                                  MATURITY_BETA   => new lang_string('maturity'.MATURITY_BETA, 'core_admin'),
+                                                  MATURITY_RC     => new lang_string('maturity'.MATURITY_RC, 'core_admin'),
+                                                  MATURITY_STABLE => new lang_string('maturity'.MATURITY_STABLE, 'core_admin'),
+                                              )));
+    $temp->add(new admin_setting_configcheckbox('updatenotifybuilds', new lang_string('updatenotifybuilds', 'core_admin'),
+                                                new lang_string('updatenotifybuilds_desc', 'core_admin'), 0));
+    $ADMIN->add('server', $temp);
+}
 
 } // end of speedup
index 34e1fa4..e031be4 100644 (file)
@@ -9,6 +9,9 @@
          $zone = clean_param($zone, PARAM_PATH);
     }
 
+    $PAGE->set_url('/admin/timezone.php');
+    $PAGE->set_context(context_system::instance());
+
     require_login();
 
     require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
@@ -18,7 +21,6 @@
     $strusers = get_string("users");
     $strall = get_string("all");
 
-    $PAGE->set_url('/admin/timezone.php');
     $PAGE->set_title($strtimezone);
     $PAGE->set_heading($strtimezone);
     $PAGE->navbar->add($strtimezone);
index 5b32c0f..9b699e9 100644 (file)
@@ -39,7 +39,7 @@ class database_transfer_form extends moodleform {
             'pgsql/native',
             'mssql/native',
             'oci/native',
-            'sqlite3/pdo',
+            'sqlsrv/native',
         );
         $drivers = array();
         foreach($supported as $driver) {
index d629486..3e57e90 100644 (file)
@@ -42,29 +42,40 @@ if ($DB->get_dbfamily() != 'mysql') {
     notice('This function is for MySQL databases only!', new moodle_url('/admin/'));
 }
 
+$prefix = str_replace('_', '\\_', $DB->get_prefix()).'%';
+$sql = "SHOW TABLE STATUS WHERE Name LIKE ? AND Engine <> 'InnoDB'";
+$rs = $DB->get_recordset_sql($sql, array($prefix));
+if (!$rs->valid()) {
+    $rs->close();
+    echo $OUTPUT->box('<p>All tables are already using InnoDB database engine.</p>');
+    echo $OUTPUT->continue_button('/admin/');
+    echo $OUTPUT->footer();
+    die;
+}
+
 if (data_submitted() and $confirm and confirm_sesskey()) {
 
     echo $OUTPUT->notification('Please be patient and wait for this to complete...', 'notifysuccess');
 
     set_time_limit(0);
 
-    if ($tables = $DB->get_tables()) {
+    foreach ($rs as $table) {
         $DB->set_debug(true);
-        foreach ($tables as $table) {
-            $fulltable = $DB->get_prefix().$table;
-            try {
-                $DB->change_database_structure("ALTER TABLE $fulltable ENGINE=INNODB");
-            } catch (moodle_exception $e) {
-                echo $OUTPUT->notification(s($e->getMessage()).'<br />'.s($e->debuginfo));
-            }
+        $fulltable = $table->name;
+        try {
+            $DB->change_database_structure("ALTER TABLE $fulltable ENGINE=INNODB");
+        } catch (moodle_exception $e) {
+            echo $OUTPUT->notification(s($e->getMessage()).'<br />'.s($e->debuginfo));
         }
         $DB->set_debug(false);
     }
+    $rs->close();
     echo $OUTPUT->notification('... done.', 'notifysuccess');
     echo $OUTPUT->continue_button(new moodle_url('/admin/'));
     echo $OUTPUT->footer();
 
 } else {
+    $rs->close();
     $optionsyes = array('confirm'=>'1', 'sesskey'=>sesskey());
     $formcontinue = new single_button(new moodle_url('/admin/tool/innodb/index.php', $optionsyes), get_string('yes'));
     $formcancel = new single_button(new moodle_url('/admin/'), get_string('no'), 'get');
index 8c17b60..d3c8737 100644 (file)
@@ -60,9 +60,8 @@ if (!isset($args[0]) || !in_array($args[0], $alloweddirs)) {
     print_error('invalidarguments');
 }
 
-// only serve some controlled extensions
-$allowedextensions = array('text/html', 'text/css', 'image/gif', 'application/x-javascript');
-if (!in_array(mimeinfo('type', $filepath), $allowedextensions)) {
+// only serve some controlled extensions/mimetypes
+if (!file_extension_in_typegroup($filepath, array('web_file', 'web_image'), true)) {
     print_error('invalidarguments');
 }
 
index 6b5881c..27fc9b9 100644 (file)
@@ -152,7 +152,7 @@ function user_download_csv($fields) {
     $filename = clean_filename(get_string('users').'.csv');
 
     header("Content-Type: application/download\n");
-    header("Content-Disposition: attachment; filename=$filename");
+    header("Content-Disposition: attachment; filename=\"$filename\"");
     header("Expires: 0");
     header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
     header("Pragma: public");
index c51c74a..04cc360 100644 (file)
@@ -727,3 +727,255 @@ class moodle_group_delete_groupmembers_form extends moodleform {
         return $params;
     }
 }
+
+/**
+ * Form class for create_categories() web service function test.
+ *
+ * @package   core_webservice
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2012 Fabio Souto
+ */
+class core_course_create_categories_form extends moodleform {
+    /**
+     * The form definition.
+     */
+    public function definition() {
+        global $CFG;
+
+        $mform = $this->_form;
+
+        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
+
+        // Note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters.
+        $data = $this->_customdata;
+        if ($data['authmethod'] == 'simple') {
+            $mform->addElement('text', 'wsusername', 'wsusername');
+            $mform->addElement('text', 'wspassword', 'wspassword');
+        } else if ($data['authmethod'] == 'token') {
+            $mform->addElement('text', 'token', 'token');
+        }
+
+        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
+        $mform->setType('authmethod', PARAM_SAFEDIR);
+        $mform->addElement('text', 'name[0]', 'name[0]');
+        $mform->addElement('text', 'parent[0]', 'parent[0]');
+        $mform->addElement('text', 'idnumber[0]', 'idnumber[0]');
+        $mform->addElement('text', 'description[0]', 'description[0]');
+        $mform->addElement('text', 'name[1]', 'name[1]');
+        $mform->addElement('text', 'parent[1]', 'parent[1]');
+        $mform->addElement('text', 'idnumber[1]', 'idnumber[1]');
+        $mform->addElement('text', 'description[1]', 'description[1]');
+
+        $mform->addElement('hidden', 'function');
+        $mform->setType('function', PARAM_SAFEDIR);
+
+        $mform->addElement('hidden', 'protocol');
+        $mform->setType('protocol', PARAM_SAFEDIR);
+
+        $this->add_action_buttons(true, get_string('execute', 'webservice'));
+    }
+
+    /**
+     * Get the parameters that the user submitted using the form.
+     * @return array|null
+     */
+    public function get_params() {
+        if (!$data = $this->get_data()) {
+            return null;
+        }
+        // Remove unused from form data.
+        unset($data->submitbutton);
+        unset($data->protocol);
+        unset($data->function);
+        unset($data->wsusername);
+        unset($data->wspassword);
+        unset($data->token);
+        unset($data->authmethod);
+
+        $params = array();
+        $params['categories'] = array();
+        for ($i=0; $i<10; $i++) {
+            if (empty($data->name[$i]) or empty($data->parent[$i])) {
+                continue;
+            }
+            $params['categories'][] = array('name'=>$data->name[$i], 'parent'=>$data->parent[$i],
+                                            'idnumber'=>$data->idnumber[$i], 'description'=>$data->description[$i]);
+        }
+        return $params;
+    }
+}
+
+/**
+ * Form class for delete_categories() web service function test.
+ *
+ * @package   core_webservice
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2012 Fabio Souto
+ */
+class core_course_delete_categories_form extends moodleform {
+    /**
+     * The form definition.
+     */
+    public function definition() {
+        global $CFG;
+
+        $mform = $this->_form;
+
+        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
+
+        // Note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters.
+        $data = $this->_customdata;
+        if ($data['authmethod'] == 'simple') {
+            $mform->addElement('text', 'wsusername', 'wsusername');
+            $mform->addElement('text', 'wspassword', 'wspassword');
+        } else if ($data['authmethod'] == 'token') {
+            $mform->addElement('text', 'token', 'token');
+        }
+
+        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
+        $mform->setType('authmethod', PARAM_SAFEDIR);
+        $mform->addElement('text', 'id[0]', 'id[0]');
+        $mform->addElement('text', 'newparent[0]', 'newparent[0]');
+        $mform->addElement('text', 'recursive[0]', 'recursive[0]');
+        $mform->addElement('text', 'id[1]', 'id[1]');
+        $mform->addElement('text', 'newparent[1]', 'newparent[1]');
+        $mform->addElement('text', 'recursive[1]', 'recursive[1]');
+
+        $mform->addElement('hidden', 'function');
+        $mform->setType('function', PARAM_SAFEDIR);
+
+        $mform->addElement('hidden', 'protocol');
+        $mform->setType('protocol', PARAM_SAFEDIR);
+
+        $this->add_action_buttons(true, get_string('execute', 'webservice'));
+    }
+
+    /**
+     * Get the parameters that the user submitted using the form.
+     * @return array|null
+     */
+    public function get_params() {
+        if (!$data = $this->get_data()) {
+            return null;
+        }
+        // Remove unused from form data.
+        unset($data->submitbutton);
+        unset($data->protocol);
+        unset($data->function);
+        unset($data->wsusername);
+        unset($data->wspassword);
+        unset($data->token);
+        unset($data->authmethod);
+
+        $params = array();
+        $params['categories'] = array();
+        for ($i=0; $i<10; $i++) {
+            if (empty($data->id[$i])) {
+                continue;
+            }
+            $attrs = array();
+            $attrs['id'] = $data->id[$i];
+            if (!empty($data->newparent[$i])) {
+                $attrs['newparent'] = $data->newparent[$i];
+            }
+            if (!empty($data->recursive[$i])) {
+                $attrs['recursive'] = $data->recursive[$i];
+            }
+            $params['categories'][] = $attrs;
+        }
+        return $params;
+    }
+}
+
+/**
+ * Form class for create_categories() web service function test.
+ *
+ * @package   core_webservice
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2012 Fabio Souto
+ */
+class core_course_update_categories_form extends moodleform {
+    /**
+     * The form definition.
+     */
+    public function definition() {
+        global $CFG;
+
+        $mform = $this->_form;
+
+        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
+
+        // Note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters.
+        $data = $this->_customdata;
+        if ($data['authmethod'] == 'simple') {
+            $mform->addElement('text', 'wsusername', 'wsusername');
+            $mform->addElement('text', 'wspassword', 'wspassword');
+        } else if ($data['authmethod'] == 'token') {
+            $mform->addElement('text', 'token', 'token');
+        }
+
+        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
+        $mform->setType('authmethod', PARAM_SAFEDIR);
+        $mform->addElement('text', 'id[0]', 'id[0]');
+        $mform->addElement('text', 'name[0]', 'name[0]');
+        $mform->addElement('text', 'parent[0]', 'parent[0]');
+        $mform->addElement('text', 'idnumber[0]', 'idnumber[0]');
+        $mform->addElement('text', 'description[0]', 'description[0]');
+        $mform->addElement('text', 'id[1]', 'id[1]');
+        $mform->addElement('text', 'name[1]', 'name[1]');
+        $mform->addElement('text', 'parent[1]', 'parent[1]');
+        $mform->addElement('text', 'idnumber[1]', 'idnumber[1]');
+        $mform->addElement('text', 'description[1]', 'description[1]');
+
+        $mform->addElement('hidden', 'function');
+        $mform->setType('function', PARAM_SAFEDIR);
+
+        $mform->addElement('hidden', 'protocol');
+        $mform->setType('protocol', PARAM_SAFEDIR);
+
+        $this->add_action_buttons(true, get_string('execute', 'webservice'));
+    }
+
+    /**
+     * Get the parameters that the user submitted using the form.
+     * @return array|null
+     */
+    public function get_params() {
+        if (!$data = $this->get_data()) {
+            return null;
+        }
+        // Remove unused from form data.
+        unset($data->submitbutton);
+        unset($data->protocol);
+        unset($data->function);
+        unset($data->wsusername);
+        unset($data->wspassword);
+        unset($data->token);
+        unset($data->authmethod);
+
+        $params = array();
+        $params['categories'] = array();
+        for ($i=0; $i<10; $i++) {
+
+            if (empty($data->id[$i])) {
+                continue;
+            }
+            $attrs = array();
+            $attrs['id'] = $data->id[$i];
+            if (!empty($data->name[$i])) {
+                $attrs['name'] = $data->name[$i];
+            }
+            if (!empty($data->parent[$i])) {
+                $attrs['parent'] = $data->parent[$i];
+            }
+            if (!empty($data->idnumber[$i])) {
+                $attrs['idnumber'] = $data->idnumber[$i];
+            }
+            if (!empty($data->description[$i])) {
+                $attrs['description'] = $data->description[$i];
+            }
+            $params['categories'][] = $attrs;
+        }
+        return $params;
+    }
+}
\ No newline at end of file
index 7906a08..36a4030 100644 (file)
@@ -27,7 +27,7 @@ class backup_files_edit_form extends moodleform {
     function definition() {
         $mform =& $this->_form;
         $contextid = $this->_customdata['contextid'];
-        $options = array('subdirs'=>0, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
+        $options = array('subdirs'=>0, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL | FILE_REFERENCE);
         $mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options);
         $mform->addElement('hidden', 'contextid', $this->_customdata['contextid']);
         $mform->addElement('hidden', 'currentcontext', $this->_customdata['currentcontext']);
index 546aa38..a97720e 100644 (file)
@@ -35,6 +35,9 @@ class moodle1_converter_testcase extends advanced_testcase {
     /** @var string the name of the directory containing the unpacked Moodle 1.9 backup */
     protected $tempdir;
 
+    /** @var string saved hash of an icon file used during testing */
+    protected $iconhash;
+
     protected function setUp() {
         global $CFG;
 
@@ -61,6 +64,7 @@ class moodle1_converter_testcase extends advanced_testcase {
             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
             "$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/icon.gif"
         );
+        $this->iconhash = sha1_file($CFG->tempdir.'/backup/'.$this->tempdir.'/moddata/unittest/4/icon.gif');
         copy(
             "$CFG->dirroot/backup/converter/moodle1/tests/fixtures/icon.gif",
             "$CFG->tempdir/backup/$this->tempdir/moddata/unittest/4/7/icon.gif"
@@ -272,7 +276,8 @@ class moodle1_converter_testcase extends advanced_testcase {
         // migrate a single file
         $fileman->itemid = 4;
         $fileman->migrate_file('moddata/unittest/4/icon.gif');
-        $this->assertTrue(is_file($converter->get_workdir_path().'/files/4e/4ea114b0558f53e3af8dd9afc0e0810a95c2a724'));
+        $subdir = substr($this->iconhash, 0, 2);
+        $this->assertTrue(is_file($converter->get_workdir_path().'/files/'.$subdir.'/'.$this->iconhash));
         // get the file id
         $fileids = $fileman->get_fileids();
         $this->assertEquals(gettype($fileids), 'array');
@@ -285,7 +290,7 @@ class moodle1_converter_testcase extends advanced_testcase {
         $filerecordids = $converter->get_stash_itemids('files');
         foreach ($filerecordids as $filerecordid) {
             $filerecord = $converter->get_stash('files', $filerecordid);
-            $this->assertEquals('4ea114b0558f53e3af8dd9afc0e0810a95c2a724', $filerecord['contenthash']);
+            $this->assertEquals($this->iconhash, $filerecord['contenthash']);
             $this->assertEquals($contextid, $filerecord['contextid']);
             $this->assertEquals('mod_unittest', $filerecord['component']);
             if ($filerecord['filearea'] === 'testarea') {
index fa12036..d1be602 100644 (file)
@@ -364,19 +364,26 @@ class backup_section_structure_step extends backup_structure_step {
         // Define each element separated
 
         $section = new backup_nested_element('section', array('id'), array(
-            'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
+                'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible',
+                'availablefrom', 'availableuntil', 'showavailability', 'groupingid'));
 
         // attach format plugin structure to $section element, only one allowed
         $this->add_plugin_structure('format', $section, false);
 
-        // Define sources
+        // Add nested elements for _availability table
+        $avail = new backup_nested_element('availability', array('id'), array(
+                'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
+        $section->add_child($avail);
 
+        // Define sources
         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
+        $avail->set_source_table('course_sections_availability', array('coursesectionid' => backup::VAR_SECTIONID));
 
         // Aliases
         $section->set_source_alias('section', 'number');
 
         // Set annotations
+        $section->annotate_ids('grouping', 'groupingid');
         $section->annotate_files('course', 'section', 'id');
 
         return $section;
@@ -965,7 +972,7 @@ class backup_groups_structure_step extends backup_structure_step {
         $groups = new backup_nested_element('groups');
 
         $group = new backup_nested_element('group', array('id'), array(
-            'name', 'description', 'descriptionformat', 'enrolmentkey',
+            'name', 'idnumber', 'description', 'descriptionformat', 'enrolmentkey',
             'picture', 'hidepicture', 'timecreated', 'timemodified'));
 
         $members = new backup_nested_element('group_members');
@@ -976,7 +983,7 @@ class backup_groups_structure_step extends backup_structure_step {
         $groupings = new backup_nested_element('groupings');
 
         $grouping = new backup_nested_element('grouping', 'id', array(
-            'name', 'description', 'descriptionformat', 'configdata',
+            'name', 'idnumber', 'description', 'descriptionformat', 'configdata',
             'timecreated', 'timemodified'));
 
         $groupinggroups = new backup_nested_element('grouping_groups');
@@ -1397,7 +1404,7 @@ class backup_final_files_structure_step extends backup_structure_step {
             'contenthash', 'contextid', 'component', 'filearea', 'itemid',
             'filepath', 'filename', 'userid', 'filesize',
             'mimetype', 'status', 'timecreated', 'timemodified',
-            'source', 'author', 'license', 'sortorder'));
+            'source', 'author', 'license', 'sortorder', 'reference', 'repositoryid'));
 
         // Build the tree
 
@@ -1405,9 +1412,12 @@ class backup_final_files_structure_step extends backup_structure_step {
 
         // Define sources
 
-        $file->set_source_sql("SELECT f.*
+        $file->set_source_sql("SELECT f.*, r.repositoryid, r.reference
                                  FROM {files} f
-                                 JOIN {backup_ids_temp} bi ON f.id = bi.itemid
+                                 JOIN {files_reference} r
+                                      ON r.id = f.referencefileid
+                                 JOIN {backup_ids_temp} bi
+                                      ON f.id = bi.itemid
                                 WHERE bi.backupid = ?
                                   AND bi.itemname = 'filefinal'", array(backup::VAR_BACKUPID));
 
@@ -1435,6 +1445,8 @@ class backup_main_structure_step extends backup_structure_step {
         $info['backup_date']    = time();
         $info['backup_uniqueid']= $this->get_backupid();
         $info['mnet_remoteusers']=backup_controller_dbops::backup_includes_mnet_remote_users($this->get_backupid());
+        $info['include_file_references_to_external_content'] =
+                backup_controller_dbops::backup_includes_file_references($this->get_backupid());
         $info['original_wwwroot']=$CFG->wwwroot;
         $info['original_site_identifier_hash'] = md5(get_site_identifier());
         $info['original_course_id'] = $this->get_courseid();
@@ -1454,7 +1466,7 @@ class backup_main_structure_step extends backup_structure_step {
 
         $information = new backup_nested_element('information', null, array(
             'name', 'moodle_version', 'moodle_release', 'backup_version',
-            'backup_release', 'backup_date', 'mnet_remoteusers', 'original_wwwroot',
+            'backup_release', 'backup_date', 'mnet_remoteusers', 'include_file_references_to_external_content', 'original_wwwroot',
             'original_site_identifier_hash', 'original_course_id',
             'original_course_fullname', 'original_course_shortname', 'original_course_startdate',
             'original_course_contextid', 'original_system_contextid'));
@@ -1577,8 +1589,12 @@ class backup_store_backup_file extends backup_execution_step {
         // Calculate the zip fullpath (in OS temp area it's always backup.mbz)
         $zipfile = $basepath . '/backup.mbz';
 
+        $has_file_references = backup_controller_dbops::backup_includes_file_references($this->get_backupid());
         // Perform storage and return it (TODO: shouldn't be array but proper result object)
-        return array('backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile));
+        return array(
+            'backup_destination' => backup_helper::store_backup_file($this->get_backupid(), $zipfile),
+            'include_file_references_to_external_content' => $has_file_references
+        );
     }
 }
 
index 3f4a446..e8831d7 100644 (file)
@@ -79,15 +79,16 @@ class restore_final_task extends restore_task {
             $this->add_step(new restore_course_logs_structure_step('course_logs', 'course/logs.xml'));
         }
 
-        // Rebuild course cache to see results, whoah!
-        $this->add_step(new restore_rebuild_course_cache('rebuild_course_cache'));
-
         // Review all the executed tasks having one after_restore method
         // executing it to perform some final adjustments of information
         // not available when the task was executed.
-        // This step is always the last one before the one cleaning the temp stuff!
+        // This step is always the last one performing modifications on restored information
+        // Don't add any new step after it. Only cache rebuild and clean are allowed.
         $this->add_step(new restore_execute_after_restore('executing_after_restore'));
 
+        // Rebuild course cache to see results, whoah!
+        $this->add_step(new restore_rebuild_course_cache('rebuild_course_cache'));
+
         // Clean the temp dir (conditionally) and drop temp table
         $this->add_step(new restore_drop_and_clean_temp_stuff('drop_and_clean_temp_stuff'));
 
index 1472012..4d610e5 100644 (file)
@@ -586,11 +586,23 @@ class restore_load_included_files extends restore_structure_step {
         return array($file);
     }
 
-    // Processing functions go here
+    /**
+     * Processing functions go here
+     *
+     * @param array $data one file record including repositoryid and reference
+     */
     public function process_file($data) {
 
         $data = (object)$data; // handy
 
+        $isreference = !empty($data->repositoryid);
+        $issamesite = $this->task->is_samesite();
+
+        // If it's not samesite, we skip file refernces
+        if (!$issamesite && $isreference) {
+            return;
+        }
+
         // load it if needed:
         //   - it it is one of the annotated inforef files (course/section/activity/block)
         //   - it is one "user", "group", "grouping", "grade", "question" or "qtype_xxxx" component file (that aren't sent to inforef ever)
@@ -601,6 +613,7 @@ class restore_load_included_files extends restore_structure_step {
                         $data->component == 'grouping' || $data->component == 'grade' ||
                         $data->component == 'question' || substr($data->component, 0, 5) == 'qtype');
         if ($isfileref || $iscomponent) {
+            // Process files
             restore_dbops::set_backup_files_record($this->get_restoreid(), $data);
         }
     }
@@ -716,6 +729,17 @@ class restore_groups_structure_step extends restore_structure_step {
         $data = (object)$data; // handy
         $data->courseid = $this->get_courseid();
 
+        // Only allow the idnumber to be set if the user has permission and the idnumber is not already in use by
+        // another a group in the same course
+        $context = context_course::instance($data->courseid);
+        if (isset($data->idnumber) and has_capability('moodle/course:changeidnumber', $context, $this->task->get_userid())) {
+            if (groups_get_group_by_idnumber($data->courseid, $data->idnumber)) {
+                unset($data->idnumber);
+            }
+        } else {
+            unset($data->idnumber);
+        }
+
         $oldid = $data->id;    // need this saved for later
 
         $restorefiles = false; // Only if we end creating the group
@@ -765,6 +789,17 @@ class restore_groups_structure_step extends restore_structure_step {
         $data = (object)$data; // handy
         $data->courseid = $this->get_courseid();
 
+        // Only allow the idnumber to be set if the user has permission and the idnumber is not already in use by
+        // another a grouping in the same course
+        $context = context_course::instance($data->courseid);
+        if (isset($data->idnumber) and has_capability('moodle/course:changeidnumber', $context, $this->task->get_userid())) {
+            if (groups_get_grouping_by_idnumber($data->courseid, $data->idnumber)) {
+                unset($data->idnumber);
+            }
+        } else {
+            unset($data->idnumber);
+        }
+
         $oldid = $data->id;    // need this saved for later
         $restorefiles = false; // Only if we end creating the grouping
 
@@ -992,16 +1027,24 @@ class restore_process_categories_and_questions extends restore_execution_step {
 class restore_section_structure_step extends restore_structure_step {
 
     protected function define_structure() {
+        global $CFG;
+
+        $paths = array();
+
         $section = new restore_path_element('section', '/section');
+        $paths[] = $section;
+        if ($CFG->enableavailability) {
+            $paths[] = new restore_path_element('availability', '/section/availability');
+        }
 
         // Apply for 'format' plugins optional paths at section level
         $this->add_plugin_structure('format', $section);
 
-        return array($section);
+        return $paths;
     }
 
     public function process_section($data) {
-        global $DB;
+        global $CFG, $DB;
         $data = (object)$data;
         $oldid = $data->id; // We'll need this later
 
@@ -1018,6 +1061,18 @@ class restore_section_structure_step extends restore_structure_step {
             $section->summaryformat = $data->summaryformat;
             $section->sequence = '';
             $section->visible = $data->visible;
+            if (empty($CFG->enableavailability)) { // Process availability information only if enabled.
+                $section->availablefrom = 0;
+                $section->availableuntil = 0;
+                $section->showavailability = 0;
+            } else {
+                $section->availablefrom = isset($data->availablefrom) ? $this->apply_date_offset($data->availablefrom) : 0;
+                $section->availableuntil = isset($data->availableuntil) ? $this->apply_date_offset($data->availableuntil) : 0;
+                $section->showavailability = isset($data->showavailability) ? $data->showavailability : 0;
+            }
+            if (!empty($CFG->enablegroupmembersonly)) { // Only if enablegroupmembersonly is enabled
+                $section->groupingid = isset($data->groupingid) ? $this->get_mappingid('grouping', $data->groupingid) : 0;
+            }
             $newitemid = $DB->insert_record('course_sections', $section);
             $restorefiles = true;
 
@@ -1032,6 +1087,16 @@ class restore_section_structure_step extends restore_structure_step {
                 $section->summaryformat = $data->summaryformat;
                 $restorefiles = true;
             }
+            if (empty($secrec->groupingid)) {
+                if (!empty($CFG->enablegroupmembersonly)) { // Only if enablegroupmembersonly is enabled
+                    $section->groupingid = isset($data->groupingid) ? $this->get_mappingid('grouping', $data->groupingid) : 0;
+                }
+            }
+
+            // Don't update available from, available until, or show availability
+            // (I didn't see a useful way to define whether existing or new one should
+            // take precedence).
+
             $DB->update_record('course_sections', $section);
             $newitemid = $secrec->id;
         }
@@ -1055,10 +1120,47 @@ class restore_section_structure_step extends restore_structure_step {
         //}
     }
 
+    public function process_availability($data) {
+        global $DB;
+        $data = (object)$data;
+        $data->coursesectionid = $this->task->get_sectionid();
+        // NOTE: Other values in $data need updating, but these (cm,
+        // grade items) have not yet been restored.
+        $DB->insert_record('course_sections_availability', $data);
+    }
+
     protected function after_execute() {
         // Add section related files, with 'course_section' itemid to match
         $this->add_related_files('course', 'section', 'course_section');
     }
+
+    public function after_restore() {
+        global $DB;
+
+        $sectionid = $this->get_task()->get_sectionid();
+
+        // Get data object for current section availability (if any)
+        // TODO: This can be processing already existing records, we need to be able to know which ones
+        //       are the just restored ones, perhaps creating 'course_sections_availability' mappings for them.
+        // TODO: Also, this must avoid duplicates, so if one course module or one grade item already is being
+        //       used for some availability rule... we need to handle that carefully.
+        $data = $DB->get_record('course_sections_availability',
+                array('coursesectionid' => $sectionid), 'id, sourcecmid, gradeitemid', IGNORE_MISSING);
+
+        // Update mappings
+        if ($data) {
+            $data->sourcecmid = $this->get_mappingid('course_module', $data->sourcecmid);
+            if (!$data->sourcecmid) {
+                $data->sourcecmid = null;
+            }
+            $data->gradeitemid = $this->get_mappingid('grade_item', $data->gradeitemid);
+            if (!$data->gradeitemid) {
+                $data->gradeitemid = null;
+            }
+
+            $DB->update_record('course_sections_availability', $data);
+        }
+    }
 }
 
 
index c77aeb6..ee98987 100644 (file)
@@ -409,6 +409,27 @@ abstract class backup_controller_dbops extends backup_dbops {
         return (int)(bool)$count;
     }
 
+    /**
+     * Given the backupid, detect if the backup contains references to external contents
+     *
+     * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
+     * @return int
+     */
+    public static function backup_includes_file_references($backupid) {
+        global $CFG, $DB;
+
+        $sql = "SELECT count(r.repositoryid)
+                  FROM {files} f
+                  JOIN {files_reference} r
+                       ON r.id = f.referencefileid
+                  JOIN {backup_ids_temp} bi
+                       ON f.id = bi.itemid
+                 WHERE bi.backupid = ?
+                       AND bi.itemname = 'filefinal'";
+        $count = $DB->count_records_sql($sql, array($backupid));
+        return (int)(bool)$count;
+    }
+
     /**
      * Given the courseid, return some course related information we want to transport
      *
index 25f2e53..e50c6e6 100644 (file)
@@ -685,6 +685,9 @@ abstract class restore_dbops {
         $rs = $DB->get_recordset_sql($sql, $params);
         foreach ($rs as $rec) {
             $file = (object)unserialize(base64_decode($rec->info));
+
+            $isreference = !empty($file->repositoryid);
+
             // ignore root dirs (they are created automatically)
             if ($file->filepath == '/' && $file->filename == '.') {
                 continue;
@@ -697,10 +700,12 @@ abstract class restore_dbops {
                 $fs->create_directory($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->userid);
                 continue;
             }
+
             // arrived here, file found
             // Find file in backup pool
             $backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
-            if (!file_exists($backuppath)) {
+
+            if (!file_exists($backuppath) && !$isreference) {
                 throw new restore_dbops_exception('file_not_found_in_pool', $file);
             }
             if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
@@ -717,7 +722,11 @@ abstract class restore_dbops {
                     'author'      => $file->author,
                     'license'     => $file->license,
                     'sortorder'   => $file->sortorder);
-                $fs->create_file_from_pathname($file_record, $backuppath);
+                if ($isreference) {
+                    $fs->create_file_from_reference($file_record, $file->repositoryid, $file->reference);
+                } else {
+                    $fs->create_file_from_pathname($file_record, $backuppath);
+                }
             }
         }
         $rs->close();
@@ -1204,6 +1213,7 @@ abstract class restore_dbops {
     public static function set_backup_files_record($restoreid, $filerec) {
         global $DB;
 
+        // Store external files info in `info` field
         $filerec->info     = base64_encode(serialize($filerec)); // Serialize the whole rec in info
         $filerec->backupid = $restoreid;
         $DB->insert_record('backup_files_temp', $filerec);
index fb6477a..9941af7 100644 (file)
@@ -73,6 +73,10 @@ class backup_file_manager {
 
         $fs = get_file_storage();
         $file = $fs->get_file_instance($filerecorid);
+        // If the file is external file, skip copying.
+        if ($file->is_external_file()) {
+            return;
+        }
 
         // Calculate source and target paths (use same subdirs strategy for both)
         $targetfilepath = self::get_backup_storage_base_dir($backupid) . '/' .
index 73b5d3b..ff5dc89 100644 (file)
@@ -145,6 +145,12 @@ abstract class backup_general_helper extends backup_helper {
         $info->original_course_startdate= $infoarr['original_course_startdate'];
         $info->original_course_contextid= $infoarr['original_course_contextid'];
         $info->original_system_contextid= $infoarr['original_system_contextid'];
+        // Moodle backup file don't have this option before 2.3
+        if (!empty($infoarr['include_file_references_to_external_content'])) {
+            $info->include_file_references_to_external_content = 1;
+        } else {
+            $info->include_file_references_to_external_content = 0;
+        }
         $info->type   =  $infoarr['details']['detail'][0]['type'];
         $info->format =  $infoarr['details']['detail'][0]['format'];
         $info->mode   =  $infoarr['details']['detail'][0]['mode'];
index 8f481de..e5d21b8 100644 (file)
@@ -55,12 +55,6 @@ abstract class convert_helper {
 
         $converters = array();
 
-        // Only apply for backup converters if the (experimental) setting enables it.
-        // This will be out once we get proper support of backup converters. MDL-29956
-        if (!$restore && empty($CFG->enablebackupconverters)) {
-            return $converters;
-        }
-
         $plugins    = get_list_of_plugins('backup/converter');
         foreach ($plugins as $name) {
             $filename = $restore ? 'lib.php' : 'backuplib.php';
index ddb2fe5..4b0ff58 100644 (file)
@@ -395,6 +395,8 @@ abstract class restore_structure_step extends restore_step {
                 $pobject->launch_after_restore_methods();
             }
         }
+        // Finally execute own (restore_structure_step) after_restore method
+        $this->after_restore();
     }
 
     /**
@@ -408,6 +410,16 @@ abstract class restore_structure_step extends restore_step {
         // do nothing by default
     }
 
+    /**
+     * This method will be executed after the rest of the restore has been processed.
+     *
+     * Use if you need to update IDs based on things which are restored after this
+     * step has completed.
+     */
+    protected function after_restore() {
+        // do nothing by default
+    }
+
     /**
      * Prepare the pathelements for processing, looking for duplicates, applying
      * processing objects and other adjustments
index 2cc2045..1ce9053 100644 (file)
@@ -470,6 +470,9 @@ class backup_ui_stage_complete extends backup_ui_stage_final {
 
         $output = '';
         $output .= $renderer->box_start();
+        if (!empty($this->results['include_file_references_to_external_content'])) {
+            $output .= $renderer->notification(get_string('filereferencesincluded', 'backup'), 'notifyproblem');
+        }
         $output .= $renderer->notification(get_string('executionsuccess', 'backup'), 'notifysuccess');
         $output .= $renderer->continue_button($restorerul);
         $output .= $renderer->box_end();
index 7afcdfd..9e0ab9d 100644 (file)
@@ -92,6 +92,16 @@ class core_backup_renderer extends plugin_renderer_base {
         $html .= $this->backup_detail_pair(get_string('originalwwwroot', 'backup'),
                 html_writer::tag('span', $details->original_wwwroot, array('class'=>'originalwwwroot')).
                 html_writer::tag('span', '['.$details->original_site_identifier_hash.']', array('class'=>'sitehash sub-detail')));
+        if (!empty($details->include_file_references_to_external_content)) {
+            $message = '';
+            if (backup_general_helper::backup_is_samesite($details)) {
+                $message = $yestick . ' ' . get_string('filereferencessamesite', 'backup');
+            } else {
+                $message = $notick . ' ' . get_string('filereferencesnotsamesite', 'backup');
+            }
+            $html .= $this->backup_detail_pair(get_string('includefilereferences', 'backup'), $message);
+        }
+
         $html .= html_writer::end_tag('div');
 
         $html .= html_writer::start_tag('div', array('class'=>'backup-section settings-section'));
index c290b24..2aa2578 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+defined('MOODLE_INTERNAL') || die();
+require_once($CFG->libdir . '/filelib.php');
+
 class block_activity_modules extends block_list {
     function init() {
         $this->title = get_string('pluginname', 'block_activity_modules');
@@ -50,7 +53,7 @@ class block_activity_modules extends block_list {
 
         foreach ($modfullnames as $modname => $modfullname) {
             if ($modname === 'resources') {
-                $icon = '<img src="'.$OUTPUT->pix_url('f/html') . '" class="icon" alt="" />&nbsp;';
+                $icon = $OUTPUT->pix_icon(file_extension_icon('.htm'), '', 'moodle', array('class' => 'icon')). '&nbsp;';
                 $this->content->items[] = '<a href="'.$CFG->wwwroot.'/course/resources.php?id='.$course->id.'">'.$icon.$modfullname.'</a>';
             } else {
                 $icon = '<img src="'.$OUTPUT->pix_url('icon', $modname) . '" class="icon" alt="" />&nbsp;';
index 5a7667b..67b1e70 100644 (file)
@@ -45,7 +45,7 @@ $PAGE->set_pagelayout('mydashboard');
 $PAGE->set_pagetype('user-private-files');
 
 $data = new stdClass();
-$options = array('subdirs'=>1, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
+$options = array('subdirs'=>1, 'maxbytes'=>$CFG->userquota, 'maxfiles'=>-1, 'accepted_types'=>'*');
 file_prepare_standard_filemanager($data, 'files', $options, $context, 'user', 'private', 0);
 
 $mform = new block_private_files_form(null, array('data'=>$data, 'options'=>$options));
index 5c27971..eb109fa 100644 (file)
@@ -65,14 +65,13 @@ class block_private_files_renderer extends plugin_renderer_base {
         }
         $result = '<ul>';
         foreach ($dir['subdirs'] as $subdir) {
-            $image = $this->output->pix_icon("f/folder", $subdir['dirname'], 'moodle', array('class'=>'icon'));
+            $image = $this->output->pix_icon(file_folder_icon(), $subdir['dirname'], 'moodle', array('class'=>'icon'));
             $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.$image.' '.s($subdir['dirname']).'</div> '.$this->htmllize_tree($tree, $subdir).'</li>';
         }
         foreach ($dir['files'] as $file) {
             $url = file_encode_url("$CFG->wwwroot/pluginfile.php", '/'.$tree->context->id.'/user/private'.$file->get_filepath().$file->get_filename(), true);
             $filename = $file->get_filename();
-            $icon = mimeinfo("icon", $filename);
-            $image = $this->output->pix_icon("f/$icon", $filename, 'moodle', array('class'=>'icon'));
+            $image = $this->output->pix_icon(file_file_icon($file), $filename, 'moodle', array('class'=>'icon'));
             $result .= '<li yuiConfig=\''.json_encode($yuiconfig).'\'><div>'.html_writer::link($url, $image.'&nbsp;'.$filename).'</div></li>';
         }
         $result .= '</ul>';
index 9eb1e8f..6ac8944 100644 (file)
@@ -513,10 +513,7 @@ class blog_entry {
             $ffurl    = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.SYSCONTEXTID.'/blog/attachment/'.$this->id.'/'.$filename);
             $mimetype = $file->get_mimetype();
 
-            $icon     = mimeinfo_from_type("icon", $mimetype);
-            $type     = mimeinfo_from_type("type", $mimetype);
-
-            $image = $OUTPUT->pix_icon("f/$icon", $filename, 'moodle', array('class'=>'icon'));
+            $image = $OUTPUT->pix_icon(file_file_icon($file), $filename, 'moodle', array('class'=>'icon'));
 
             if ($return == "html") {
                 $output .= html_writer::link($ffurl, $image);
@@ -526,7 +523,7 @@ class blog_entry {
                 $output .= "$strattachment $filename:\n$ffurl\n";
 
             } else {
-                if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) {    // Image attachments don't get printed as links
+                if (file_mimetype_in_typegroup($file->get_mimetype(), 'web_image')) {    // Image attachments don't get printed as links
                     $imagereturn .= '<br /><img src="'.$ffurl.'" alt="" />';
                 } else {
                     $imagereturn .= html_writer::link($ffurl, $image);
index 7d55605..16c5f28 100644 (file)
@@ -1049,28 +1049,6 @@ function calendar_get_link_href($linkbase, $d, $m, $y) {
     return $linkbase;
 }
 
-/**
- * This function has been deprecated as of Moodle 2.0... DO NOT USE!!!!!
- *
- * @deprecated Moodle 2.0 - MDL-24284 please do not use this function any more.
- * @todo MDL-31134 - will be removed in Moodle 2.3
- * @see calendar_get_link_href()
- *
- * @param string $text
- * @param string|moodle_url $linkbase
- * @param int|null $d The number of the day.
- * @param int|null $m The number of the month.
- * @param int|null $y The number of the year.
- * @return string HTML link
- */
-function calendar_get_link_tag($text, $linkbase, $d, $m, $y) {
-    $url = calendar_get_link_href(new moodle_url($linkbase), $d, $m, $y);
-    if (empty($url)) {
-        return $text;
-    }
-    return html_writer::link($url, $text);
-}
-
 /**
  * Build and return a previous month HTML link, with an arrow.
  *
@@ -1491,15 +1469,16 @@ function calendar_get_default_courses() {
     }
 
     $courses = array();
-    if (!empty($CFG->calendar_adminseesall) && has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_SYSTEM))) {
+    if (!empty($CFG->calendar_adminseesall) && has_capability('moodle/calendar:manageentries', context_system::instance())) {
         list ($select, $join) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
-        $sql = "SELECT DISTINCT c.* $select
+        $sql = "SELECT c.* $select
                   FROM {course} c
-                  JOIN {event} e ON e.courseid = c.id
-                  $join";
+                  $join
+                  WHERE EXISTS (SELECT 1 FROM {event} e WHERE e.courseid = c.id)
+                  ";
         $courses = $DB->get_records_sql($sql, null, 0, 20);
         foreach ($courses as $course) {
-            context_instance_preload($course);
+            context_helper::preload_from_record($course);
         }
         return $courses;
     }
index 4b3534f..e82d7bd 100644 (file)
@@ -38,7 +38,7 @@ $CFG = new stdClass();
 // will be stored.  This database must already have been created         //
 // and a username/password created to access it.                         //
 
-$CFG->dbtype    = 'pgsql';      // 'pgsql', 'mysqli', 'mssql' or 'oci'
+$CFG->dbtype    = 'pgsql';      // 'pgsql', 'mysqli', 'mssql', 'sqlsrv' or 'oci'
 $CFG->dblibrary = 'native';     // 'native' only at the moment
 $CFG->dbhost    = 'localhost';  // eg 'localhost' or 'db.isp.com' or IP
 $CFG->dbname    = 'moodle';     // database name, eg moodle
@@ -431,6 +431,11 @@ $CFG->admin = 'admin';
 //
 //      $CFG->cssoptimiserpretty = true;
 //
+// Use the following flag to completely disable the Available update notifications
+// feature and hide it from the server administration UI.
+//
+//      $CFG->disableupdatenotifications = true;
+//
 //=========================================================================
 // 8. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
 //=========================================================================
diff --git a/course/dndupload.js b/course/dndupload.js
new file mode 100644 (file)
index 0000000..9b7c4e4
--- /dev/null
@@ -0,0 +1,932 @@
+// 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/>.
+
+/**
+ * Javascript library for enableing a drag and drop upload to courses
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2012 Davo Smith
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+M.course_dndupload = {
+    // YUI object.
+    Y: null,
+    // URL for upload requests
+    url: M.cfg.wwwroot + '/course/dndupload.php',
+    // maximum size of files allowed in this form
+    maxbytes: 0,
+    // ID of the course we are on
+    courseid: null,
+    // Data about the different file/data handlers that are available
+    handlers: null,
+    // Nasty hack to distinguish between dragenter(first entry),
+    // dragenter+dragleave(moving between child elements) and dragleave (leaving element)
+    entercount: 0,
+    // Used to keep track of the section we are dragging across - to make
+    // spotting movement between sections more reliable
+    currentsection: null,
+    // Used to store the pending uploads whilst the user is being asked for further input
+    uploadqueue: null,
+    // True if the there is currently a dialog being shown (asking for a name, or giving a
+    // choice of file handlers)
+    uploaddialog: false,
+    // An array containing the last selected file handler for each file type
+    lastselected: null,
+
+    // The following are used to identify specific parts of the course page
+
+    // The type of HTML element that is a course section
+    sectiontypename: 'li',
+    // The classes that an element must have to be identified as a course section
+    sectionclasses: ['section', 'main'],
+    // The ID of the main content area of the page (for adding the 'status' div)
+    pagecontentid: 'page-content',
+    // The selector identifying the list of modules within a section (note changing this may require
+    // changes to the get_mods_element function)
+    modslistselector: 'ul.section',
+
+    /**
+     * Initalise the drag and drop upload interface
+     * Note: one and only one of options.filemanager and options.formcallback must be defined
+     *
+     * @param Y the YUI object
+     * @param object options {
+     *            courseid: ID of the course we are on
+     *            maxbytes: maximum size of files allowed in this form
+     *            handlers: Data about the different file/data handlers that are available
+     *          }
+     */
+    init: function(Y, options) {
+        this.Y = Y;
+
+        if (!this.browser_supported()) {
+            return; // Browser does not support the required functionality
+        }
+
+        this.maxbytes = options.maxbytes;
+        this.courseid = options.courseid;
+        this.handlers = options.handlers;
+        this.uploadqueue = new Array();
+        this.lastselected = new Array();
+
+        var sectionselector = this.sectiontypename + '.' + this.sectionclasses.join('.');
+        var sections = this.Y.all(sectionselector);
+        if (sections.isEmpty()) {
+            return; // No sections - incompatible course format or front page.
+        }
+        sections.each( function(el) {
+            this.add_preview_element(el);
+            this.init_events(el);
+        }, this);
+
+        this.add_status_div();
+    },
+
+    /**
+     * Add a div element to tell the user that drag and drop upload
+     * is available (or to explain why it is not available)
+     */
+    add_status_div: function() {
+        var div = document.createElement('div');
+        div.id = 'dndupload-status';
+        var coursecontents = document.getElementById(this.pagecontentid);
+        if (coursecontents) {
+            coursecontents.insertBefore(div, coursecontents.firstChild);
+        }
+        div = this.Y.one(div);
+
+        var handlefile = (this.handlers.filehandlers.length > 0);
+        var handletext = false;
+        var handlelink = false;
+        var i;
+        for (i=0; i<this.handlers.types.length; i++) {
+            switch (this.handlers.types[i].identifier) {
+            case 'text':
+            case 'text/html':
+                handletext = true;
+                break;
+            case 'url':
+                handlelink = true;
+                break;
+            }
+        }
+        $msgident = 'dndworking';
+        if (handlefile) {
+            $msgident += 'file';
+        }
+        if (handletext) {
+            $msgident += 'text';
+        }
+        if (handlelink) {
+            $msgident += 'link';
+        }
+        div.setContent(M.util.get_string($msgident, 'moodle'));
+    },
+
+    /**
+     * Check the browser has the required functionality
+     * @return true if browser supports drag/drop upload
+     */
+    browser_supported: function() {
+        if (typeof FileReader == 'undefined') {
+            return false;
+        }
+        if (typeof FormData == 'undefined') {
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * Initialise drag events on node container, all events need
+     * to be processed for drag and drop to work
+     * @param el the element to add events to
+     */
+    init_events: function(el) {
+        this.Y.on('dragenter', this.drag_enter, el, this);
+        this.Y.on('dragleave', this.drag_leave, el, this);
+        this.Y.on('dragover',  this.drag_over,  el, this);
+        this.Y.on('drop',      this.drop,       el, this);
+    },
+
+    /**
+     * Work out which course section a given element is in
+     * @param el the child DOM element within the section
+     * @return the DOM element representing the section
+     */
+    get_section: function(el) {
+        var sectionclasses = this.sectionclasses;
+        return el.ancestor( function(test) {
+            var i;
+            for (i=0; i<sectionclasses.length; i++) {
+                if (!test.hasClass(sectionclasses[i])) {
+                    return false;
+                }
+                return true;
+            }
+        }, true);
+    },
+
+    /**
+     * Work out the number of the section we have been dropped on to, from the section element
+     * @param DOMElement section the selected section
+     * @return int the section number
+     */
+    get_section_number: function(section) {
+        var sectionid = section.get('id').split('-');
+        if (sectionid.length < 2 || sectionid[0] != 'section') {
+            return false;
+        }
+        return parseInt(sectionid[1]);
+    },
+
+    /**
+     * Check if the event includes data of the given type
+     * @param e the event details
+     * @param type the data type to check for
+     * @return true if the data type is found in the event data
+     */
+    types_includes: function(e, type) {
+        var i;
+        var types = e._event.dataTransfer.types;
+        for (i=0; i<types.length; i++) {
+            if (types[i] == type) {
+                return true;
+            }
+        }
+        return false;
+    },
+
+    /**
+     * Look through the event data, checking it against the registered data types
+     * (in order of priority) and return details of the first matching data type
+     * @param e the event details
+     * @return mixed false if not found or an object {
+     *           realtype: the type as given by the browser
+     *           addmessage: the message to show to the user during dragging
+     *           namemessage: the message for requesting a name for the resource from the user
+     *           type: the identifier of the type (may match several 'realtype's)
+     *           }
+     */
+    drag_type: function(e) {
+        // Check there is some data attached.
+        if (e._event.dataTransfer === null) {
+            return false;
+        }
+        if (e._event.dataTransfer.types === null) {
+            return false;
+        }
+        if (e._event.dataTransfer.types.length == 0) {
+            return false;
+        }
+
+        // Check for files first.
+        if (this.types_includes(e, 'Files')) {
+            if (e.type != 'drop' || e._event.dataTransfer.files.length != 0) {
+                if (this.handlers.filehandlers.length == 0) {
+                    return false; // No available file handlers - ignore this drag.
+                }
+                return {
+                    realtype: 'Files',
+                    addmessage: M.util.get_string('addfilehere', 'moodle'),
+                    namemessage: null, // Should not be asked for anyway
+                    type: 'Files'
+                };
+            }
+        }
+
+        // Check each of the registered types.
+        var types = this.handlers.types;
+        for (var i=0; i<types.length; i++) {
+            // Check each of the different identifiers for this type
+            var dttypes = types[i].datatransfertypes;
+            for (var j=0; j<dttypes.length; j++) {
+                if (this.types_includes(e, dttypes[j])) {
+                    return {
+                        realtype: dttypes[j],
+                        addmessage: types[i].addmessage,
+                        namemessage: types[i].namemessage,
+                        type: types[i].identifier,
+                        handlers: types[i].handlers
+                    };
+                }
+            }
+        }
+        return false; // No types we can handle
+    },
+
+    /**
+     * Check the content of the drag/drop includes a type we can handle, then, if
+     * it is, notify the browser that we want to handle it
+     * @param event e
+     * @return string type of the event or false
+     */
+    check_drag: function(e) {
+        var type = this.drag_type(e);
+        if (type) {
+            // Notify browser that we will handle this drag/drop
+            e.stopPropagation();
+            e.preventDefault();
+        }
+        return type;
+    },
+
+    /**
+     * Handle a dragenter event: add a suitable 'add here' message
+     * when a drag event occurs, containing a registered data type
+     * @param e event data
+     * @return false to prevent the event from continuing to be processed
+     */
+    drag_enter: function(e) {
+        if (!(type = this.check_drag(e))) {
+            return false;
+        }
+
+        var section = this.get_section(e.currentTarget);
+        if (!section) {
+            return false;
+        }
+
+        if (this.currentsection && this.currentsection != section) {
+            this.currentsection = section;
+            this.entercount = 1;
+        } else {
+            this.entercount++;
+            if (this.entercount > 2) {
+                this.entercount = 2;
+                return false;
+            }
+        }
+
+        this.show_preview_element(section, type);
+
+        return false;
+    },
+
+    /**
+     * Handle a dragleave event: remove the 'add here' message (if present)
+     * @param e event data
+     * @return false to prevent the event from continuing to be processed
+     */
+    drag_leave: function(e) {
+        if (!this.check_drag(e)) {
+            return false;
+        }
+
+        this.entercount--;
+        if (this.entercount == 1) {
+            return false;
+        }
+        this.entercount = 0;
+        this.currentsection = null;
+
+        this.hide_preview_element();
+        return false;
+    },
+
+    /**
+     * Handle a dragover event: just prevent the browser default (necessary
+     * to allow drag and drop handling to work)
+     * @param e event data
+     * @return false to prevent the event from continuing to be processed
+     */
+    drag_over: function(e) {
+        this.check_drag(e);
+        return false;
+    },
+
+    /**
+     * Handle a drop event: hide the 'add here' message, check the attached
+     * data type and start the upload process
+     * @param e event data
+     * @return false to prevent the event from continuing to be processed
+     */
+    drop: function(e) {
+        if (!(type = this.check_drag(e))) {
+            return false;
+        }
+
+        this.hide_preview_element();
+
+        // Work out the number of the section we are on (from its id)
+        var section = this.get_section(e.currentTarget);
+        var sectionnumber = this.get_section_number(section);
+
+        // Process the file or the included data
+        if (type.type == 'Files') {
+            var files = e._event.dataTransfer.files;
+            for (var i=0, f; f=files[i]; i++) {
+                this.handle_file(f, section, sectionnumber);
+            }
+        } else {
+            var contents = e._event.dataTransfer.getData(type.realtype);
+            if (contents) {
+                this.handle_item(type, contents, section, sectionnumber);
+            }
+        }
+
+        return false;
+    },
+
+    /**
+     * Find or create the 'ul' element that contains all of the module
+     * instances in this section
+     * @param section the DOM element representing the section
+     * @return false to prevent the event from continuing to be processed
+     */
+    get_mods_element: function(section) {
+        // Find the 'ul' containing the list of mods
+        var modsel = section.one(this.modslistselector);
+        if (!modsel) {
+            // Create the above 'ul' if it doesn't exist
+            var modsel = document.createElement('ul');
+            modsel.className = 'section img-text';
+            var contentel = section.get('children').pop();
+            var brel = contentel.get('children').pop();
+            contentel.insertBefore(modsel, brel);
+            modsel = this.Y.one(modsel);
+        }
+
+        return modsel;
+    },
+
+    /**
+     * Add a new dummy item to the list of mods, to be replaced by a real
+     * item & link once the AJAX upload call has completed
+     * @param name the label to show in the element
+     * @param section the DOM element reperesenting the course section
+     * @return DOM element containing the new item
+     */
+    add_resource_element: function(name, section) {
+        var modsel = this.get_mods_element(section);
+
+        var resel = {
+            parent: modsel,
+            li: document.createElement('li'),
+            div: document.createElement('div'),
+            a: document.createElement('a'),
+            icon: document.createElement('img'),
+            namespan: document.createElement('span'),
+            progressouter: document.createElement('span'),
+            progress: document.createElement('span')
+        };
+
+        resel.li.className = 'activity resource modtype_resource';
+
+        resel.div.className = 'mod-indent';
+        resel.li.appendChild(resel.div);
+
+        resel.a.href = '#';
+        resel.div.appendChild(resel.a);
+
+        resel.icon.src = M.util.image_url('i/ajaxloader');
+        resel.icon.className = 'activityicon';
+        resel.a.appendChild(resel.icon);
+
+        resel.a.appendChild(document.createTextNode(' '));
+
+        resel.namespan.className = 'instancename';
+        resel.namespan.innerHTML = name;
+        resel.a.appendChild(resel.namespan);
+
+        resel.div.appendChild(document.createTextNode(' '));
+
+        resel.progressouter.className = 'dndupload-progress-outer';
+        resel.progress.className = 'dndupload-progress-inner';
+        resel.progress.innerHTML = '&nbsp;';
+        resel.progressouter.appendChild(resel.progress);
+        resel.div.appendChild(resel.progressouter);
+
+        modsel.insertBefore(resel.li, modsel.get('children').pop()); // Leave the 'preview element' at the bottom
+
+        return resel;
+    },
+
+    /**
+     * Hide any visible dndupload-preview elements on the page
+     */
+    hide_preview_element: function() {
+        this.Y.all('li.dndupload-preview').addClass('dndupload-hidden');
+    },
+
+    /**
+     * Unhide the preview element for the given section and set it to display
+     * the correct message
+     * @param section the YUI node representing the selected course section
+     * @param type the details of the data type detected in the drag (including the message to display)
+     */
+    show_preview_element: function(section, type) {
+        this.hide_preview_element();
+        var preview = section.one('li.dndupload-preview').removeClass('dndupload-hidden');
+        preview.one('span').setContent(type.addmessage);
+    },
+
+    /**
+     * Add the preview element to a course section. Note: this needs to be done before 'addEventListener'
+     * is called, otherwise Firefox will ignore events generated when the mouse is over the preview
+     * element (instead of passing them up to the parent element)
+     * @param section the YUI node representing the selected course section
+     */
+    add_preview_element: function(section) {
+        var modsel = this.get_mods_element(section);
+        var preview = {
+            li: document.createElement('li'),
+            div: document.createElement('div'),
+            icon: document.createElement('img'),
+            namespan: document.createElement('span')
+        };
+
+        preview.li.className = 'dndupload-preview activity resource modtype_resource dndupload-hidden';
+
+        preview.div.className = 'mod-indent';
+        preview.li.appendChild(preview.div);
+
+        preview.icon.src = M.util.image_url('t/addfile');
+        preview.div.appendChild(preview.icon);
+
+        preview.div.appendChild(document.createTextNode(' '));
+
+        preview.namespan.className = 'instancename';
+        preview.namespan.innerHTML = M.util.get_string('addfilehere', 'moodle');
+        preview.div.appendChild(preview.namespan);
+
+        modsel.appendChild(preview.li);
+    },
+
+    /**
+     * Find the registered handler for the given file type. If there is more than one, ask the
+     * user which one to use. Then upload the file to the server
+     * @param file the details of the file, taken from the FileList in the drop event
+     * @param section the DOM element representing the selected course section
+     * @param sectionnumber the number of the selected course section
+     */
+    handle_file: function(file, section, sectionnumber) {
+        var handlers = new Array();
+        var filehandlers = this.handlers.filehandlers;
+        var extension = '';
+        var dotpos = file.name.lastIndexOf('.');
+        if (dotpos != -1) {
+            extension = file.name.substr(dotpos+1, file.name.length);
+        }
+
+        for (var i=0; i<filehandlers.length; i++) {
+            if (filehandlers[i].extension == '*' || filehandlers[i].extension == extension) {
+                handlers.push(filehandlers[i]);
+            }
+        }
+
+        if (handlers.length == 0) {
+            // No handlers at all (not even 'resource'?)
+            return;
+        }
+
+        if (handlers.length == 1) {
+            this.upload_file(file, section, sectionnumber, handlers[0].module);
+            return;
+        }
+
+        this.file_handler_dialog(handlers, extension, file, section, sectionnumber);
+    },
+
+    /**
+     * Show a dialog box, allowing the user to choose what to do with the file they are uploading
+     * @param handlers the available handlers to choose between
+     * @param extension the extension of the file being uploaded
+     * @param file the File object being uploaded
+     * @param section the DOM element of the section being uploaded to
+     * @param sectionnumber the number of the selected course section
+     */
+    file_handler_dialog: function(handlers, extension, file, section, sectionnumber) {
+        if (this.uploaddialog) {
+            var details = new Object();
+            details.isfile = true;
+            details.handlers = handlers;
+            details.extension = extension;
+            details.file = file;
+            details.section = section;
+            details.sectionnumber = sectionnumber;
+            this.uploadqueue.push(details);
+            return;
+        }
+        this.uploaddialog = true;
+
+        var timestamp = new Date().getTime();
+        var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
+        var content = '';
+        var sel;
+        if (extension in this.lastselected) {
+            sel = this.lastselected[extension];
+        } else {
+            sel = handlers[0].module;
+        }
+        content += '<p>'+M.util.get_string('actionchoice', 'moodle', file.name)+'</p>';
+        content += '<div id="dndupload_handlers'+uploadid+'">';
+        for (var i=0; i<handlers.length; i++) {
+            var id = 'dndupload_handler'+uploadid+handlers[i].module;
+            var checked = (handlers[i].module == sel) ? 'checked="checked" ' : '';
+            content += '<input type="radio" name="handler" value="'+handlers[i].module+'" id="'+id+'" '+checked+'/>';
+            content += ' <label for="'+id+'">';
+            content += handlers[i].message;
+            content += '</label><br/>';
+        }
+        content += '</div>';
+
+        var Y = this.Y;
+        var self = this;
+        var panel = new Y.Panel({
+            bodyContent: content,
+            width: 350,
+            zIndex: 5,
+            centered: true,
+            modal: true,
+            visible: true,
+            render: true,
+            buttons: [{
+                value: M.util.get_string('upload', 'moodle'),
+                action: function(e) {
+                    e.preventDefault();
+                    // Find out which module was selected
+                    var module = false;
+                    var div = Y.one('#dndupload_handlers'+uploadid);
+                    div.all('input').each(function(input) {
+                        if (input.get('checked')) {
+                            module = input.get('value');
+                        }
+                    });
+                    if (!module) {
+                        return;
+                    }
+                    panel.hide();
+                    // Remember this selection for next time
+                    self.lastselected[extension] = module;
+                    // Do the upload
+                    self.upload_file(file, section, sectionnumber, module);
+                },
+                section: Y.WidgetStdMod.FOOTER
+            },{
+                value: M.util.get_string('cancel', 'moodle'),
+                action: function(e) {
+                    e.preventDefault();
+                    panel.hide();
+                },
+                section: Y.WidgetStdMod.FOOTER
+            }]
+        });
+        // When the panel is hidden - destroy it and then check for other pending uploads
+        panel.after("visibleChange", function(e) {
+            if (!panel.get('visible')) {
+                panel.destroy(true);
+                self.check_upload_queue();
+            }
+        });
+    },
+
+    /**
+     * Check to see if there are any other dialog boxes to show, now that the current one has
+     * been dealt with
+     */
+    check_upload_queue: function() {
+        this.uploaddialog = false;
+        if (this.uploadqueue.length == 0) {
+            return;
+        }
+
+        var details = this.uploadqueue.shift();
+        if (details.isfile) {
+            this.file_handler_dialog(details.handlers, details.extension, details.file, details.section, details.sectionnumber);
+        } else {
+            this.handle_item(details.type, details.contents, details.section, details.sectionnumber);
+        }
+    },
+
+    /**
+     * Do the file upload: show the dummy element, use an AJAX call to send the data
+     * to the server, update the progress bar for the file, then replace the dummy
+     * element with the real information once the AJAX call completes
+     * @param file the details of the file, taken from the FileList in the drop event
+     * @param section the DOM element representing the selected course section
+     * @param sectionnumber the number of the selected course section
+     */
+    upload_file: function(file, section, sectionnumber, module) {
+
+        // This would be an ideal place to use the Y.io function
+        // however, this does not support data encoded using the
+        // FormData object, which is needed to transfer data from
+        // the DataTransfer object into an XMLHTTPRequest
+        // This can be converted when the YUI issue has been integrated:
+        // http://yuilibrary.com/projects/yui3/ticket/2531274
+        var xhr = new XMLHttpRequest();
+        var self = this;
+
+        if (file.size > this.maxbytes) {
+            alert("'"+file.name+"' "+M.util.get_string('filetoolarge', 'moodle'));
+            return;
+        }
+
+        // Add the file to the display
+        var resel = this.add_resource_element(file.name, section);
+
+        // Update the progress bar as the file is uploaded
+        xhr.upload.addEventListener('progress', function(e) {
+            if (e.lengthComputable) {
+                var percentage = Math.round((e.loaded * 100) / e.total);
+                resel.progress.style.width = percentage + '%';
+            }
+        }, false);
+
+        // Wait for the AJAX call to complete, then update the
+        // dummy element with the returned details
+        xhr.onreadystatechange = function() {
+            if (xhr.readyState == 4) {
+                if (xhr.status == 200) {
+                    var result = JSON.parse(xhr.responseText);
+                    if (result) {
+                        if (result.error == 0) {
+                            // All OK - update the dummy element
+                            resel.icon.src = result.icon;
+                            resel.a.href = result.link;
+                            resel.namespan.innerHTML = result.name;
+                            resel.div.removeChild(resel.progressouter);
+                            resel.li.id = result.elementid;
+                            resel.div.innerHTML += result.commands;
+                            if (result.onclick) {
+                                resel.a.onclick = result.onclick;
+                            }
+                            if (self.Y.UA.gecko > 0) {
+                                // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+                                // log the user out when clicking on the link (before refreshing the page).
+                                resel.div.innerHTML = unescape(resel.div.innerHTML);
+                            }
+                            self.add_editing(result.elementid);
+                        } else {
+                            // Error - remove the dummy element
+                            resel.parent.removeChild(resel.li);
+                            alert(result.error);
+                        }
+                    }
+                } else {
+                    alert(M.util.get_string('servererror', 'moodle'));
+                }
+            }
+        };
+
+        // Prepare the data to send
+        var formData = new FormData();
+        formData.append('repo_upload_file', file);
+        formData.append('sesskey', M.cfg.sesskey);
+        formData.append('course', this.courseid);
+        formData.append('section', sectionnumber);
+        formData.append('module', module);
+        formData.append('type', 'Files');
+
+        // Send the AJAX call
+        xhr.open("POST", this.url, true);
+        xhr.send(formData);
+    },
+
+    /**
+     * Show a dialog box to gather the name of the resource / activity to be created
+     * from the uploaded content
+     * @param type the details of the type of content
+     * @param contents the contents to be uploaded
+     * @section the DOM element for the section being uploaded to
+     * @sectionnumber the number of the section being uploaded to
+     */
+    handle_item: function(type, contents, section, sectionnumber) {
+        if (type.handlers.length == 0) {
+            // Nothing to handle this - should not have got here
+            return;
+        }
+
+        if (this.uploaddialog) {
+            var details = new Object();
+            details.isfile = false;
+            details.type = type;
+            details.contents = contents;
+            details.section = section;
+            details.setcionnumber = sectionnumber;
+            this.uploadqueue.push(details);
+            return;
+        }
+        this.uploaddialog = true;
+
+        var timestamp = new Date().getTime();
+        var uploadid = Math.round(Math.random()*100000)+'-'+timestamp;
+        var nameid = 'dndupload_handler_name'+uploadid;
+        var content = '';
+        content += '<label for="'+nameid+'">'+type.namemessage+'</label>';
+        content += ' <input type="text" id="'+nameid+'" value="" />';
+        if (type.handlers.length > 1) {
+            content += '<div id="dndupload_handlers'+uploadid+'">';
+            var sel = type.handlers[0].module;
+            for (var i=0; i<type.handlers.length; i++) {
+                var id = 'dndupload_handler'+uploadid;
+                var checked = (type.handlers[i].module == sel) ? 'checked="checked" ' : '';
+                content += '<input type="radio" name="handler" value="'+type.handlers[i].module+'" id="'+id+'" '+checked+'/>';
+                content += ' <label for="'+id+'">';
+                content += type.handlers[i].message;
+                content += '</label><br/>';
+            }
+            content += '</div>';
+        }
+
+        var Y = this.Y;
+        var self = this;
+        var panel = new Y.Panel({
+            bodyContent: content,
+            width: 350,
+            zIndex: 5,
+            centered: true,
+            modal: true,
+            visible: true,
+            render: true,
+            buttons: [{
+                value: M.util.get_string('upload', 'moodle'),
+                action: function(e) {
+                    e.preventDefault();
+                    var name = Y.one('#dndupload_handler_name'+uploadid).get('value');
+                    name = name.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // Trim
+                    if (name == '') {
+                        return;
+                    }
+                    var module = false;
+                    if (type.handlers.length > 1) {
+                        // Find out which module was selected
+                        var div = Y.one('#dndupload_handlers'+uploadid);
+                        div.all('input').each(function(input) {
+                            if (input.get('checked')) {
+                                module = input.get('value');
+                            }
+                        });
+                        if (!module) {
+                            return;
+                        }
+                    } else {
+                        module = type.handlers[0].module;
+                    }
+                    panel.hide();
+                    // Do the upload
+                    self.upload_item(name, type.type, contents, section, sectionnumber, module);
+                },
+                section: Y.WidgetStdMod.FOOTER
+            },{
+                value: M.util.get_string('cancel', 'moodle'),
+                action: function(e) {
+                    e.preventDefault();
+                    panel.hide();
+                },
+                section: Y.WidgetStdMod.FOOTER
+            }]
+        });
+        // When the panel is hidden - destroy it and then check for other pending uploads
+        panel.after("visibleChange", function(e) {
+            if (!panel.get('visible')) {
+                panel.destroy(true);
+                self.check_upload_queue();
+            }
+        });
+        // Focus on the 'name' box
+        Y.one('#'+nameid).focus();
+    },
+
+    /**
+     * Upload any data types that are not files: display a dummy resource element, send
+     * the data to the server, update the progress bar for the file, then replace the
+     * dummy element with the real information once the AJAX call completes
+     * @param name the display name for the resource / activity to create
+     * @param type the details of the data type found in the drop event
+     * @param contents the actual data that was dropped
+     * @param section the DOM element representing the selected course section
+     * @param sectionnumber the number of the selected course section
+     * @param module the module chosen to handle this upload
+     */
+    upload_item: function(name, type, contents, section, sectionnumber, module) {
+
+        // This would be an ideal place to use the Y.io function
+        // however, this does not support data encoded using the
+        // FormData object, which is needed to transfer data from
+        // the DataTransfer object into an XMLHTTPRequest
+        // This can be converted when the YUI issue has been integrated:
+        // http://yuilibrary.com/projects/yui3/ticket/2531274
+        var xhr = new XMLHttpRequest();
+        var self = this;
+
+        // Add the item to the display
+        var resel = this.add_resource_element(name, section);
+
+        // Wait for the AJAX call to complete, then update the
+        // dummy element with the returned details
+        xhr.onreadystatechange = function() {
+            if (xhr.readyState == 4) {
+                if (xhr.status == 200) {
+                    var result = JSON.parse(xhr.responseText);
+                    if (result) {
+                        if (result.error == 0) {
+                            // All OK - update the dummy element
+                            resel.icon.src = result.icon;
+                            resel.a.href = result.link;
+                            resel.namespan.innerHTML = result.name;
+                            resel.div.removeChild(resel.progressouter);
+                            resel.li.id = result.elementid;
+                            resel.div.innerHTML += result.commands;
+                            if (result.onclick) {
+                                resel.a.onclick = result.onclick;
+                            }
+                            if (self.Y.UA.gecko > 0) {
+                                // Fix a Firefox bug which makes sites with a '~' in their wwwroot
+                                // log the user out when clicking on the link (before refreshing the page).
+                                resel.div.innerHTML = unescape(resel.div.innerHTML);
+                            }
+                            self.add_editing(result.elementid, sectionnumber);
+                        } else {
+                            // Error - remove the dummy element
+                            resel.parent.removeChild(resel.li);
+                            alert(result.error);
+                        }
+                    }
+                } else {
+                    alert(M.util.get_string('servererror', 'moodle'));
+                }
+            }
+        };
+
+        // Prepare the data to send
+        var formData = new FormData();
+        formData.append('contents', contents);
+        formData.append('displayname', name);
+        formData.append('sesskey', M.cfg.sesskey);
+        formData.append('course', this.courseid);
+        formData.append('section', sectionnumber);
+        formData.append('type', type);
+        formData.append('module', module);
+
+        // Send the data
+        xhr.open("POST", this.url, true);
+        xhr.send(formData);
+    },
+
+    /**
+     * Call the AJAX course editing initialisation to add the editing tools
+     * to the newly-created resource link
+     * @param elementid the id of the DOM element containing the new resource link
+     * @param sectionnumber the number of the selected course section
+     */
+    add_editing: function(elementid) {
+        YUI().use('moodle-course-coursebase', function(Y) {
+            M.course.coursebase.invoke_function('setup_for_resource', '#' + elementid);
+        });
+    }
+};
diff --git a/course/dndupload.php b/course/dndupload.php
new file mode 100644 (file)
index 0000000..c4df5a8
--- /dev/null
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Starting point for drag and drop course uploads
+ *
+ * @package    core
+ * @subpackage lib
+ * @copyright  2012 Davo smith
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('AJAX_SCRIPT', true);
+
+require_once(dirname(dirname(__FILE__)).'/config.php');
+require_once($CFG->dirroot.'/course/dnduploadlib.php');
+
+$courseid = required_param('course', PARAM_INT);
+$section = required_param('section', PARAM_INT);
+$type = required_param('type', PARAM_TEXT);
+$modulename = required_param('module', PARAM_PLUGIN);
+$displayname = optional_param('displayname', null, PARAM_TEXT);
+$contents = optional_param('contents', null, PARAM_RAW); // It will be up to each plugin to clean this data, before saving it.
+
+$dndproc = new dndupload_ajax_processor($courseid, $section, $type, $modulename);
+$dndproc->process($displayname, $contents);
diff --git a/course/dnduploadlib.php b/course/dnduploadlib.php
new file mode 100644 (file)
index 0000000..c0ed633
--- /dev/null
@@ -0,0 +1,671 @@
+<?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/>.
+
+/**
+ * Library to handle drag and drop course uploads
+ *
+ * @package    core
+ * @subpackage lib
+ * @copyright  2012 Davo smith
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot.'/repository/lib.php');
+require_once($CFG->dirroot.'/repository/upload/lib.php');
+require_once($CFG->dirroot.'/course/lib.php');
+
+/**
+ * Add the Javascript to enable drag and drop upload to a course page
+ *
+ * @param object $course The currently displayed course
+ * @param array $modnames The list of enabled (visible) modules on this site
+ * @return void
+ */
+function dndupload_add_to_course($course, $modnames) {
+    global $CFG, $PAGE;
+
+    // Get all handlers.
+    $handler = new dndupload_handler($course, $modnames);
+    $jsdata = $handler->get_js_data();
+    if (empty($jsdata->types) && empty($jsdata->filehandlers)) {
+        return; // No valid handlers - don't enable drag and drop.
+    }
+
+    // Add the javascript to the page.
+    $jsmodule = array(
+        'name' => 'coursedndupload',
+        'fullpath' => new moodle_url('/course/dndupload.js'),
+        'strings' => array(
+            array('addfilehere', 'moodle'),
+            array('dndworkingfiletextlink', 'moodle'),
+            array('dndworkingfilelink', 'moodle'),
+            array('dndworkingfiletext', 'moodle'),
+            array('dndworkingfile', 'moodle'),
+            array('dndworkingtextlink', 'moodle'),
+            array('dndworkingtext', 'moodle'),
+            array('dndworkinglink', 'moodle'),
+            array('filetoolarge', 'moodle'),
+            array('actionchoice', 'moodle'),
+            array('servererror', 'moodle'),
+            array('upload', 'moodle'),
+            array('cancel', 'moodle')
+        ),
+        'requires' => array('node', 'event', 'panel', 'json')
+    );
+    $vars = array(
+        array('courseid' => $course->id,
+              'maxbytes' => get_max_upload_file_size($CFG->maxbytes, $course->maxbytes),
+              'handlers' => $handler->get_js_data())
+    );
+
+    $PAGE->requires->js_init_call('M.course_dndupload.init', $vars, true, $jsmodule);
+}
+
+
+/**
+ * Stores all the information about the available dndupload handlers
+ *
+ * @package    core
+ * @copyright  2012 Davo Smith
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class dndupload_handler {
+
+    /**
+     * @var array A list of all registered mime types that can be dropped onto a course
+     *            along with the modules that will handle them.
+     */
+    protected $types = array();
+
+    /**
+     * @var array  A list of the different file types&nbs