Merge branch 'get_users_by_capability_fix_master' of http://github.com/timgus/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 1 Jul 2013 22:46:30 +0000 (00:46 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 1 Jul 2013 22:46:30 +0000 (00:46 +0200)
473 files changed:
admin/auth_config.php
admin/blocks.php
admin/cli/install.php
admin/courseformats.php
admin/cron.php
admin/editors.php
admin/enrol.php
admin/environment.xml
admin/localplugins.php
admin/modules.php
admin/plagiarism.php
admin/plugins.php
admin/renderer.php
admin/reports.php
admin/roles/classes/preset.php [new file with mode: 0644]
admin/roles/classes/preset_form.php [new file with mode: 0644]
admin/roles/define.php
admin/roles/lib.php
admin/roles/manage.php
admin/roles/role_schema.xml [new file with mode: 0644]
admin/roles/tests/preset_test.php [new file with mode: 0644]
admin/settings/development.php
admin/tool/customlang/edit.php
admin/tool/customlang/locallib.php
admin/tool/customlang/renderer.php
admin/tool/installaddon/classes/installer.php
admin/tool/installaddon/classes/installfromzip_form.php
admin/tool/installaddon/deploy.php
admin/tool/installaddon/index.php
admin/tool/installaddon/permcheck.php
admin/tool/installaddon/tests/installer_test.php
admin/tool/installaddon/tests/validator_test.php
admin/tool/installaddon/validate.php
admin/tool/profiling/export.php [new file with mode: 0644]
admin/tool/profiling/import.php [new file with mode: 0644]
admin/tool/profiling/import_form.php [new file with mode: 0644]
admin/tool/profiling/index.php
admin/tool/profiling/lang/en/tool_profiling.php
admin/tool/profiling/version.php
admin/tool/uploaduser/db/access.php [new file with mode: 0644]
admin/tool/uploaduser/lang/en/tool_uploaduser.php
admin/tool/uploaduser/picture.php
admin/tool/uploaduser/settings.php
admin/tool/uploaduser/version.php
admin/tool/xmldb/actions/XMLDBAction.class.php
admin/tools.php
admin/user/user_bulk_confirm.php
admin/user/user_bulk_delete.php
admin/webservice/protocols.php
auth/cas/config.html
auth/ldap/auth.php
auth/ldap/config.html
backup/backup.class.php
backup/controller/backup_controller.class.php
backup/controller/tests/controller_test.php
backup/moodle2/backup_stepslib.php
backup/moodle2/restore_course_task.class.php
backup/moodle2/restore_final_task.class.php
backup/moodle2/restore_root_task.class.php
backup/moodle2/restore_stepslib.php
backup/upgrade.txt
backup/util/dbops/backup_controller_dbops.class.php
backup/util/dbops/restore_controller_dbops.class.php
backup/util/dbops/restore_dbops.class.php
backup/util/dbops/tests/backup_dbops_test.php [moved from backup/util/dbops/tests/dbops_test.php with 59% similarity]
backup/util/dbops/tests/restore_dbops_test.php [new file with mode: 0644]
backup/util/helper/backup_file_manager.class.php
backup/util/helper/backup_general_helper.class.php
backup/util/ui/tests/behat/behat_backup.php
backup/util/ui/tests/behat/restore_moodle2_courses.feature
badges/criteria/award_criteria.php
badges/criteria/award_criteria_course.php
blocks/badges/db/access.php
blocks/badges/version.php
blocks/completionstatus/details.php
blocks/course_overview/renderer.php
blocks/dock.js
blocks/navigation/renderer.php
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-debug.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation-min.js
blocks/navigation/yui/build/moodle-block_navigation-navigation/moodle-block_navigation-navigation.js
blocks/navigation/yui/src/navigation/js/navigation.js
cache/classes/helper.php
cache/classes/loaders.php
cache/stores/file/addinstanceform.php
cache/stores/memcache/addinstanceform.php
cache/stores/mongodb/addinstanceform.php
cohort/index.php
config-dist.php
course/edit_form.php
course/format/social/format.php
course/format/weeks/format.js
course/format/weeks/lib.php
course/lib.php
course/moodleform_mod.php
course/renderer.php
course/tests/courselib_test.php
course/view.php
enrol/externallib.php
enrol/flatfile/lib.php
enrol/flatfile/tests/flatfile_test.php
enrol/manual/lib.php
enrol/manual/settings.php
enrol/manual/tests/lib_test.php
enrol/tests/externallib_role_test.php [new file with mode: 0644]
enrol/tests/externallib_test.php
files/tests/externallib_test.php [new file with mode: 0644]
filter/urltolink/filter.php
filter/urltolink/tests/filter_test.php
grade/edit/tree/grade.php
grade/report/grader/ajax_callbacks.php
grade/report/grader/lib.php
grade/report/grader/styles.css
index.php
install.php
install/lang/es/error.php
install/lang/pan/langconfig.php [new file with mode: 0644]
install/lang/pt_br/error.php
install/lang/sv/error.php
lang/README.txt
lang/en/admin.php
lang/en/auth.php
lang/en/cache.php
lang/en/editor.php
lang/en/enrol.php
lang/en/hub.php
lang/en/install.php
lang/en/moodle.php
lang/en/plagiarism.php
lang/en/plugin.php
lang/en/role.php
lib/accesslib.php
lib/adminlib.php
lib/ajax/getnavbranch.php
lib/authlib.php
lib/behat/behat_field_manager.php
lib/behat/behat_files.php
lib/behat/form_field/behat_form_field.php
lib/behat/form_field/behat_form_radio.php
lib/blocklib.php
lib/classes/component.php [new file with mode: 0644]
lib/coursecatlib.php
lib/csslib.php
lib/datalib.php
lib/db/access.php
lib/db/caches.php
lib/db/install.php
lib/db/install.xml
lib/db/upgrade.php
lib/deprecatedlib.php
lib/editor/tinymce/adminlib.php
lib/editor/tinymce/lang/en/editor_tinymce.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/subplugins.php
lib/editor/tinymce/tests/editor_test.php
lib/externallib.php
lib/filelib.php
lib/filestorage/tests/fixtures/test_osx_compress.zip [new file with mode: 0644]
lib/filestorage/tests/fixtures/test_thumbsdb.zip [new file with mode: 0644]
lib/filestorage/tests/zip_packer_test.php
lib/filestorage/zip_archive.php
lib/filterlib.php
lib/form/dateselector.php
lib/form/editor.php
lib/form/form.js
lib/form/hidden.php
lib/form/yui/dateselector/dateselector.js
lib/formslib.php
lib/html2text.php
lib/html2text_readme.txt
lib/jslib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputfactories.php
lib/outputlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/pagelib.php
lib/phpunit/bootstrap.php
lib/phpunit/classes/autoloader.php [new file with mode: 0644]
lib/pluginlib.php
lib/rsslib.php
lib/setup.php
lib/simplepie/moodle_simplepie.php
lib/statslib.php
lib/testing/classes/tests_finder.php
lib/tests/accesslib_test.php
lib/tests/behat/behat_forms.php
lib/tests/component_test.php [new file with mode: 0644]
lib/tests/csslib_test.php
lib/tests/externallib_test.php
lib/tests/filelib_test.php
lib/tests/html2text_test.php
lib/tests/moodlelib_test.php
lib/tests/rsslib_test.php
lib/tests/weblib_test.php
lib/upgrade.txt
lib/weblib.php
lib/xhprof/readme_moodle.txt
lib/xhprof/xhprof_moodle.php
lib/yui/build/moodle-core-blocks/moodle-core-blocks-debug.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks-min.js
lib/yui/build/moodle-core-blocks/moodle-core-blocks.js
lib/yui/dragdrop/dragdrop.js
lib/yui/src/blocks/build.json
lib/yui/src/blocks/js/blockregion.js [new file with mode: 0644]
lib/yui/src/blocks/js/blocks.js
lib/yui/src/blocks/js/manager.js [new file with mode: 0644]
mod/assign/backup/moodle2/backup_assign_stepslib.php
mod/assign/backup/moodle2/restore_assign_stepslib.php
mod/assign/batchsetallocatedmarkerform.php [new file with mode: 0644]
mod/assign/batchsetmarkingworkflowstateform.php [new file with mode: 0644]
mod/assign/db/access.php
mod/assign/db/install.xml
mod/assign/db/log.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/locallib.php
mod/assign/mod_form.php
mod/assign/module.js
mod/assign/renderer.php
mod/assign/settings.php
mod/assign/submissionplugin.php
mod/assign/tests/generator/lib.php
mod/assign/tests/locallib_test.php
mod/assign/upgradelib.php
mod/assign/version.php
mod/assignment/lib.php
mod/book/edit.php
mod/data/edit.php
mod/data/export.php
mod/data/field.php
mod/data/lib.php
mod/data/preset.php
mod/data/templates.php
mod/data/view.php
mod/feedback/edit_form.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/imscp/locallib.php
mod/lesson/locallib.php
mod/lesson/pagetypes/matching.php
mod/lesson/pagetypes/multichoice.php
mod/lesson/tests/behat/date_availability.feature
mod/lti/mod_form.php
mod/quiz/attemptlib.php
mod/quiz/lang/en/quiz.php
mod/quiz/locallib.php
mod/quiz/mod_form.php
mod/quiz/settings.php
mod/quiz/startattempt.php
mod/scorm/db/upgrade.php
mod/scorm/lang/en/scorm.php
mod/scorm/lib.php
mod/scorm/loadSCO.php
mod/scorm/locallib.php
mod/scorm/mod_form.php
mod/scorm/module.js
mod/scorm/settings.php
mod/scorm/styles.css
mod/scorm/version.php
mod/wiki/pagelib.php
mod/wiki/renderer.php
mod/wiki/view.php
notes/delete.php
notes/edit_form.php
phpunit.xml.dist
question/behaviour/behaviourbase.php
question/behaviour/manualgraded/behaviour.php
question/behaviour/upgrade.txt
question/engine/datalib.php
question/type/calculated/datasetitems_form.php
question/type/calculated/question.php
question/type/calculated/questiontype.php
question/type/calculated/renderer.php
question/type/calculated/tests/helper.php
question/type/calculated/tests/question_test.php
question/type/calculatedmulti/questiontype.php
question/type/calculatedsimple/edit_calculatedsimple_form.php
question/type/calculatedsimple/questiontype.php
question/type/essay/question.php
question/type/essay/tests/question_test.php
question/type/questiontypebase.php
question/type/shortanswer/db/upgrade.php
question/type/upgrade.txt
report/progress/index.php
report/progress/textrotate.js
repository/recent/tests/behat/behat_repository_recent.php
repository/tests/behat/behat_filepicker.php
repository/upload/tests/behat/behat_repository_upload.php
tag/edit_form.php
theme/base/style/admin.css
theme/base/style/core.css
theme/bootstrapbase/config.php
theme/bootstrapbase/layout/columns1.php [new file with mode: 0644]
theme/bootstrapbase/layout/columns2.php [new file with mode: 0644]
theme/bootstrapbase/layout/columns3.php [new file with mode: 0644]
theme/bootstrapbase/layout/embedded.php
theme/bootstrapbase/layout/general.php [deleted file]
theme/bootstrapbase/layout/secure.php [new file with mode: 0644]
theme/bootstrapbase/less/moodle.less
theme/bootstrapbase/less/moodle/admin.less
theme/bootstrapbase/less/moodle/buttons.less
theme/bootstrapbase/less/moodle/core.less
theme/bootstrapbase/less/moodle/expendable.less
theme/bootstrapbase/less/moodle/grade.less
theme/bootstrapbase/less/moodle/modules.less
theme/bootstrapbase/less/moodle/question.less
theme/bootstrapbase/less/moodle/reports.less [new file with mode: 0644]
theme/bootstrapbase/less/moodle/responsive.less
theme/bootstrapbase/renderers/core_renderer.php
theme/bootstrapbase/style/moodle.css
theme/boxxie/layout/frontpage.php
theme/boxxie/layout/general.php
theme/clean/config.php
theme/clean/layout/columns1.php [new file with mode: 0644]
theme/clean/layout/columns2.php [new file with mode: 0644]
theme/clean/layout/columns3.php [new file with mode: 0644]
theme/clean/layout/embedded.php [new file with mode: 0644]
theme/clean/layout/general.php [deleted file]
theme/clean/layout/secure.php [new file with mode: 0644]
theme/clean/lib.php
theme/formal_white/config.php
theme/formal_white/db/upgrade.php
theme/formal_white/lang/en/theme_formal_white.php
theme/formal_white/layout/embedded.php
theme/formal_white/layout/frontpage.php
theme/formal_white/layout/general.php
theme/formal_white/layout/report.php
theme/formal_white/lib.php
theme/formal_white/pix/trend/blueberry/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/blueberry.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/blueberry/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/lemon.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lemon/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/lime.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/lime/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/mink/bg_bread.jpg [moved from theme/formal_white/pix/bg_bread.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/custommenubg.jpg [moved from theme/formal_white/pix/custommenubg.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/gradient-sb.jpg [moved from theme/formal_white/pix/gradient-sb.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/gradient_h.jpg [moved from theme/formal_white/pix/gradient_h.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/hgradient.jpg [moved from theme/formal_white/pix/hgradient.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/mink.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/mink/roundcorner/body_l.jpg [moved from theme/formal_white/pix/roundcorner/body_l.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/body_r.jpg [moved from theme/formal_white/pix/roundcorner/body_r.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/footer.jpg [moved from theme/formal_white/pix/roundcorner/footer.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/footer_l.jpg [moved from theme/formal_white/pix/roundcorner/footer_l.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/footer_r.jpg [moved from theme/formal_white/pix/roundcorner/footer_r.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/header.jpg [moved from theme/formal_white/pix/roundcorner/header.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/header_l.jpg [moved from theme/formal_white/pix/roundcorner/header_l.jpg with 100% similarity]
theme/formal_white/pix/trend/mink/roundcorner/header_r.jpg [moved from theme/formal_white/pix/roundcorner/header_r.jpg with 100% similarity]
theme/formal_white/pix/trend/orange/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/orange.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/orange/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/peach.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/peach/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/bg_bread.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/custommenubg.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/gradient-sb.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/gradient_h.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/hgradient.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/body_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/body_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/footer.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/footer_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/footer_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/header.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/header_l.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/roundcorner/header_r.jpg [new file with mode: 0644]
theme/formal_white/pix/trend/silver/silver.jpg [new file with mode: 0644]
theme/formal_white/settings.php
theme/formal_white/style/block.css
theme/formal_white/style/calendar.css
theme/formal_white/style/core.css
theme/formal_white/style/course.css
theme/formal_white/style/formal_white.css
theme/formal_white/style/frame.css
theme/formal_white/style/menu.css
theme/formal_white/style/quiz.css
theme/formal_white/version.php
theme/formfactor/layout/frontpage.php
theme/formfactor/layout/general.php
theme/image.php
theme/jquery.php
theme/mymobile/config.php
theme/mymobile/jquery/custom131.js [moved from theme/mymobile/jquery/custom.js with 100% similarity]
theme/mymobile/jquery/jquery.mobile-1.1.1.js [deleted file]
theme/mymobile/jquery/jquery.mobile-1.3.1.js [new file with mode: 0644]
theme/mymobile/jquery/plugins.php
theme/mymobile/lib.php
theme/mymobile/pix/icons-18-black.png
theme/mymobile/pix/icons-18-white.png
theme/mymobile/pix/icons-36-black.png
theme/mymobile/pix/icons-36-white.png
theme/mymobile/style/core.css
theme/mymobile/style/jmobile11.css [deleted file]
theme/mymobile/style/jmobile131.css [new file with mode: 0644]
theme/mymobile/style/jmobile131_rtl.css [moved from theme/mymobile/style/jmobile11_rtl.css with 100% similarity]
theme/sky_high/style/admin.css
theme/standard/style/modules.css
theme/styles.php
theme/upgrade.txt
theme/yui_combo.php
theme/yui_image.php
user/editadvanced.php
user/editadvanced_form.php
user/editlib.php
user/files.php
user/index.php
user/profile.php
user/selector/module.js
version.php

index c702711..50bcfc9 100644 (file)
@@ -87,8 +87,8 @@ exit;
 // but some may want a custom one if they are offering
 // other options
 // Note: lockconfig_ fields have special handling.
-function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts, $updateopts) {
-    global $OUTPUT;
+function print_auth_lock_options($auth, $user_fields, $helptext, $retrieveopts, $updateopts, $customfields = array()) {
+    global $DB, $OUTPUT;
     echo '<tr><td colspan="3">';
     if ($retrieveopts) {
         echo $OUTPUT->heading(get_string('auth_data_mapping', 'auth'));
@@ -107,14 +107,21 @@ function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts,
 
     $pluginconfig = get_config("auth/$auth");
 
-    // helptext is on a field with rowspan
+    // Helptext is on a field with rowspan.
     if (empty($helptext)) {
-                $helptext = '&nbsp;';
+        $helptext = '&nbsp;';
     }
 
-    foreach ($user_fields as $field) {
+    // If we have custom fields then merge them with user fields.
+    if (!empty($customfields)) {
+        $user_fields = array_merge($user_fields, $customfields);
+    }
 
-        // Define some vars we'll work with
+    if (!empty($customfields)) {
+        $customfieldname = $DB->get_records('user_info_field', null, '', 'shortname, name');
+    }
+    foreach ($user_fields as $field) {
+        // Define some vars we'll work with.
         if (!isset($pluginconfig->{"field_map_$field"})) {
             $pluginconfig->{"field_map_$field"} = '';
         }
@@ -128,7 +135,7 @@ function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts,
             $pluginconfig->{"field_lock_$field"} = '';
         }
 
-        // define the fieldname we display to the  user
+        // Define the fieldname we display to the  user.
         $fieldname = $field;
         if ($fieldname === 'lang') {
             $fieldname = get_string('language');
@@ -136,6 +143,10 @@ function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts,
             $fieldname =  get_string($matches[1]) . ' ' . $matches[2];
         } elseif ($fieldname == 'url') {
             $fieldname = get_string('webpage');
+        } elseif (!empty($customfields) && in_array($field, $customfields)) {
+            // If custom field then pick name from database.
+            $fieldshortname = str_replace('profile_field_', '', $fieldname);
+            $fieldname = $customfieldname[$fieldshortname]->name;
         } else {
             $fieldname = get_string($fieldname);
         }
@@ -155,8 +166,6 @@ function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts,
                 echo '<label for="menulockconfig_field_updateremote_'.$field.'">'.get_string('auth_updateremote', 'auth') . '</label>&nbsp;';
                 echo html_writer::select($updateextoptions, "lockconfig_field_updateremote_{$field}", $pluginconfig->{"field_updateremote_$field"}, false);
                 echo '<br />';
-
-
             }
             echo '<label for="menulockconfig_field_lock_'.$field.'">'.get_string('auth_fieldlock', 'auth') . '</label>&nbsp;';
             echo html_writer::select($lockoptions, "lockconfig_field_lock_{$field}", $pluginconfig->{"field_lock_$field"}, false);
@@ -175,5 +184,3 @@ function print_auth_lock_options ($auth, $user_fields, $helptext, $retrieveopts,
         echo '</tr>';
     }
 }
-
-
index f2017b9..2ad0a0e 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 
-    // Allows the admin to configure blocks (hide/show, delete and configure)
+    // Allows the admin to configure blocks (hide/show, uninstall and configure)
 
     require_once('../config.php');
     require_once($CFG->libdir.'/adminlib.php');
     $confirm  = optional_param('confirm', 0, PARAM_BOOL);
     $hide     = optional_param('hide', 0, PARAM_INT);
     $show     = optional_param('show', 0, PARAM_INT);
-    $delete   = optional_param('delete', 0, PARAM_INT);
     $unprotect = optional_param('unprotect', 0, PARAM_INT);
     $protect = optional_param('protect', 0, PARAM_INT);
 
 /// Print headings
 
     $strmanageblocks = get_string('manageblocks');
-    $strdelete = get_string('delete');
+    $struninstall = get_string('uninstallplugin', 'core_admin');
     $strversion = get_string('version');
     $strhide = get_string('hide');
     $strshow = get_string('show');
         admin_get_root(true, false);  // settings not required - only pages
     }
 
-    if (!empty($delete) && confirm_sesskey()) {
-        echo $OUTPUT->header();
-        echo $OUTPUT->heading($strmanageblocks);
-
-        if (!$block = blocks_get_record($delete)) {
-            print_error('blockdoesnotexist', 'error');
-        }
-
-        if (get_string_manager()->string_exists('pluginname', "block_$block->name")) {
-            $strblockname = get_string('pluginname', "block_$block->name");
-        } else {
-            $strblockname = $block->name;
-        }
-
-        if (!$confirm) {
-            echo $OUTPUT->confirm(get_string('blockdeleteconfirm', '', $strblockname), 'blocks.php?delete='.$block->id.'&confirm=1', 'blocks.php');
-            echo $OUTPUT->footer();
-            exit;
-
-        } else {
-            uninstall_plugin('block', $block->name);
-
-            $a = new stdClass();
-            $a->block = $strblockname;
-            $a->directory = $CFG->dirroot.'/blocks/'.$block->name;
-            notice(get_string('blockdeletefiles', '', $a), 'blocks.php');
-        }
-    }
-
     echo $OUTPUT->header();
     echo $OUTPUT->heading($strmanageblocks);
 
 
     $table = new flexible_table('admin-blocks-compatible');
 
-    $table->define_columns(array('name', 'instances', 'version', 'hideshow', 'undeletable', 'delete', 'settings'));
-    $table->define_headers(array($strname, $strcourses, $strversion, $strhide.'/'.$strshow, $strprotecthdr, $strdelete, $strsettings));
+    $table->define_columns(array('name', 'instances', 'version', 'hideshow', 'undeletable', 'uninstall', 'settings'));
+    $table->define_headers(array($strname, $strcourses, $strversion, $strhide.'/'.$strshow, $strprotecthdr, $struninstall, $strsettings));
     $table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
     $table->set_attribute('class', 'admintable blockstable generaltable');
     $table->set_attribute('id', 'compatibleblockstable');
             }
         }
 
-        $delete = '<a href="blocks.php?delete='.$blockid.'&amp;sesskey='.sesskey().'">'.$strdelete.'</a>';
+        if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('block_'.$blockname)) {
+            $uninstall = html_writer::link($uninstallurl, $struninstall);
+        } else {
+            $uninstall = '';
+        }
 
         $settings = ''; // By default, no configuration
         if ($blockobject and $blockobject->has_config()) {
             '<span'.$class.'>'.$version.'</span>',
             $visible,
             $undeletable,
-            $delete,
+            $uninstall,
             $settings
         );
         $table->add_data($row);
 
         $table = new flexible_table('admin-blocks-incompatible');
 
-        $table->define_columns(array('block', 'delete'));
-        $table->define_headers(array($strname, $strdelete));
+        $table->define_columns(array('block', 'uninstall'));
+        $table->define_headers(array($strname, $struninstall));
         $table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/blocks.php');
 
         $table->set_attribute('class', 'incompatibleblockstable generaltable');
         $table->setup();
 
         foreach ($incompatible as $block) {
+            if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('block_'.$block->name)) {
+                $uninstall = html_writer::link($uninstallurl, $struninstall);
+            } else {
+                $uninstall = '';
+            }
             $table->add_data(array(
                 $block->name,
-                '<a href="blocks.php?delete='.$block->id.'&amp;sesskey='.sesskey().'">'.$strdelete.'</a>',
+                $uninstall,
             ));
         }
         $table->print_html();
index a84d556..684acb5 100644 (file)
@@ -58,7 +58,8 @@ Options:
 --dbname=NAME         Database name. Default is moodle
 --dbuser=USERNAME     Database user. Default is root
 --dbpass=PASSWORD     Database password. Default is blank
---dbsocket            Use database sockets. Available for some databases only.
+--dbport=NUMBER       Use database port.
+--dbsocket=PATH       Use database socket, 1 means default. Available for some databases only.
 --prefix=STRING       Table prefix for above database tables. Default is mdl_
 --fullname=STRING     The fullname of the site
 --shortname=STRING    The shortname of the site
@@ -130,6 +131,8 @@ define('MOODLE_INTERNAL', true);
 // Disables all caching.
 define('CACHE_DISABLE_ALL', true);
 
+define('PHPUNIT_TEST', false);
+
 // Check that PHP is of a sufficient version
 if (version_compare(phpversion(), "5.3.3") < 0) {
     $phpversion = phpversion();
@@ -150,6 +153,7 @@ $CFG->docroot              = 'http://docs.moodle.org';
 $CFG->running_installer    = true;
 $CFG->early_install_lang   = true;
 $CFG->ostype               = (stristr(PHP_OS, 'win') && !stristr(PHP_OS, 'darwin')) ? 'WINDOWS' : 'UNIX';
+$CFG->dboptions            = array();
 
 $parts = explode('/', str_replace('\\', '/', dirname(dirname(__FILE__))));
 $CFG->admin                = array_pop($parts);
@@ -158,6 +162,7 @@ $CFG->admin                = array_pop($parts);
 //the problem is that we need specific version of quickforms and hacked excel files :-(
 ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
 
+require_once($CFG->libdir.'/classes/component.php');
 require_once($CFG->libdir.'/installlib.php');
 require_once($CFG->libdir.'/clilib.php');
 require_once($CFG->libdir.'/setuplib.php');
@@ -204,7 +209,8 @@ list($options, $unrecognized) = cli_get_params(
         'dbname'            => 'moodle',
         'dbuser'            => empty($distro->dbuser) ? 'root' : $distro->dbuser, // let distros set dbuser
         'dbpass'            => '',
-        'dbsocket'          => false,
+        'dbport'            => '',
+        'dbsocket'          => '',
         'prefix'            => 'mdl_',
         'fullname'          => '',
         'shortname'         => '',
@@ -497,6 +503,34 @@ if ($interactive) {
     $CFG->prefix = $options['prefix'];
 }
 
+// ask for db port
+if ($interactive) {
+    cli_separator();
+    cli_heading(get_string('databaseport', 'install'));
+    $prompt = get_string('clitypevaluedefault', 'admin', $options['dbport']);
+    $CFG->dboptions['dbport'] = (int)cli_input($prompt, $options['dbport']);
+
+} else {
+    $CFG->dboptions['dbport'] = (int)$options['dbport'];
+}
+if ($CFG->dboptions['dbport'] <= 0) {
+    $CFG->dboptions['dbport'] = '';
+}
+
+// ask for db socket
+if ($CFG->ostype === 'WINDOWS') {
+    $CFG->dboptions['dbsocket'] = '';
+
+} else if ($interactive and empty($CFG->dboptions['dbport'])) {
+    cli_separator();
+    cli_heading(get_string('databasesocket', 'install'));
+    $prompt = get_string('clitypevaluedefault', 'admin', $options['dbsocket']);
+    $CFG->dboptions['dbsocket'] = cli_input($prompt, $options['dbsocket']);
+
+} else {
+    $CFG->dboptions['dbsocket'] = $options['dbsocket'];
+}
+
 // ask for db user
 if ($interactive) {
     cli_separator();
@@ -525,14 +559,14 @@ if ($interactive) {
 
         $CFG->dbpass = cli_input($prompt, $options['dbpass']);
         if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation
-            $distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']), $distro);
+            $distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']), $distro);
         }
-        $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
+        $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']));
     } while ($hint_database !== '');
 
 } else {
     $CFG->dbpass = $options['dbpass'];
-    $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbsocket'=>$options['dbsocket']));
+    $hint_database = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix, array('dbpersist'=>0, 'dbport'=>$CFG->dboptions['dbport'], 'dbsocket'=>$CFG->dboptions['dbsocket']));
     if ($hint_database !== '') {
         cli_error(get_string('dbconnectionerror', 'install'));
     }
index fddcdda..7ea15e8 100644 (file)
@@ -28,7 +28,6 @@ require_once($CFG->libdir.'/pluginlib.php');
 
 $action  = required_param('action', PARAM_ALPHANUMEXT);
 $formatname   = required_param('format', PARAM_PLUGIN);
-$confirm = optional_param('confirm', 0, PARAM_BOOL);
 
 $syscontext = context_system::instance();
 $PAGE->set_url('/admin/courseformats.php');
@@ -79,52 +78,5 @@ switch ($action) {
             set_config('format_plugins_sortorder', implode(',', $seq));
         }
         break;
-    case 'uninstall':
-        echo $OUTPUT->header();
-        echo $OUTPUT->heading(get_string('courseformats', 'moodle'));
-
-        $coursecount = $DB->count_records('course', array('format' => $formatname));
-        if ($coursecount) {
-            // Check that default format is set. It will be used to convert courses
-            // using this format
-            $defaultformat = get_config('moodlecourse', 'format');
-            $defaultformat = $formatplugins[get_config('moodlecourse', 'format')];
-            if (!$defaultformat) {
-                echo $OUTPUT->error_text(get_string('defaultformatnotset', 'admin'));
-                echo $OUTPUT->footer();
-                exit;
-            }
-        }
-
-        $format = $formatplugins[$formatname];
-        $deleteurl = $format->get_uninstall_url();
-        if (!$deleteurl) {
-            // somebody was trying to cheat and type non-existing link
-            echo $OUTPUT->error_text(get_string('cannotuninstall', 'admin', $format->displayname));
-            echo $OUTPUT->footer();
-            exit;
-        }
-
-        if (!$confirm) {
-            if ($coursecount) {
-                $message = get_string('formatuninstallwithcourses', 'admin',
-                        (object)array('count' => $coursecount, 'format' => $format->displayname,
-                            'defaultformat' => $defaultformat->displayname));
-            } else {
-                $message = get_string('formatuninstallconfirm', 'admin', $format->displayname);
-            }
-            $deleteurl->param('confirm', 1);
-            echo $OUTPUT->confirm($message, $deleteurl, $return);
-        } else {
-            $a = new stdClass();
-            $a->plugin = $format->displayname;
-            $a->directory = $format->rootdir;
-            uninstall_plugin('format', $formatname);
-            echo $OUTPUT->notification(get_string('formatuninstalled', 'admin', $a), 'notifysuccess');
-            echo $OUTPUT->continue_button($return);
-        }
-
-        echo $OUTPUT->footer();
-        exit;
 }
 redirect($return);
index cce8ae3..e411545 100644 (file)
@@ -72,24 +72,10 @@ if (!empty($CFG->cronremotepassword)) {
 }
 
 // send mime type and encoding
-if (check_browser_version('MSIE')) {
-    //ugly IE hack to work around downloading instead of viewing
-    @header('Content-Type: text/html; charset=utf-8');
-    echo "<xmp>"; //<pre> is not good enough for us here
-} else {
-    //send proper plaintext header
-    @header('Content-Type: text/plain; charset=utf-8');
-}
+@header('Content-Type: text/plain; charset=utf-8');
 
 // we do not want html markup in emulated CLI
 @ini_set('html_errors', 'off');
 
 // execute the cron
 cron_run();
-
-// finish the IE hack
-if (check_browser_version('MSIE')) {
-    echo "</xmp>";
-}
-
-
index f6e68ff..da846fa 100644 (file)
@@ -83,47 +83,6 @@ switch ($action) {
         }
         break;
 
-    case 'uninstall':
-        if ($editor === 'textarea') {
-            redirect($returnurl);
-        }
-        if (get_string_manager()->string_exists('pluginname', 'editor_'.$editor)) {
-            $strplugin = get_string('pluginname', 'editor_'.$editor);
-        } else {
-            $strplugin = $editor;
-        }
-
-        $PAGE->set_title($strplugin);
-        echo $OUTPUT->header();
-
-        if (!$confirm) {
-            echo $OUTPUT->heading(get_string('editors', 'core_editor'));
-
-            $deleteurl = new moodle_url('/admin/editors.php', array('action'=>'uninstall', 'editor'=>$editor, 'sesskey'=>sesskey(), 'confirm'=>1));
-
-            echo $OUTPUT->confirm(get_string('editordeleteconfirm', 'core_editor', $strplugin),
-                $deleteurl, $returnurl);
-            echo $OUTPUT->footer();
-            die();
-
-        } else {
-            // Remove from enabled list.
-            $key = array_search($editor, $active_editors);
-            unset($active_editors[$key]);
-            set_config('texteditors', implode(',', $active_editors));
-
-            // Delete everything!!
-            uninstall_plugin('editor', $editor);
-
-            $a = new stdClass();
-            $a->name = $strplugin;
-            $a->directory = "$CFG->dirroot/lib/editor/$editor";
-            echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
-            echo $OUTPUT->continue_button($returnurl);
-            echo $OUTPUT->footer();
-            die();
-        }
-
     default:
         break;
 }
index 10062a1..59f77c5 100644 (file)
@@ -31,7 +31,6 @@ require_once($CFG->libdir.'/adminlib.php');
 $action  = required_param('action', PARAM_ALPHANUMEXT);
 $enrol   = required_param('enrol', PARAM_PLUGIN);
 $confirm = optional_param('confirm', 0, PARAM_BOOL);
-$migrate = optional_param('migrate', 0, PARAM_BOOL);
 
 $PAGE->set_url('/admin/enrol.php');
 $PAGE->set_context(context_system::instance());
@@ -96,7 +95,7 @@ switch ($action) {
         set_config('enrol_plugins_enabled', implode(',', $enabled));
         break;
 
-    case 'uninstall':
+    case 'migrate':
         if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
             $strplugin = get_string('pluginname', 'enrol_'.$enrol);
         } else {
@@ -106,58 +105,26 @@ switch ($action) {
         $PAGE->set_title($strplugin);
         echo $OUTPUT->header();
 
-        if (!$confirm) {
-            echo $OUTPUT->heading(get_string('enrolments', 'enrol'));
+        // This may take a long time.
+        set_time_limit(0);
 
-            $deleteurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1, 'migrate'=>0));
-            $migrateurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1, 'migrate'=>1));
-
-            $migrate = new single_button($migrateurl, get_string('uninstallmigrate', 'enrol'));
-            $delete = new single_button($deleteurl, get_string('uninstalldelete', 'enrol'));
-            $cancel = new single_button($return, get_string('cancel'), 'get');
-
-            $buttons = $OUTPUT->render($delete) . $OUTPUT->render($cancel);
-            if ($enrol !== 'manual') {
-                $buttons = $OUTPUT->render($migrate) . $buttons;
-            }
-
-            echo $OUTPUT->box_start('generalbox', 'notice');
-            echo html_writer::tag('p', markdown_to_html(get_string('uninstallconfirm', 'enrol', $strplugin)));
-            echo html_writer::tag('div', $buttons, array('class' => 'buttons'));
-            echo $OUTPUT->box_end();
-
-            echo $OUTPUT->footer();
-            exit;
-
-        } else {
-            // This may take a long time.
-            set_time_limit(0);
-
-            // Disable plugin to prevent concurrent cron execution.
-            unset($enabled[$enrol]);
-            set_config('enrol_plugins_enabled', implode(',', array_keys($enabled)));
-
-            if ($migrate) {
-                echo $OUTPUT->heading(get_string('uninstallmigrating', 'enrol', 'enrol_'.$enrol));
+        // Disable plugin to prevent concurrent cron execution.
+        unset($enabled[$enrol]);
+        set_config('enrol_plugins_enabled', implode(',', array_keys($enabled)));
 
-                require_once("$CFG->dirroot/enrol/manual/locallib.php");
-                enrol_manual_migrate_plugin_enrolments($enrol);
+        echo $OUTPUT->heading(get_string('uninstallmigrating', 'enrol', 'enrol_'.$enrol));
 
-                echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
-            }
+        require_once("$CFG->dirroot/enrol/manual/locallib.php");
+        enrol_manual_migrate_plugin_enrolments($enrol);
 
-            // Delete everything!!
-            uninstall_plugin('enrol', $enrol);
-            $syscontext->mark_dirty(); // Resets all enrol caches.
+        echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
 
-            $a = new stdClass();
-            $a->plugin = $strplugin;
-            $a->directory = "$CFG->dirroot/enrol/$enrol";
-            echo $OUTPUT->notification(get_string('uninstalldeletefiles', 'enrol', $a), 'notifysuccess');
-            echo $OUTPUT->continue_button($return);
-            echo $OUTPUT->footer();
-            exit;
+        if (!$return = plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol)) {
+            $return = new moodle_url('/admin/plugins.php');
         }
+        echo $OUTPUT->continue_button($return);
+        echo $OUTPUT->footer();
+        exit;
 }
 
 
index 61af243..19997a4 100644 (file)
       </PHP_SETTING>
     </PHP_SETTINGS>
   </MOODLE>
+  <MOODLE version="2.6" requires="2.2">
+    <UNICODE level="required">
+      <FEEDBACK>
+        <ON_ERROR message="unicoderequired" />
+      </FEEDBACK>
+    </UNICODE>
+    <DATABASE level="required">
+      <VENDOR name="mysql" version="5.1.33" />
+      <VENDOR name="postgres" version="8.3" />
+      <VENDOR name="mssql" version="9.0" />
+      <VENDOR name="oracle" version="10.2" />
+    </DATABASE>
+    <PHP version="5.3.3" level="required">
+    </PHP>
+    <PCREUNICODE level="optional">
+      <FEEDBACK>
+        <ON_CHECK message="pcreunicodewarning" />
+      </FEEDBACK>
+    </PCREUNICODE>
+    <PHP_EXTENSIONS>
+      <PHP_EXTENSION name="iconv" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="iconvrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="mbstring" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="mbstringrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="curl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="curlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="openssl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="opensslrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="tokenizer" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="tokenizerrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xmlrpc" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="xmlrpcrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="soap" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="soaprecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="ctype" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ctyperequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="zip" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="ziprequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="gd" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="gdrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="simplexml" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="simplexmlrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="spl" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="splrequired" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="pcre" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="dom" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="xml" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="intl" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="intlrecommended" />
+        </FEEDBACK>
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="json" level="required">
+      </PHP_EXTENSION>
+      <PHP_EXTENSION name="hash" level="required"/>
+    </PHP_EXTENSIONS>
+    <PHP_SETTINGS>
+      <PHP_SETTING name="memory_limit" value="64M" level="required">
+        <FEEDBACK>
+          <ON_ERROR message="settingmemorylimit" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="safe_mode" value="0" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingsafemode" />
+        </FEEDBACK>
+      </PHP_SETTING>
+      <PHP_SETTING name="file_uploads" value="1" level="optional">
+        <FEEDBACK>
+          <ON_CHECK message="settingfileuploads" />
+        </FEEDBACK>
+      </PHP_SETTING>
+    </PHP_SETTINGS>
+  </MOODLE>
 </COMPATIBILITY_MATRIX>
index cd2f26e..782f850 100644 (file)
@@ -33,48 +33,14 @@ require_once($CFG->libdir.'/tablelib.php');
 
 admin_externalpage_setup('managelocalplugins');
 
-$delete  = optional_param('delete', '', PARAM_PLUGIN);
-$confirm = optional_param('confirm', '', PARAM_BOOL);
-
-/// If data submitted, then process and store.
-
-if (!empty($delete) and confirm_sesskey()) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('localplugins'));
-
-    if (!$confirm) {
-        if (get_string_manager()->string_exists('pluginname', 'local_' . $delete)) {
-            $strpluginname = get_string('pluginname', 'local_' . $delete);
-        } else {
-            $strpluginname = $delete;
-        }
-        echo $OUTPUT->confirm(get_string('localplugindeleteconfirm', '', $strpluginname),
-                                new moodle_url($PAGE->url, array('delete' => $delete, 'confirm' => 1)),
-                                $PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-
-    } else {
-        uninstall_plugin('local', $delete);
-        $a = new stdclass();
-        $a->name = $delete;
-        $pluginlocation = get_plugin_types();
-        $a->directory = $pluginlocation['local'] . '/' . $delete;
-        echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
-        echo $OUTPUT->continue_button($PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-    }
-}
-
 echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('localplugins'));
 
 /// Print the table of all installed local plugins
 
 $table = new flexible_table('localplugins_administration_table');
-$table->define_columns(array('name', 'version', 'delete'));
-$table->define_headers(array(get_string('plugin'), get_string('version'), get_string('delete')));
+$table->define_columns(array('name', 'version', 'uninstall'));
+$table->define_headers(array(get_string('plugin'), get_string('version'), get_string('uninstallplugin', 'core_admin')));
 $table->define_baseurl($PAGE->url);
 $table->set_attribute('id', 'localplugins');
 $table->set_attribute('class', 'admintable generaltable');
@@ -92,8 +58,10 @@ foreach (get_plugin_list('local') as $plugin => $plugindir) {
 collatorlib::asort($plugins);
 
 foreach ($plugins as $plugin => $name) {
-    $delete = new moodle_url($PAGE->url, array('delete' => $plugin, 'sesskey' => sesskey()));
-    $delete = html_writer::link($delete, get_string('delete'));
+    $uninstall = '';
+    if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('local_'.$plugin)) {
+        $uninstall = html_writer::link($uninstall, get_string('uninstallplugin', 'core_admin'));
+    }
 
     $version = get_config('local_' . $plugin);
     if (!empty($version->version)) {
@@ -102,7 +70,7 @@ foreach ($plugins as $plugin => $name) {
         $version = '?';
     }
 
-    $table->add_data(array($name, $version, $delete));
+    $table->add_data(array($name, $version, $uninstall));
 }
 
 $table->print_html();
index 33d2fbb..8eab4bc 100644 (file)
 
     $show    = optional_param('show', '', PARAM_PLUGIN);
     $hide    = optional_param('hide', '', PARAM_PLUGIN);
-    $delete  = optional_param('delete', '', PARAM_PLUGIN);
-    $confirm = optional_param('confirm', '', PARAM_BOOL);
 
 
 /// Print headings
 
     $stractivities = get_string("activities");
-    $strdelete = get_string("delete");
+    $struninstall = get_string('uninstallplugin', 'core_admin');
     $strversion = get_string("version");
     $strhide = get_string("hide");
     $strshow = get_string("show");
@@ -46,7 +44,7 @@
                  WHERE module=?";
         $DB->execute($sql, array($module->id));
         // clear the course modinfo cache for courses
-        // where we just deleted something
+        // where we just uninstalld something
         $sql = "UPDATE {course}
                    SET modinfo=''
                  WHERE id IN (SELECT DISTINCT course
         admin_get_root(true, false);  // settings not required - only pages
     }
 
-    if (!empty($delete) and confirm_sesskey()) {
-        echo $OUTPUT->header();
-        echo $OUTPUT->heading($stractivities);
-
-        if (get_string_manager()->string_exists('modulename', $delete)) {
-            $strmodulename = get_string('modulename', $delete);
-        } else {
-            $strmodulename = $delete;
-        }
-
-        if (!$confirm) {
-            echo $OUTPUT->confirm(get_string("moduledeleteconfirm", "", $strmodulename), "modules.php?delete=$delete&confirm=1", "modules.php");
-            echo $OUTPUT->footer();
-            exit;
-
-        } else {  // Delete everything!!
-
-            if ($delete == "forum") {
-                print_error("cannotdeleteforummodule", 'forum');
-            }
-
-            uninstall_plugin('mod', $delete);
-            $a = new stdClass();
-            $a->module = $strmodulename;
-            $a->directory = "$CFG->dirroot/mod/$delete";
-            echo $OUTPUT->notification(get_string("moduledeletefiles", "", $a), 'notifysuccess');
-            echo $OUTPUT->continue_button("modules.php");
-            echo $OUTPUT->footer();
-            exit;
-        }
-    }
-
     echo $OUTPUT->header();
     echo $OUTPUT->heading($stractivities);
 
 /// Print the table of all modules
     // construct the flexible table ready to display
     $table = new flexible_table(MODULE_TABLE);
-    $table->define_columns(array('name', 'instances', 'version', 'hideshow', 'delete', 'settings'));
-    $table->define_headers(array($stractivitymodule, $stractivities, $strversion, "$strhide/$strshow", $strdelete, $strsettings));
+    $table->define_columns(array('name', 'instances', 'version', 'hideshow', 'uninstall', 'settings'));
+    $table->define_headers(array($stractivitymodule, $stractivities, $strversion, "$strhide/$strshow", $struninstall, $strsettings));
     $table->define_baseurl($CFG->wwwroot.'/'.$CFG->admin.'/modules.php');
     $table->set_attribute('id', 'modules');
     $table->set_attribute('class', 'generaltable');
             $missing = false;
         }
 
-        $delete = "<a href=\"modules.php?delete=$module->name&amp;sesskey=".sesskey()."\">$strdelete</a>";
+        $uninstall = '';
+        if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('mod_'.$module->name)) {
+            $uninstall = html_writer::link($uninstallurl, $struninstall);
+        }
 
         if (file_exists("$CFG->dirroot/mod/$module->name/settings.php") ||
                 file_exists("$CFG->dirroot/mod/$module->name/settingstree.php")) {
             $class =   ' class="dimmed_text"';
         }
         if ($module->name == "forum") {
-            $delete = "";
+            $uninstall = "";
             $visible = "";
             $class = "";
         }
             $countlink,
             '<span'.$class.'>'.$module->version.'</span>',
             $visible,
-            $delete,
+            $uninstall,
             $settings
         ));
     }
index 40ed3ae..db2cd2f 100644 (file)
@@ -18,7 +18,7 @@
  * Provides an overview of installed plagiarism plugins
  *
  * Displays the list of found plagiarism plugins, their version (if found) and
- * a link to delete the plagiarism plugin.
+ * a link to uninstall the plagiarism plugin.
  *
  * @see       http://docs.moodle.org/dev/Plagiarism_API
  * @package   admin
@@ -32,43 +32,12 @@ require_once($CFG->libdir.'/tablelib.php');
 
 admin_externalpage_setup('manageplagiarismplugins');
 
-$delete  = optional_param('delete', '', PARAM_PLUGIN);
-$confirm = optional_param('confirm', false, PARAM_BOOL);
-
-if (!empty($delete) and confirm_sesskey()) { // If data submitted, then process and store.
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('manageplagiarism', 'plagiarism'));
-
-    if (!$confirm) {
-        if (get_string_manager()->string_exists('pluginname', 'plagiarism_' . $delete)) {
-            $strpluginname = get_string('pluginname', 'plagiarism_' . $delete);
-        } else {
-            $strpluginname = $delete;
-        }
-        echo $OUTPUT->confirm(get_string('plagiarismplugindeleteconfirm', 'plagiarism', $strpluginname),
-            new moodle_url($PAGE->url, array('delete' => $delete, 'confirm' => 1)),
-            $PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-
-    } else {
-        uninstall_plugin('plagiarism', $delete);
-        $a = new stdclass();
-        $a->name = $delete;
-        $pluginlocation = get_plugin_types();
-        $a->directory = $pluginlocation['plagiarism'] . '/' . $delete;
-        echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
-        echo $OUTPUT->continue_button($PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-    }
-}
-
 echo $OUTPUT->header();
 
 // Print the table of all installed plagiarism plugins.
 
-$txt = get_strings(array('settings', 'name', 'version', 'delete'));
+$txt = get_strings(array('settings', 'name', 'version'));
+$txt->uninstall = get_string('uninstallplugin', 'core_admin');
 
 $plagiarismplugins = get_plugin_list('plagiarism');
 if (empty($plagiarismplugins)) {
@@ -81,7 +50,7 @@ echo $OUTPUT->heading(get_string('availableplugins', 'plagiarism'), 3, 'main');
 echo $OUTPUT->box_start('generalbox authsui');
 
 $table = new html_table();
-$table->head  = array($txt->name, $txt->version, $txt->delete, $txt->settings);
+$table->head  = array($txt->name, $txt->version, $txt->uninstall, $txt->settings);
 $table->colclasses = array('mdl-left', 'mdl-align', 'mdl-align', 'mdl-align');
 $table->data  = array();
 $table->attributes['class'] = 'manageplagiarismtable generaltable';
@@ -101,10 +70,12 @@ foreach ($plagiarismplugins as $plugin => $dir) {
         } else {
             $version = '?';
         }
-        // Delete link.
-        $delete = new moodle_url($PAGE->url, array('delete' => $plugin, 'sesskey' => sesskey()));
-        $delete = html_writer::link($delete, get_string('delete'));
-        $table->data[] = array($displayname, $version, $delete, $settings);
+        // uninstall link.
+        $uninstall = '';
+        if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('plagiarism_'.$plugin)) {
+            $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
+        }
+        $table->data[] = array($displayname, $version, $uninstall, $settings);
     }
 }
 echo html_writer::table($table);
index 08157df..280c0ae 100644 (file)
@@ -131,7 +131,6 @@ if ($delete and $confirmed) {
 
     // So long, and thanks for all the bugs.
     fulldelete($pluginfo->rootdir);
-    cache::make('core', 'pluginlist')->purge();
     redirect($PAGE->url);
 }
 
index e708152..68cdb49 100644 (file)
@@ -375,7 +375,7 @@ class core_admin_renderer extends plugin_renderer_base {
      * Display a page to confirm the plugin uninstallation.
      *
      * @param plugin_manager $pluginman
-     * @param plugin_info $pluginfo
+     * @param plugininfo_base $pluginfo
      * @param moodle_url $continueurl URL to continue after confirmation
      * @return string
      */
@@ -384,10 +384,14 @@ class core_admin_renderer extends plugin_renderer_base {
 
         $pluginname = $pluginman->plugin_name($pluginfo->component);
 
+        $confirm = '<p>' . get_string('uninstallconfirm', 'core_plugin', array('name' => $pluginname)) . '</p>';
+        if ($extraconfirm = $pluginfo->get_uninstall_extra_warning()) {
+            $confirm .= $extraconfirm;
+        }
+
         $output .= $this->output->header();
         $output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
-        $output .= $this->output->confirm(get_string('uninstallconfirm', 'core_plugin', array('name' => $pluginname)),
-            $continueurl, $this->page->url);
+        $output .= $this->output->confirm($confirm, $continueurl, $this->page->url);
         $output .= $this->output->footer();
 
         return $output;
@@ -397,7 +401,7 @@ class core_admin_renderer extends plugin_renderer_base {
      * Display a page with results of plugin uninstallation and offer removal of plugin files.
      *
      * @param plugin_manager $pluginman
-     * @param plugin_info $pluginfo
+     * @param plugininfo_base $pluginfo
      * @param progress_trace_buffer $progress
      * @param moodle_url $continueurl URL to continue to remove the plugin folder
      * @return string
@@ -431,7 +435,7 @@ class core_admin_renderer extends plugin_renderer_base {
      * Display a page with results of plugin uninstallation and inform about the need to remove plugin files manually.
      *
      * @param plugin_manager $pluginman
-     * @param plugin_info $pluginfo
+     * @param plugininfo_base $pluginfo
      * @param progress_trace_buffer $progress
      * @return string
      */
index e03c928..19fafdb 100644 (file)
@@ -18,7 +18,7 @@
  * Provides an overview of installed reports
  *
  * Displays the list of found reports, their version (if found) and
- * a link to delete the report.
+ * a link to uninstall the report.
  *
  * The code is based on admin/localplugins.php by David Mudrak.
  *
@@ -33,48 +33,16 @@ require_once($CFG->libdir.'/tablelib.php');
 
 admin_externalpage_setup('managereports');
 
-$delete  = optional_param('delete', '', PARAM_PLUGIN);
-$confirm = optional_param('confirm', '', PARAM_BOOL);
-
-/// If data submitted, then process and store.
-
-if (!empty($delete) and confirm_sesskey()) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('reports'));
-
-    if (!$confirm) {
-        if (get_string_manager()->string_exists('pluginname', 'report_' . $delete)) {
-            $strpluginname = get_string('pluginname', 'report_' . $delete);
-        } else {
-            $strpluginname = $delete;
-        }
-        echo $OUTPUT->confirm(get_string('reportsdeleteconfirm', 'admin', $strpluginname),
-                                new moodle_url($PAGE->url, array('delete' => $delete, 'confirm' => 1)),
-                                $PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-
-    } else {
-        uninstall_plugin('report', $delete);
-        $a = new stdclass();
-        $a->name = $delete;
-        $pluginlocation = get_plugin_types();
-        $a->directory = $pluginlocation['report'] . '/' . $delete;
-        echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
-        echo $OUTPUT->continue_button($PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-    }
-}
-
 echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('reports'));
 
 /// Print the table of all installed report plugins
 
+$struninstall = get_string('uninstallplugin', 'core_admin');
+
 $table = new flexible_table('reportplugins_administration_table');
-$table->define_columns(array('name', 'version', 'delete'));
-$table->define_headers(array(get_string('plugin'), get_string('version'), get_string('delete')));
+$table->define_columns(array('name', 'version', 'uninstall'));
+$table->define_headers(array(get_string('plugin'), get_string('version'), $struninstall));
 $table->define_baseurl($PAGE->url);
 $table->set_attribute('id', 'reportplugins');
 $table->set_attribute('class', 'generaltable generalbox boxaligncenter boxwidthwide');
@@ -104,8 +72,10 @@ foreach ($installed as $config) {
 }
 
 foreach ($plugins as $plugin => $name) {
-    $delete = new moodle_url($PAGE->url, array('delete' => $plugin, 'sesskey' => sesskey()));
-    $delete = html_writer::link($delete, get_string('delete'));
+    $uninstall = '';
+    if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('report_'.$plugin)) {
+        $uninstall = html_writer::link($uninstallurl, $struninstall);
+    }
 
     if (!isset($versions[$plugin])) {
         if (file_exists("$CFG->dirroot/report/$plugin/version.php")) {
@@ -126,7 +96,7 @@ foreach ($plugins as $plugin => $name) {
         }
     }
 
-    $table->add_data(array($name, $version, $delete));
+    $table->add_data(array($name, $version, $uninstall));
 }
 
 $table->print_html();
diff --git a/admin/roles/classes/preset.php b/admin/roles/classes/preset.php
new file mode 100644 (file)
index 0000000..85904eb
--- /dev/null
@@ -0,0 +1,314 @@
+<?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/>.
+
+/**
+ * New role XML processing.
+ *
+ * @package    core_role
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * XML role file manipulation class.
+ *
+ * @package    core_role
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_role_preset {
+
+    /**
+     * Send role export xml file to browser.
+     *
+     * @param int $roleid
+     * @return void does not return, send the file to output
+     */
+    public static function send_export_xml($roleid) {
+        global $CFG, $DB;
+        require_once($CFG->libdir . '/filelib.php');
+
+        $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
+
+        if ($role->shortname) {
+            $filename = $role->shortname.'.xml';
+        } else {
+            $filename = 'role.xml';
+        }
+        $xml = self::get_export_xml($roleid);
+        send_file($xml, $filename, 0, false, true, true);
+        die();
+    }
+
+    /**
+     * Generate role export xml file.
+     *
+     * @param $roleid
+     * @return string
+     */
+    public static function get_export_xml($roleid) {
+        global $DB;
+
+        $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
+
+        $dom = new DOMDocument('1.0', 'UTF-8');
+        $top = $dom->createElement('role');
+        $dom->appendChild($top);
+
+        $top->appendChild($dom->createElement('shortname', $role->shortname));
+        $top->appendChild($dom->createElement('name', $role->name));
+        $top->appendChild($dom->createElement('description', $role->description));
+        $top->appendChild($dom->createElement('archetype', $role->archetype));
+
+        $contextlevels = $dom->createElement('contextlevels');
+        $top->appendChild($contextlevels);
+        foreach (get_role_contextlevels($roleid) as $level) {
+            $name = context_helper::get_class_for_level($level);
+            $name = preg_replace('/^context_/', '', $name);
+            $contextlevels->appendChild($dom->createElement('level', $name));
+        }
+
+        foreach (array('assign', 'override', 'switch') as $type) {
+            $allows = $dom->createElement('allow'.$type);
+            $top->appendChild($allows);
+            $records = $DB->get_records('role_allow_'.$type, array('roleid'=>$roleid), "allow$type ASC");
+            foreach ($records as $record) {
+                if (!$ar = $DB->get_record('role', array('id'=>$record->{'allow'.$type}))) {
+                    continue;
+                }
+                $allows->appendChild($dom->createElement('shortname', $ar->shortname));
+            }
+        }
+
+        $permissions = $dom->createElement('permissions');
+        $top->appendChild($permissions);
+
+        $capabilities = $DB->get_records_sql_menu(
+            "SELECT capability, permission
+               FROM {role_capabilities}
+              WHERE contextid = :syscontext AND roleid = :roleid
+           ORDER BY capability ASC",
+            array('syscontext'=>context_system::instance()->id, 'roleid'=>$roleid));
+
+        $allcapabilities = $DB->get_records('capabilities', array(), 'name ASC');
+        foreach ($allcapabilities as $cap) {
+            if (!isset($capabilities[$cap->name])) {
+                $permissions->appendChild($dom->createElement('inherit', $cap->name));
+            }
+        }
+
+        foreach ($capabilities as $capability => $permission) {
+            if ($permission == CAP_ALLOW) {
+                $permissions->appendChild($dom->createElement('allow', $capability));
+            }
+        }
+        foreach ($capabilities as $capability => $permission) {
+            if ($permission == CAP_PREVENT) {
+                $permissions->appendChild($dom->createElement('prevent', $capability));
+            }
+        }
+        foreach ($capabilities as $capability => $permission) {
+            if ($permission == CAP_PROHIBIT) {
+                $permissions->appendChild($dom->createElement('prohibit', $capability));
+            }
+        }
+
+        return $dom->saveXML();
+    }
+
+    /**
+     * Is this XML valid role preset?
+     *
+     * @param string $xml
+     * @return bool
+     */
+    public static function is_valid_preset($xml) {
+        $dom = new DOMDocument();
+        if (!$dom->loadXML($xml)) {
+            return false;
+        } else {
+            $val = @$dom->schemaValidate(__DIR__.'/../role_schema.xml');
+            if (!$val) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Parse role preset xml file.
+     *
+     * @param string $xml
+     * @return array role info, null on error
+     */
+    public static function parse_preset($xml) {
+        global $DB;
+
+        $info = array();
+
+        if (!self::is_valid_preset($xml)) {
+            return null;
+        }
+
+        $dom = new DOMDocument();
+        $dom->loadXML($xml);
+
+        $info['shortname'] = self::get_node_value($dom, '/role/shortname');
+        if (isset($info['shortname'])) {
+            $info['shortname'] = strtolower(clean_param($info['shortname'], PARAM_ALPHANUMEXT));
+        }
+
+        $info['name'] = self::get_node_value($dom, '/role/name');
+        if (isset($value)) {
+            $info['name'] = clean_param($info['name'], PARAM_TEXT);
+        }
+
+        $info['description'] = self::get_node_value($dom, '/role/description');
+        if (isset($value)) {
+            $info['description'] = clean_param($info['description'], PARAM_CLEANHTML);
+        }
+
+        $info['archetype'] = self::get_node_value($dom, '/role/archetype');
+        if (isset($value)) {
+            $archetypes = get_role_archetypes();
+            if (!isset($archetypes[$info['archetype']])) {
+                $info['archetype'] = null;
+            }
+        }
+
+        $values = self::get_node_children_values($dom, '/role/contextlevels', 'level');
+        if (isset($values)) {
+            $info['contextlevels'] = array();
+            $levelmap = array_flip(context_helper::get_all_levels());
+            foreach ($values as $value) {
+                $level = 'context_'.$value;
+                if (isset($levelmap[$level])) {
+                    $cl = $levelmap[$level];
+                    $info['contextlevels'][$cl] = $cl;
+                }
+            }
+        }
+
+        foreach (array('assign', 'override', 'switch') as $type) {
+            $values = self::get_node_children_values($dom, '/role/allow'.$type, 'shortname');
+            if (!isset($values)) {
+                $info['allow'.$type] = null;
+                continue;
+            }
+            $info['allow'.$type] = array();
+            foreach ($values as $value) {
+                if ($value === $info['shortname']) {
+                    array_unshift($info['allow'.$type], -1); // Means self.
+                }
+                if ($role = $DB->get_record('role', array('shortname'=>$value))) {
+                    $info['allow'.$type][] = $role->id;
+                    continue;
+                }
+            }
+        }
+
+        $info['permissions'] = array();
+        $values = self::get_node_children_values($dom, '/role/permissions', 'inherit');
+        if (isset($values)) {
+            foreach ($values as $value) {
+                if ($value = clean_param($value, PARAM_CAPABILITY)) {
+                    $info['permissions'][$value] = CAP_INHERIT;
+                }
+            }
+        }
+        $values = self::get_node_children_values($dom, '/role/permissions', 'allow');
+        if (isset($values)) {
+            foreach ($values as $value) {
+                if ($value = clean_param($value, PARAM_CAPABILITY)) {
+                    $info['permissions'][$value] = CAP_ALLOW;
+                }
+            }
+        }
+        $values = self::get_node_children_values($dom, '/role/permissions', 'prevent');
+        if (isset($values)) {
+            foreach ($values as $value) {
+                if ($value = clean_param($value, PARAM_CAPABILITY)) {
+                    $info['permissions'][$value] = CAP_PREVENT;
+                }
+            }
+        }
+        $values = self::get_node_children_values($dom, '/role/permissions', 'prohibit');
+        if (isset($values)) {
+            foreach ($values as $value) {
+                if ($value = clean_param($value, PARAM_CAPABILITY)) {
+                    $info['permissions'][$value] = CAP_PROHIBIT;
+                }
+            }
+        }
+
+        return $info;
+    }
+
+    protected static function get_node(DOMDocument $dom, $path) {
+        $parts = explode('/', $path);
+        $elname = end($parts);
+
+        $nodes = $dom->getElementsByTagName($elname);
+
+        if ($nodes->length == 0) {
+            return null;
+        }
+
+        foreach ($nodes as $node) {
+            if ($node->getNodePath() === $path) {
+                return $node;
+            }
+        }
+
+        return null;
+    }
+
+    protected static function get_node_value(DOMDocument $dom, $path) {
+        if (!$node = self::get_node($dom, $path)) {
+            return null;
+        }
+        return $node->nodeValue;
+    }
+
+    protected static function get_node_children(DOMDocument $dom, $path, $tagname) {
+        if (!$node = self::get_node($dom, $path)) {
+            return null;
+        }
+
+        $return = array();
+        foreach ($node->childNodes as $child) {
+            if ($child->nodeName === $tagname) {
+                $return[] = $child;
+            }
+        }
+        return $return;
+    }
+
+    protected static function get_node_children_values(DOMDocument $dom, $path, $tagname) {
+        $children = self::get_node_children($dom, $path, $tagname);
+
+        if ($children === null) {
+            return null;
+        }
+        $return = array();
+        foreach ($children as $child) {
+            $return[] = $child->nodeValue;
+        }
+        return $return;
+    }
+}
diff --git a/admin/roles/classes/preset_form.php b/admin/roles/classes/preset_form.php
new file mode 100644 (file)
index 0000000..ff2024d
--- /dev/null
@@ -0,0 +1,120 @@
+<?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/>.
+
+/**
+ * Role add/reset selection form.
+ *
+ * @package    core_role
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once("$CFG->libdir/formslib.php");
+
+
+/**
+ * Role add/reset selection form.
+ *
+ * @package    core_role
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_role_preset_form extends moodleform {
+
+    /**
+     * Definition of this form.
+     */
+    protected function definition() {
+        $mform = $this->_form;
+
+        $data = $this->_customdata;
+        $options = array();
+
+        $group = get_string('other');
+        $options[$group] = array();
+        $options[$group][0] = get_string('norole', 'core_role');
+
+        $group = get_string('role', 'core');
+        $options[$group] = array();
+        foreach (role_get_names(null, ROLENAME_BOTH) as $role) {
+            // Allow reset to self too, it may be useful when importing incomplete XML preset.
+            $options[$group][$role->id] = $role->localname;
+        }
+
+        $group = get_string('archetype', 'core_role');
+        $options[$group] = array();
+        foreach (get_role_archetypes() as $type) {
+            $options[$group][$type] = get_string('archetype'.$type, 'core_role');
+        }
+
+        $mform->addElement('header', 'presetheader', get_string('roleresetdefaults', 'core_role'));
+
+        $mform->addElement('selectgroups', 'resettype', get_string('roleresetrole', 'core_role'), $options);
+
+        $mform->addElement('filepicker', 'rolepreset', get_string('rolerepreset', 'core_role'));
+
+        if ($data['roleid']) {
+            $mform->addElement('header', 'resetheader', get_string('resetrole', 'core_role'));
+
+            $mform->addElement('advcheckbox', 'shortname', get_string('roleshortname', 'core_role'));
+            $mform->addElement('advcheckbox', 'name', get_string('customrolename', 'core_role'));
+            $mform->addElement('advcheckbox', 'description', get_string('customroledescription', 'core_role'));
+            $mform->addElement('advcheckbox', 'archetype', get_string('archetype', 'core_role'));
+            $mform->addElement('advcheckbox', 'contextlevels', get_string('maybeassignedin', 'core_role'));
+            $mform->addElement('advcheckbox', 'allowassign', get_string('allowassign', 'core_role'));
+            $mform->addElement('advcheckbox', 'allowoverride', get_string('allowoverride', 'core_role'));
+            $mform->addElement('advcheckbox', 'allowswitch', get_string('allowswitch', 'core_role'));
+            $mform->addElement('advcheckbox', 'permissions', get_string('permissions', 'core_role'));
+        }
+
+        $mform->addElement('hidden', 'roleid');
+        $mform->setType('roleid', PARAM_INT);
+
+        $mform->addElement('hidden', 'action');
+        $mform->setType('action', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'return');
+        $mform->setType('return', PARAM_ALPHA);
+
+        $this->add_action_buttons(true, get_string('continue', 'core'));
+
+        $this->set_data($data);
+    }
+
+    /**
+     * Validate this form.
+     *
+     * @param array $data submitted data
+     * @param array $files not used
+     * @return array errors
+     */
+    public function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        if ($files = $this->get_draft_files('rolepreset')) {
+            /** @var stored_file $file */
+            $file = reset($files);
+            $xml = $file->get_content();
+            if (!core_role_preset::is_valid_preset($xml)) {
+                $errors['rolepreset'] = get_string('invalidpresetfile', 'core_role');
+            }
+        }
+
+        return $errors;
+    }
+}
index d889a10..1012c48 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
  * Lets the user edit role definitions.
  *
  * Responds to actions:
- *   add       - add a new role
- *   duplicate - like add, only initialise the new role by using an existing one.
+ *   add       - add a new role (allows import, duplicate, archetype)
+ *   export    - save xml role definition
  *   edit      - edit the definition of a role
  *   view      - view the definition of a role
  *
- * @package    core
- * @subpackage role
+ * @package    core_role
  * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -34,7 +32,7 @@
     require_once($CFG->dirroot . '/' . $CFG->admin . '/roles/lib.php');
 
     $action = required_param('action', PARAM_ALPHA);
-    if (!in_array($action, array('add', 'duplicate', 'edit', 'view'))) {
+    if (!in_array($action, array('add', 'export', 'edit', 'reset', 'view'))) {
         throw new moodle_exception('invalidaccess');
     }
     if ($action != 'add') {
     } else {
         $roleid = 0;
     }
+    $resettype = optional_param('resettype', '', PARAM_RAW);
+    $return = optional_param('return', 'manage', PARAM_ALPHA);
 
 /// Get the base URL for this and related pages into a convenient variable.
-    $manageurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/manage.php';
-    $defineurl = $CFG->wwwroot . '/' . $CFG->admin . '/roles/define.php';
-    if ($action == 'duplicate') {
-        $baseurl = $defineurl . '?action=add';
+    $baseurl = new moodle_url('/admin/roles/define.php', array('action'=>$action, 'roleid'=>$roleid));
+    $manageurl = new moodle_url('/admin/roles/manage.php');
+    if ($return === 'manage') {
+        $returnurl = $manageurl;
     } else {
-        $baseurl = $defineurl . '?action=' . $action;
-        if ($roleid) {
-            $baseurl .= '&amp;roleid=' . $roleid;
-        }
+        $returnurl = new moodle_url('/admin/roles/define.php', array('action'=>'view', 'roleid'=>$roleid));;
     }
 
 /// Check access permissions.
     $systemcontext = context_system::instance();
     require_login();
     require_capability('moodle/role:manage', $systemcontext);
-    admin_externalpage_setup('defineroles', '', array('action' => $action, 'roleid' => $roleid), $defineurl);
+    admin_externalpage_setup('defineroles', '', array('action' => $action, 'roleid' => $roleid), new moodle_url('/admin/roles/define.php'));
 
-/// Handle the cancel button.
-    if (optional_param('cancel', false, PARAM_BOOL)) {
-        redirect($manageurl);
+/// Export role.
+    if ($action === 'export') {
+        core_role_preset::send_export_xml($roleid);
+        die;
     }
 
 /// Handle the toggle advanced mode button.
     $rolenames = role_fix_names($roles, $systemcontext, ROLENAME_ORIGINAL);
     $rolescount = count($roles);
 
-/// Create the table object.
-    if ($action == 'view') {
-        $definitiontable = new view_role_definition_table($systemcontext, $roleid);
-    } else if ($showadvanced) {
-        $definitiontable = new define_role_table_advanced($systemcontext, $roleid);
+    if ($action == 'add') {
+        $title = get_string('addinganewrole', 'role');
+    } else if ($action == 'view') {
+        $title = get_string('viewingdefinitionofrolex', 'role', $rolenames[$roleid]->localname);
+    } else if ($action == 'reset') {
+        $title = get_string('resettingrole', 'role', $rolenames[$roleid]->localname);
+    } else {
+        $title = get_string('editingrolex', 'role', $rolenames[$roleid]->localname);
+    }
+
+/// Decide how to create new role.
+    if ($action === 'add' and $resettype !== 'none') {
+        $mform = new core_role_preset_form(null, array('action'=>'add', 'roleid'=>0, 'resettype'=>'0', 'return'=>'manage'));
+        if ($mform->is_cancelled()) {
+            redirect($manageurl);
+
+        } else if ($data = $mform->get_data()) {
+            $resettype = $data->resettype;
+            $options = array(
+                'shortname'     => 1,
+                'name'          => 1,
+                'description'   => 1,
+                'permissions'   => 1,
+                'archetype'     => 1,
+                'contextlevels' => 1,
+                'allowassign'   => 1,
+                'allowoverride' => 1,
+                'allowswitch'   => 1);
+            if ($showadvanced) {
+                $definitiontable = new define_role_table_advanced($systemcontext, 0);
+            } else {
+                $definitiontable = new define_role_table_basic($systemcontext, 0);
+            }
+            if (is_number($resettype)) {
+                // Duplicate the role.
+                $definitiontable->force_duplicate($resettype, $options);
+            } else {
+                // Must be an archetype.
+                $definitiontable->force_archetype($resettype, $options);
+            }
+
+            if ($xml = $mform->get_file_content('rolepreset')) {
+                $definitiontable->force_preset($xml, $options);
+            }
+
+        } else {
+            echo $OUTPUT->header();
+            echo $OUTPUT->heading_with_help($title, 'roles', 'role');
+            $mform->display();
+            echo $OUTPUT->footer();
+            die;
+        }
+
+    } else if ($action === 'reset' and $resettype !== 'none') {
+        if (!$role = $DB->get_record('role', array('id'=>$roleid))) {
+            redirect($manageurl);
+        }
+        $resettype = empty($role->archetype) ? '0' : $role->archetype;
+        $mform = new core_role_preset_form(null,
+            array('action'=>'reset', 'roleid'=>$roleid, 'resettype'=>$resettype , 'permissions'=>1, 'archetype'=>1, 'contextlevels'=>1, 'return'=>$return));
+        if ($mform->is_cancelled()) {
+            redirect($returnurl);
+
+        } else if ($data = $mform->get_data()) {
+            $resettype = $data->resettype;
+            $options = array(
+                'shortname'     => $data->shortname,
+                'name'          => $data->name,
+                'description'   => $data->description,
+                'permissions'   => $data->permissions,
+                'archetype'     => $data->archetype,
+                'contextlevels' => $data->contextlevels,
+                'allowassign'   => $data->allowassign,
+                'allowoverride' => $data->allowoverride,
+                'allowswitch'   => $data->allowswitch);
+            if ($showadvanced) {
+                $definitiontable = new define_role_table_advanced($systemcontext, $roleid);
+            } else {
+                $definitiontable = new define_role_table_basic($systemcontext, $roleid);
+            }
+            if (is_number($resettype)) {
+                // Duplicate the role.
+                $definitiontable->force_duplicate($resettype, $options);
+            } else {
+                // Must be an archetype.
+                $definitiontable->force_archetype($resettype, $options);
+            }
+
+            if ($xml = $mform->get_file_content('rolepreset')) {
+                $definitiontable->force_preset($xml, $options);
+            }
+
+        } else {
+            echo $OUTPUT->header();
+            echo $OUTPUT->heading_with_help($title, 'roles', 'role');
+            $mform->display();
+            echo $OUTPUT->footer();
+            die;
+        }
+
     } else {
-        $definitiontable = new define_role_table_basic($systemcontext, $roleid);
+    /// Create the table object.
+        if ($action == 'view') {
+            $definitiontable = new view_role_definition_table($systemcontext, $roleid);
+        } else if ($showadvanced) {
+            $definitiontable = new define_role_table_advanced($systemcontext, $roleid);
+        } else {
+            $definitiontable = new define_role_table_basic($systemcontext, $roleid);
+        }
+        $definitiontable->read_submitted_permissions();
     }
-    $definitiontable->read_submitted_permissions();
-    if ($action == 'duplicate') {
-        $definitiontable->make_copy();
+
+/// Handle the cancel button.
+    if (optional_param('cancel', false, PARAM_BOOL)) {
+        redirect($returnurl);
     }
 
 /// Process submission in necessary.
         $definitiontable->save_changes();
         add_to_log(SITEID, 'role', $action, 'admin/roles/define.php?action=view&roleid=' .
                 $definitiontable->get_role_id(), $definitiontable->get_role_name(), '', $USER->id);
-        redirect($manageurl);
+        if ($action === 'add') {
+            redirect(new moodle_url('/admin/roles/define.php', array('action'=>'view', 'roleid'=>$definitiontable->get_role_id())));
+        } else {
+            redirect($returnurl);
+        }
     }
 
 /// Print the page header and tabs.
     $currenttab = 'manage';
     include('managetabs.php');
 
-    if ($action == 'add') {
-        $title = get_string('addinganewrole', 'role');
-    } else if ($action == 'duplicate') {
-        $title = get_string('addingrolebycopying', 'role', $rolenames[$roleid]->localname);
-    } else if ($action == 'view') {
-        $title = get_string('viewingdefinitionofrolex', 'role', $rolenames[$roleid]->localname);
-    } else if ($action == 'edit') {
-        $title = get_string('editingrolex', 'role', $rolenames[$roleid]->localname);
-    }
     echo $OUTPUT->heading_with_help($title, 'roles', 'role');
 
 /// Work out some button labels.
-    if ($action == 'add' || $action == 'duplicate') {
+    if ($action === 'add') {
         $submitlabel = get_string('createthisrole', 'role');
     } else {
         $submitlabel = get_string('savechanges');
     }
 
 /// On the view page, show some extra controls at the top.
-    if ($action == 'view') {
+    if ($action === 'view') {
         echo $OUTPUT->container_start('buttons');
-        $options = array();
-        $options['roleid'] = $roleid;
-        $options['action'] = 'edit';
-        echo $OUTPUT->single_button(new moodle_url($defineurl, $options), get_string('edit'));
-        $options['action'] = 'reset';
-        if ($definitiontable->get_archetype()) {
-            echo $OUTPUT->single_button(new moodle_url($manageurl, $options), get_string('resetrole', 'role'));
-        } else {
-            echo $OUTPUT->single_button(new moodle_url($manageurl, $options), get_string('resetrolenolegacy', 'role'));
-        }
-        $options['action'] = 'duplicate';
-        echo $OUTPUT->single_button(new moodle_url($defineurl, $options), get_string('duplicaterole', 'role'));
-        echo $OUTPUT->single_button(new moodle_url($manageurl), get_string('listallroles', 'role'));
+        $url = new moodle_url('/admin/roles/define.php', array('action'=>'edit', 'roleid'=>$roleid, 'return'=>'define'));
+        echo $OUTPUT->single_button(new moodle_url($url), get_string('edit'));
+        $url = new moodle_url('/admin/roles/define.php', array('action'=>'reset', 'roleid'=>$roleid, 'return'=>'define'));
+        echo $OUTPUT->single_button(new moodle_url($url), get_string('resetrole', 'role'));
+        $url = new moodle_url('/admin/roles/define.php', array('action'=>'export', 'roleid'=>$roleid));
+        echo $OUTPUT->single_button(new moodle_url($url), get_string('export', 'core_role'));
+        echo $OUTPUT->single_button($manageurl, get_string('listallroles', 'role'));
         echo $OUTPUT->container_end();
     }
 
         echo '<div class="mform">';
     } else {
     ?>
-<form id="rolesform" class="mform" action="<?php echo $baseurl; ?>" method="post"><div>
+<form id="rolesform" class="mform" action="<?php p($baseurl->out(false)); ?>" method="post"><div>
 <input type="hidden" name="sesskey" value="<?php p(sesskey()) ?>" />
+<input type="hidden" name="return" value="<?php p($return); ?>" />
+<input type="hidden" name="resettype" value="none" />
 <div class="submit buttons">
-    <input type="submit" name="savechanges" value="<?php echo $submitlabel; ?>" />
+    <input type="submit" name="savechanges" value="<?php p($submitlabel); ?>" />
     <input type="submit" name="cancel" value="<?php print_string('cancel'); ?>" />
 </div>
     <?php
     } else {
         ?>
 <div class="submit buttons">
-    <input type="submit" name="savechanges" value="<?php echo $submitlabel; ?>" />
+    <input type="submit" name="savechanges" value="<?php p($submitlabel); ?>" />
     <input type="submit" name="cancel" value="<?php print_string('cancel'); ?>" />
 </div>
 </div></form>
 
 /// Print a link back to the all roles list.
     echo '<div class="backlink">';
-    echo '<p><a href="' . $manageurl . '">' . get_string('backtoallroles', 'role') . '</a></p>';
+    echo '<p><a href="' . s($manageurl->out(false)) . '">' . get_string('backtoallroles', 'role') . '</a></p>';
     echo '</div>';
 
     echo $OUTPUT->footer();
index 7473f86..00b0f4c 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
  *
  * Responds to actions:
  *   add       - add a new role
- *   duplicate - like add, only initialise the new role by using an existing one.
  *   edit      - edit the definition of a role
  *   view      - view the definition of a role
  *
- * @package    core
- * @subpackage role
+ * @package    core_role
  * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->dirroot.'/user/selector/lib.php');
 
@@ -544,6 +543,10 @@ class define_role_table_advanced extends capability_table_with_risks {
     protected $allcontextlevels;
     protected $disabled = '';
 
+    protected $allowassign;
+    protected $allowoverride;
+    protected $allowswitch;
+
     public function __construct($context, $roleid) {
         $this->roleid = $roleid;
         parent::__construct($context, 'defineroletable', $roleid);
@@ -573,6 +576,10 @@ class define_role_table_advanced extends capability_table_with_risks {
             } else {
                 $this->contextlevels = array();
             }
+            $this->allowassign = array_keys($this->get_allow_roles_list('assign'));
+            $this->allowoverride = array_keys($this->get_allow_roles_list('override'));
+            $this->allowswitch = array_keys($this->get_allow_roles_list('switch'));
+
         } else {
             $this->role = new stdClass;
             $this->role->name = '';
@@ -580,6 +587,9 @@ class define_role_table_advanced extends capability_table_with_risks {
             $this->role->description = '';
             $this->role->archetype = '';
             $this->contextlevels = array();
+            $this->allowassign = array();
+            $this->allowoverride = array();
+            $this->allowswitch = array();
         }
         parent::load_current_permissions();
     }
@@ -646,6 +656,20 @@ class define_role_table_advanced extends capability_table_with_risks {
             }
         }
 
+        // Allowed roles.
+        $allow = optional_param_array('allowassign', null, PARAM_INT);
+        if (!is_null($allow)) {
+            $this->allowassign = $allow;
+        }
+        $allow = optional_param_array('allowoverride', null, PARAM_INT);
+        if (!is_null($allow)) {
+            $this->allowoverride = $allow;
+        }
+        $allow = optional_param_array('allowswitch', null, PARAM_INT);
+        if (!is_null($allow)) {
+            $this->allowswitch = $allow;
+        }
+
         // Now read the permissions for each capability.
         parent::read_submitted_permissions();
     }
@@ -655,14 +679,228 @@ class define_role_table_advanced extends capability_table_with_risks {
     }
 
     /**
-     * Call this after the table has been initialised, so to indicate that
-     * when save is called, we want to make a duplicate role.
+     * Call this after the table has been initialised,
+     * this resets everything to that role.
+     *
+     * @param int $roleid role id or 0 for no role
+     * @param array $options array with following keys:
+     *      'name', 'shortname', 'description', 'permissions', 'archetype',
+     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
      */
-    public function make_copy() {
-        $this->roleid = 0;
-        unset($this->role->id);
-        $this->role->name = role_get_name($this->role, null, ROLENAME_ORIGINAL) . ' ' . get_string('copyasnoun');
-        $this->role->shortname .= 'copy';
+    public function force_duplicate($roleid, array $options) {
+        global $DB;
+
+        if ($roleid == 0) {
+            // This means reset to nothing == remove everything.
+
+            if ($options['shortname']) {
+                $this->role->shortname = '';
+            }
+
+            if ($options['name']) {
+                $this->role->name = '';
+            }
+
+            if ($options['description']) {
+                $this->role->description = '';
+            }
+
+            if ($options['archetype']) {
+                $this->role->archetype = '';
+            }
+
+            if ($options['contextlevels']) {
+                $this->contextlevels = array();
+            }
+
+            if ($options['allowassign']) {
+                $this->allowassign = array();
+            }
+            if ($options['allowoverride']) {
+                $this->allowoverride = array();
+            }
+            if ($options['allowswitch']) {
+                $this->allowswitch = array();
+            }
+
+            if ($options['permissions']) {
+                foreach ($this->capabilities as $capid => $cap) {
+                    $this->permissions[$cap->name] = CAP_INHERIT;
+                }
+            }
+
+            return;
+        }
+
+        $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
+
+        if ($options['shortname']) {
+            $this->role->shortname = $role->shortname;
+        }
+
+        if ($options['name']) {
+            $this->role->name = $role->name;
+        }
+
+        if ($options['description']) {
+            $this->role->description = $role->description;
+        }
+
+        if ($options['archetype']) {
+            $this->role->archetype = $role->archetype;
+        }
+
+        if ($options['contextlevels']) {
+            $this->contextlevels = array();
+            $levels = get_role_contextlevels($roleid);
+            foreach ($levels as $cl) {
+                $this->contextlevels[$cl] = $cl;
+            }
+        }
+
+        if ($options['allowassign']) {
+            $this->allowassign = array_keys($this->get_allow_roles_list('assign', $roleid));
+        }
+        if ($options['allowoverride']) {
+            $this->allowoverride = array_keys($this->get_allow_roles_list('override', $roleid));
+        }
+        if ($options['allowswitch']) {
+            $this->allowswitch = array_keys($this->get_allow_roles_list('switch', $roleid));
+        }
+
+        if ($options['permissions']) {
+            $this->permissions = $DB->get_records_menu('role_capabilities',
+                array('roleid' => $roleid, 'contextid' => context_system::instance()->id),
+                '', 'capability,permission');
+
+            foreach ($this->capabilities as $capid => $cap) {
+                if (!isset($this->permissions[$cap->name])) {
+                    $this->permissions[$cap->name] = CAP_INHERIT;
+                }
+            }
+        }
+    }
+
+    /**
+     * Change the role definition to match given archetype.
+     *
+     * @param string $archetype
+     * @param array $options array with following keys:
+     *      'name', 'shortname', 'description', 'permissions', 'archetype',
+     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
+     */
+    public function force_archetype($archetype, array $options) {
+        $archetypes = get_role_archetypes();
+        if (!isset($archetypes[$archetype])) {
+            throw new coding_exception('Unknown archetype: '.$archetype);
+        }
+
+        if ($options['shortname']) {
+            $this->role->shortname = '';
+        }
+
+        if ($options['name']) {
+            $this->role->name = '';
+        }
+
+        if ($options['description']) {
+            $this->role->description = '';
+        }
+
+        if ($options['archetype']) {
+            $this->role->archetype = $archetype;
+        }
+
+        if ($options['contextlevels']) {
+            $this->contextlevels = array();
+            $defaults = get_default_contextlevels($archetype);
+            foreach ($defaults as $cl) {
+                $this->contextlevels[$cl] = $cl;
+            }
+        }
+
+        if ($options['allowassign']) {
+            $this->allowassign = get_default_role_archetype_allows('assign', $archetype);
+        }
+        if ($options['allowoverride']) {
+            $this->allowoverride = get_default_role_archetype_allows('override', $archetype);
+        }
+        if ($options['allowswitch']) {
+            $this->allowswitch = get_default_role_archetype_allows('switch', $archetype);
+        }
+
+        if ($options['permissions']) {
+            $defaultpermissions = get_default_capabilities($archetype);
+            foreach ($this->permissions as $k => $v) {
+                if (isset($defaultpermissions[$k])) {
+                    $this->permissions[$k] = $defaultpermissions[$k];
+                    continue;
+                }
+                $this->permissions[$k] = CAP_INHERIT;
+            }
+        }
+    }
+
+    /**
+     * Change the role definition to match given preset.
+     *
+     * @param string $xml
+     * @param array $options array with following keys:
+     *      'name', 'shortname', 'description', 'permissions', 'archetype',
+     *      'contextlevels', 'allowassign', 'allowoverride', 'allowswitch'
+     */
+    public function force_preset($xml, array $options) {
+        if (!$info = core_role_preset::parse_preset($xml)) {
+            throw new coding_exception('Invalid role preset');
+        }
+
+        if ($options['shortname']) {
+            if (isset($info['shortname'])) {
+                $this->role->shortname = $info['shortname'];
+            }
+        }
+
+        if ($options['name']) {
+            if (isset($info['name'])) {
+                $this->role->name = $info['name'];
+            }
+        }
+
+        if ($options['description']) {
+            if (isset($info['description'])) {
+                $this->role->description = $info['description'];
+            }
+        }
+
+        if ($options['archetype']) {
+            if (isset($info['archetype'])) {
+                $this->role->archetype = $info['archetype'];
+            }
+        }
+
+        if ($options['contextlevels']) {
+            if (isset($info['contextlevels'])) {
+                $this->contextlevels = $info['contextlevels'];
+            }
+        }
+
+        foreach (array('assign', 'override', 'switch') as $type) {
+            if ($options['allow'.$type]) {
+                if (isset($info['allow'.$type])) {
+                    $this->{'allow'.$type} = $info['allow'.$type];
+                }
+            }
+        }
+
+        if ($options['permissions']) {
+            foreach ($this->permissions as $k => $v) {
+                // Note: do not set everything else to CAP_INHERIT here
+                //       because the xml file might not contain all capabilities.
+                if (isset($info['permissions'][$k])) {
+                    $this->permissions[$k] = $info['permissions'][$k];
+                }
+            }
+        }
     }
 
     public function get_role_name() {
@@ -696,10 +934,42 @@ class define_role_table_advanced extends capability_table_with_risks {
         // Assignable contexts.
         set_role_contextlevels($this->role->id, $this->contextlevels);
 
+        // Set allowed roles.
+        $this->save_allow('assign');
+        $this->save_allow('override');
+        $this->save_allow('switch');
+
         // Permissions.
         parent::save_changes();
     }
 
+    protected function save_allow($type) {
+        global $DB;
+
+        $current = array_keys($this->get_allow_roles_list($type));
+        $wanted = $this->{'allow'.$type};
+
+        $addfunction = 'allow_'.$type;
+        $deltable = 'role_allow_'.$type;
+        $field = 'allow'.$type;
+
+        foreach ($current as $roleid) {
+            if (!in_array($roleid, $wanted)) {
+                $DB->delete_records($deltable, array('roleid'=>$this->roleid, $field=>$roleid));
+                continue;
+            }
+            $key = array_search($roleid, $wanted);
+            unset($wanted[$key]);
+        }
+
+        foreach ($wanted as $roleid) {
+            if ($roleid == -1) {
+                $roleid = $this->roleid;
+            }
+            $addfunction($this->roleid, $roleid);
+        }
+    }
+
     protected function get_name_field($id) {
         return '<input type="text" id="' . $id . '" name="' . $id . '" maxlength="254" value="' . s($this->role->name) . '" />';
     }
@@ -742,9 +1012,10 @@ class define_role_table_advanced extends capability_table_with_risks {
      * Returns an array of roles of the allowed type.
      *
      * @param string $type Must be one of: assign, switch, or override.
+     * @param int $roleid (null means current role)
      * @return array
      */
-    protected function get_allow_roles_list($type) {
+    protected function get_allow_roles_list($type, $roleid = null) {
         global $DB;
 
         if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
@@ -752,7 +1023,11 @@ class define_role_table_advanced extends capability_table_with_risks {
             return array();
         }
 
-        if (empty($this->roleid)) {
+        if ($roleid === null) {
+            $roleid = $this->roleid;
+        }
+
+        if (empty($roleid)) {
             return array();
         }
 
@@ -761,7 +1036,7 @@ class define_role_table_advanced extends capability_table_with_risks {
                   JOIN {role_allow_{$type}} a ON a.allow{$type} = r.id
                  WHERE a.roleid = :roleid
               ORDER BY r.sortorder ASC";
-        return $DB->get_records_sql($sql, array('roleid'=>$this->roleid));
+        return $DB->get_records_sql($sql, array('roleid'=>$roleid));
     }
 
     /**
@@ -771,12 +1046,22 @@ class define_role_table_advanced extends capability_table_with_risks {
      * @return array Am array of role names with the allowed type
      */
     protected function get_allow_role_control($type) {
-        if ($roles = $this->get_allow_roles_list($type)) {
-            $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
-            return implode(', ', $roles);
-        } else {
-            return get_string('none');
+        if ($type !== 'assign' and $type !== 'switch' and $type !== 'override') {
+            debugging('Invalid role allowed type specified', DEBUG_DEVELOPER);
+            return '';
+        }
+
+        $property = 'allow'.$type;
+        $selected = $this->$property;
+
+        $options = array();
+        foreach (role_get_names(null, ROLENAME_ALIAS) as $role) {
+            $options[$role->id] = $role->localname;
         }
+        if ($this->roleid == 0) {
+            $options[-1] = get_string('thisnewrole', 'core_role');
+        }
+        return html_writer::select($options, 'allow'.$type.'[]', $selected, false, array('multiple'=>'multiple', 'size'=>10));
     }
 
     /**
@@ -807,10 +1092,10 @@ class define_role_table_advanced extends capability_table_with_risks {
             $extraclass = '';
         }
         echo '<div class="felement' . $extraclass . '">';
+        echo $field;
         if (isset($this->errors[$name])) {
             echo $OUTPUT->error_text($this->errors[$name]);
         }
-        echo $field;
         echo '</div>';
         echo '</div>';
     }
@@ -831,9 +1116,9 @@ class define_role_table_advanced extends capability_table_with_risks {
         $this->print_field('edit-description', get_string('customroledescription', 'role').'&nbsp;'.$OUTPUT->help_icon('customroledescription', 'role'), $this->get_description_field('description'));
         $this->print_field('menuarchetype', get_string('archetype', 'role').'&nbsp;'.$OUTPUT->help_icon('archetype', 'role'), $this->get_archetype_field('archetype'));
         $this->print_field('', get_string('maybeassignedin', 'role'), $this->get_assignable_levels_control());
-        $this->print_field('', get_string('allowassign', 'role'), $this->get_allow_role_control('assign'));
-        $this->print_field('', get_string('allowoverride', 'role'), $this->get_allow_role_control('override'));
-        $this->print_field('', get_string('allowswitch', 'role'), $this->get_allow_role_control('switch'));
+        $this->print_field('menuallowassign', get_string('allowassign', 'role'), $this->get_allow_role_control('assign'));
+        $this->print_field('menuallowoverride', get_string('allowoverride', 'role'), $this->get_allow_role_control('override'));
+        $this->print_field('menuallowswitch', get_string('allowswitch', 'role'), $this->get_allow_role_control('switch'));
         if ($risks = $this->get_role_risks_info()) {
             $this->print_field('', get_string('rolerisks', 'role'), $risks);
         }
@@ -934,6 +1219,16 @@ class view_role_definition_table extends define_role_table_advanced {
         }
     }
 
+    protected function get_allow_role_control($type) {
+        if ($roles = $this->get_allow_roles_list($type)) {
+            $roles = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
+            return implode(', ', $roles);
+        } else {
+            return get_string('none');
+        }
+    }
+
+
     protected function print_show_hide_advanced_button() {
         // Do nothing.
     }
@@ -1856,4 +2151,4 @@ class admins_existing_selector extends user_selector_base {
         $options['file'] = $CFG->admin . '/roles/lib.php';
         return $options;
     }
-}
+}
\ No newline at end of file
index c7a4f69..e7ffc71 100644 (file)
             redirect($baseurl);
             break;
 
-        case 'reset':
-            if (!$confirmed) {
-                // show confirmation
-                echo $OUTPUT->header();
-                $optionsyes = array('action'=>'reset', 'roleid'=>$roleid, 'sesskey'=>sesskey(), 'confirm'=>1);
-                $optionsno  = array('action'=>'view', 'roleid'=>$roleid);
-                $a = new stdClass();
-                $a->id = $roleid;
-                $a->name = $roles[$roleid]->name;
-                $a->shortname = $roles[$roleid]->shortname;
-                $a->legacytype = $roles[$roleid]->archetype;
-                if (empty($a->legacytype)) {
-                    $warning = get_string('resetrolesurenolegacy', 'role', $a);
-                } else {
-                    $warning = get_string('resetrolesure', 'role', $a);
-                }
-                $formcontinue = new single_button(new moodle_url('manage.php', $optionsyes), get_string('yes'));
-                $formcancel = new single_button(new moodle_url('manage.php', $optionsno), get_string('no'), 'get');
-                echo $OUTPUT->confirm($warning, $formcontinue, $formcancel);
-                echo $OUTPUT->footer();
-                die;
-            }
-
-            // Reset context levels for standard archetypes
-            if ($roles[$roleid]->archetype) {
-                set_role_contextlevels($roleid, get_default_contextlevels($roles[$roleid]->archetype));
-            }
-
-            //reset or delete the capabilities
-            reset_role_capabilities($roleid);
-
-            // Mark context dirty, log and redirect.
-            mark_context_dirty($systemcontext->path);
-            add_to_log(SITEID, 'role', 'reset', 'admin/roles/manage.php?action=reset&roleid=' . $roleid, $roles[$roleid]->localname, '', $USER->id);
-            redirect($defineurl . '?action=view&roleid=' . $roleid);
-            break;
     }
 
 /// Print the page header and tabs.
 
 /// Get some strings outside the loop.
     $stredit = get_string('edit');
-    $strduplicate = get_string('duplicate');
     $strdelete = get_string('delete');
     $strmoveup = get_string('moveup');
     $strmovedown = get_string('movedown');
         // edit
         $row[3] .= get_action_icon($defineurl . '?action=edit&amp;roleid=' . $role->id,
                 'edit', $stredit, get_string('editxrole', 'role', $role->localname));
-        // duplicate
-        $row[3] .= get_action_icon($defineurl . '?action=duplicate&amp;roleid=' . $role->id,
-                'copy', $strduplicate, get_string('createrolebycopying', 'role', $role->localname));
         // delete
         if (isset($undeletableroles[$role->id])) {
             $row[3] .= get_spacer();
diff --git a/admin/roles/role_schema.xml b/admin/roles/role_schema.xml
new file mode 100644 (file)
index 0000000..f0a33c9
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+    <xs:element name="role">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="shortname" minOccurs="0"/>
+                <xs:element ref="name" minOccurs="0"/>
+                <xs:element ref="description" minOccurs="0"/>
+                <xs:element ref="archetype" minOccurs="0"/>
+                <xs:element ref="contextlevels" minOccurs="0"/>
+                <xs:element ref="allowassign" minOccurs="0"/>
+                <xs:element ref="allowoverride" minOccurs="0"/>
+                <xs:element ref="allowswitch" minOccurs="0"/>
+                <xs:element ref="permissions" minOccurs="0"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="archetype" type="xs:string"/>
+    <xs:element name="contextlevels">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element minOccurs="0" maxOccurs="unbounded" ref="level"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="level" type="xs:string"/>
+    <xs:element name="allowassign">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="shortname"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="allowoverride">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="shortname"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="allowswitch">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element maxOccurs="unbounded" minOccurs="0" ref="shortname"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="permissions">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element ref="inherit" minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element ref="allow" minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element ref="prevent" minOccurs="0" maxOccurs="unbounded"/>
+                <xs:element ref="prohibit" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:element name="shortname" type="xs:string"/>
+    <xs:element name="name" type="xs:string"/>
+    <xs:element name="description" type="xs:string"/>
+    <xs:element name="inherit" type="xs:string"/>
+    <xs:element name="allow" type="xs:string"/>
+    <xs:element name="prevent" type="xs:string"/>
+    <xs:element name="prohibit" type="xs:string"/>
+</xs:schema>
diff --git a/admin/roles/tests/preset_test.php b/admin/roles/tests/preset_test.php
new file mode 100644 (file)
index 0000000..0899480
--- /dev/null
@@ -0,0 +1,79 @@
+<?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/>.
+
+/**
+ * Role XML presets test case.
+ *
+ * @package   core_role
+ * @category  phpunit
+ * @copyright 2013 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+class core_role_preset_testcase extends advanced_testcase {
+    public function test_xml() {
+        global $DB;
+
+        $roles = $DB->get_records('role');
+
+        foreach ($roles as $role) {
+            $xml = core_role_preset::get_export_xml($role->id);
+            $this->assertTrue(core_role_preset::is_valid_preset($xml));
+            $info = core_role_preset::parse_preset($xml);
+            $this->assertSame($role->shortname, $info['shortname']);
+            $this->assertSame($role->name, $info['name']);
+            $this->assertSame($role->description, $info['description']);
+            $this->assertSame($role->archetype, $info['archetype']);
+
+            $contextlevels = get_role_contextlevels($role->id);
+            $this->assertEquals(array_values($contextlevels), array_values($info['contextlevels']));
+
+            foreach (array('assign', 'override', 'switch') as $type) {
+                $records = $DB->get_records('role_allow_'.$type, array('roleid'=>$role->id), "allow$type ASC");
+                $allows = array();
+                foreach ($records as $record) {
+                    if ($record->{'allow'.$type} == $role->id) {
+                        array_unshift($allows, -1);
+                    }
+                    $allows[] = $record->{'allow'.$type};
+                }
+                $this->assertEquals($allows, $info['allow'.$type], "$type $role->shortname does not match");
+            }
+
+            $capabilities = $DB->get_records_sql(
+                "SELECT *
+                   FROM {role_capabilities}
+                  WHERE contextid = :syscontext AND roleid = :roleid
+               ORDER BY capability ASC",
+                array('syscontext'=>context_system::instance()->id, 'roleid'=>$role->id));
+
+            foreach ($capabilities as $cap) {
+                $this->assertEquals($cap->permission, $info['permissions'][$cap->capability]);
+                unset($info['permissions'][$cap->capability]);
+            }
+            // The remainders should be only inherits.
+            foreach ($info['permissions'] as $capability => $permission) {
+                if ($permission == CAP_INHERIT) {
+                    continue;
+                }
+                $this->fail('only CAP_INHERIT expected');
+            }
+        }
+    }
+}
index 4d9cd84..44d2546 100644 (file)
@@ -65,6 +65,10 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
             60 => new lang_string('numminutes', '', 60),
             30 => new lang_string('numminutes', '', 30),
             15 => new lang_string('numminutes', '', 15))));
+        // Define the prefix to be added to imported profiling runs.
+        $temp->add(new admin_setting_configtext('profilingimportprefix',
+                new lang_string('profilingimportprefix', 'admin'),
+                new lang_string('profilingimportprefix_desc', 'admin'), '(I)', PARAM_TAG, 10));
 
         // Add the 'profiling' page to admin block
         $ADMIN->add('development', $temp);
index f34e139..57d3859 100644 (file)
@@ -32,7 +32,7 @@ $lng                    = required_param('lng', PARAM_LANG);
 $currentpage            = optional_param('p', 0, PARAM_INT);
 $translatorsubmitted    = optional_param('translatorsubmitted', 0, PARAM_BOOL);
 
-$PAGE->set_pagelayout('standard');
+$PAGE->set_pagelayout('report'); // Allows for wide page contents.
 $PAGE->set_url('/admin/tool/customlang/edit.php', array('lng' => $lng));
 navigation_node::override_active_url(new moodle_url('/admin/tool/customlang/index.php'));
 $PAGE->set_title(get_string('pluginname', 'tool_customlang'));
index 878eb4c..f209785 100644 (file)
@@ -56,22 +56,18 @@ class tool_customlang_utils {
 
         $list['moodle'] = 'core';
 
-        $coresubsystems = get_core_subsystems();
+        $coresubsystems = core_component::get_core_subsystems();
         ksort($coresubsystems); // should be but just in case
         foreach ($coresubsystems as $name => $location) {
-            if ($name != 'moodle.org') {
-                $list[$name] = 'core_'.$name;
-            }
+            $list[$name] = 'core_'.$name;
         }
 
-        $plugintypes = get_plugin_types();
+        $plugintypes = core_component::get_plugin_types();
         foreach ($plugintypes as $type => $location) {
-            $pluginlist = get_plugin_list($type);
+            $pluginlist = core_component::get_plugin_list($type);
             foreach ($pluginlist as $name => $ununsed) {
                 if ($type == 'mod') {
-                    if (array_key_exists($name, $list)) {
-                        throw new Exception('Activity module and core subsystem name collision');
-                    }
+                    // Plugin names are now automatically validated.
                     $list[$name] = $type.'_'.$name;
                 } else {
                     $list[$type.'_'.$name] = $type.'_'.$name;
index f96f83b..c733dda 100644 (file)
@@ -53,7 +53,7 @@ class tool_customlang_renderer extends plugin_renderer_base {
         $output = '';
 
         if (empty($translator->strings)) {
-            return $this->heading(get_string('nostringsfound', 'tool_customlang'), 3);
+            return $this->notification(get_string('nostringsfound', 'tool_customlang'));
         }
 
         $table = new html_table();
index 3490eaa..0b888ba 100644 (file)
@@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die();
  */
 class tool_installaddon_installer {
 
-    /** @var tool_installaddon_installfromzip */
+    /** @var tool_installaddon_installfromzip_form */
     protected $installfromzipform = null;
 
     /**
@@ -87,12 +87,9 @@ class tool_installaddon_installer {
     }
 
     /**
-     * @return tool_installaddon_installfromzip
+     * @return tool_installaddon_installfromzip_form
      */
     public function get_installfromzip_form() {
-        global $CFG;
-        require_once(dirname(__FILE__).'/installfromzip_form.php');
-
         if (!is_null($this->installfromzipform)) {
             return $this->installfromzipform;
         }
@@ -100,22 +97,22 @@ class tool_installaddon_installer {
         $action = $this->index_url();
         $customdata = array('installer' => $this);
 
-        $this->installfromzipform = new tool_installaddon_installfromzip($action, $customdata);
+        $this->installfromzipform = new tool_installaddon_installfromzip_form($action, $customdata);
 
         return $this->installfromzipform;
     }
 
     /**
-     * Saves the ZIP file from the {@link tool_installaddon_installfromzip} form
+     * Saves the ZIP file from the {@link tool_installaddon_installfromzip_form} form
      *
      * The file is saved into the given temporary location for inspection and eventual
      * deployment. The form is expected to be submitted and validated.
      *
-     * @param tool_installaddon_installfromzip $form
+     * @param tool_installaddon_installfromzip_form $form
      * @param string $targetdir full path to the directory where the ZIP should be stored to
      * @return string filename of the saved file relative to the given target
      */
-    public function save_installfromzip_file(tool_installaddon_installfromzip $form, $targetdir) {
+    public function save_installfromzip_file(tool_installaddon_installfromzip_form $form, $targetdir) {
 
         $filename = clean_param($form->get_new_filename('zipfile'), PARAM_FILE);
         $form->save_file('zipfile', $targetdir.'/'.$filename);
@@ -575,7 +572,7 @@ class tool_installaddon_installer {
             return false;
         }
 
-        list($plugintype, $pluginname) = normalize_component($data->component);
+        list($plugintype, $pluginname) = core_component::normalize_component($data->component);
 
         if ($plugintype === 'core') {
             return false;
@@ -585,6 +582,15 @@ class tool_installaddon_installer {
             return false;
         }
 
+        if (!core_component::is_valid_plugin_name($plugintype, $pluginname)) {
+            return false;
+        }
+
+        $plugintypes = core_component::get_plugin_types();
+        if (!isset($plugintypes[$plugintype])) {
+            return false;
+        }
+
         // Keep this regex in sync with the one used by the download.moodle.org/api/x.y/pluginfo.php
         if (!preg_match('/^[0-9]+$/', $data->version)) {
             return false;
index 18552fa..8a6d6a0 100644 (file)
@@ -33,7 +33,7 @@ require_once($CFG->libdir.'/formslib.php');
  * @copyright 2013 David Mudrak <david@moodle.com>
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class tool_installaddon_installfromzip extends moodleform {
+class tool_installaddon_installfromzip_form extends moodleform {
 
     /**
      * Defines the form elements
index f68ebed..7dea2cc 100644 (file)
@@ -25,8 +25,6 @@
 
 require(dirname(__FILE__) . '/../../../config.php');
 require_once($CFG->libdir.'/filelib.php');
-require_once(dirname(__FILE__).'/classes/installer.php');
-require_once(dirname(__FILE__).'/classes/validator.php');
 
 require_login();
 require_capability('moodle/site:config', context_system::instance());
index cb0d2a6..5249694 100644 (file)
@@ -25,7 +25,6 @@
 
 require(dirname(__FILE__) . '/../../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
-require_once(dirname(__FILE__).'/classes/installer.php');
 
 admin_externalpage_setup('tool_installaddon_index');
 
index f38d109..9bd433f 100644 (file)
@@ -28,7 +28,6 @@ define('AJAX_SCRIPT', true);
 
 require(dirname(__FILE__) . '/../../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
-require_once(dirname(__FILE__).'/classes/installer.php');
 
 require_login();
 
index 4169719..28e37bd 100644 (file)
@@ -26,9 +26,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-global $CFG;
-require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/installaddon/classes/installer.php');
-
 
 /**
  * Unit tests for the {@link tool_installaddon_installer} class
@@ -124,6 +121,13 @@ class tool_installaddon_installer_test extends advanced_testcase {
             'version' => 2012123199,
         )));
         $this->assertSame(false, $installer->testable_decode_remote_request($request));
+
+        $request = base64_encode(json_encode(array(
+            'name' => 'Bogus module name',
+            'component' => 'mod_xxx_yyy',
+            'version' => 2012123190,
+        )));
+        $this->assertSame(false, $installer->testable_decode_remote_request($request));
     }
 
     public function test_move_directory() {
index c008d9c..dd55653 100644 (file)
@@ -26,9 +26,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-global $CFG;
-require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/installaddon/classes/validator.php');
-
 
 /**
  * Unit tests for the {@link tool_installaddon_installer} class
index 2fc66cf..ef7454d 100644 (file)
@@ -26,8 +26,6 @@
 require(dirname(__FILE__) . '/../../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->libdir.'/filelib.php');
-require_once(dirname(__FILE__).'/classes/installer.php');
-require_once(dirname(__FILE__).'/classes/validator.php');
 
 navigation_node::override_active_url(new moodle_url('/admin/tool/installaddon/index.php'));
 admin_externalpage_setup('tool_installaddon_validate');
diff --git a/admin/tool/profiling/export.php b/admin/tool/profiling/export.php
new file mode 100644 (file)
index 0000000..32adc0d
--- /dev/null
@@ -0,0 +1,56 @@
+<?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/>.
+
+/**
+ * Profiling tool export utility.
+ *
+ * @package    tool_profiling
+ * @copyright  2013 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');
+
+// Page parameters.
+$runid = required_param('runid', PARAM_ALPHANUM);
+$listurl = required_param('listurl', PARAM_PATH);
+
+admin_externalpage_setup('toolprofiling');
+
+$PAGE->navbar->add(get_string('export', 'tool_profiling'));
+
+// Calculate export variables.
+$tempdir = 'profiling';
+make_temp_directory($tempdir);
+$runids = array($runid);
+$filename = $runid . '.mpr';
+$filepath = $CFG->tempdir . '/' . $tempdir . '/' . $filename;
+
+// Generate the mpr file and send it.
+if (profiling_export_runs($runids, $filepath)) {
+    send_file($filepath, $filename, 0, 0, false, false, '', true);
+    unlink($filepath); // Delete once sent.
+    die;
+}
+
+// Something wrong happened, notice it and done.
+$urlparams = array(
+        'runid' => $runid,
+        'listurl' => $listurl);
+$url = new moodle_url('/admin/tool/profiling/index.php', $urlparams);
+notice(get_string('exportproblem', 'tool_profiling', $urlparams), $url);
diff --git a/admin/tool/profiling/import.php b/admin/tool/profiling/import.php
new file mode 100644 (file)
index 0000000..04c216e
--- /dev/null
@@ -0,0 +1,70 @@
+<?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/>.
+
+/**
+ * Profiling tool import utility.
+ *
+ * @package    tool_profiling
+ * @copyright  2013 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(__FILE__) . '/../../../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');
+require_once(dirname(__FILE__) . '/import_form.php');
+
+admin_externalpage_setup('toolprofiling');
+
+$PAGE->navbar->add(get_string('import', 'tool_profiling'));
+
+// Calculate export variables.
+$tempdir = 'profiling';
+make_temp_directory($tempdir);
+
+// URL where we'll end, both on success and failure.
+$url = new moodle_url('/admin/tool/profiling/index.php');
+
+// Instantiate the upload profiling runs form.
+$mform = new profiling_import_form();
+
+// If there is any file to import.
+if ($data = $mform->get_data()) {
+    $filename = $mform->get_new_filename('mprfile');
+    $file = $CFG->tempdir . '/' . $tempdir . '/' . $filename;
+    $status = $mform->save_file('mprfile', $file);
+    if ($status) {
+        // File saved properly, let's import it.
+        $status = profiling_import_runs($file, $data->importprefix);
+    }
+    // Delete the temp file, not needed anymore.
+    if (file_exists($file)) {
+        unlink($file);
+    }
+    if ($status) {
+        // Import ended ok, let's redirect to main profiling page.
+        redirect($url, get_string('importok', 'tool_profiling', $filename));
+    }
+} else {
+    echo $OUTPUT->header();
+    echo $OUTPUT->heading(get_string('import', 'tool_profiling'));
+    $mform->display();
+    echo $OUTPUT->footer();
+    die;
+}
+
+// Something wrong happened, notice it and done.
+notice(get_string('importproblem', 'tool_profiling', $filename), $url);
diff --git a/admin/tool/profiling/import_form.php b/admin/tool/profiling/import_form.php
new file mode 100644 (file)
index 0000000..f165a6b
--- /dev/null
@@ -0,0 +1,47 @@
+<?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/>.
+
+/**
+ * Profiling tool import utility form.
+ *
+ * @package    tool_profiling
+ * @copyright  2013 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->libdir . '/formslib.php');
+
+class profiling_import_form extends moodleform {
+    public function definition () {
+        global $CFG;
+
+        $mform = $this->_form;
+
+        $mform->addElement('header', 'settingsheader', get_string('upload'));
+
+        $mform->addElement('filepicker', 'mprfile', get_string('file'), null, array('accepted_types' => array('.mpr', '.zip')));
+        $mform->addRule('mprfile', null, 'required');
+
+        $mform->addElement('text', 'importprefix',
+                get_string('importprefix', 'tool_profiling'), array('size' => 10));
+        $mform->setDefault('importprefix', $CFG->profilingimportprefix);
+        $mform->setType('importprefix', PARAM_TAG);
+
+        $this->add_action_buttons(false, get_string('import', 'tool_profiling'));
+    }
+}
index eebfcd8..a5e21d3 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Profiling tool.
  *
- * @package    tool
- * @subpackage profiling
+ * @package    tool_profiling
  * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -161,6 +160,9 @@ if (isset($script)) {
 
     echo $OUTPUT->heading($header);
 
+    // Print the controller block with different options.
+    echo profiling_list_controls($listurl);
+
     // TODO: Fix flexitable to validate tsort/thide/tshow/tifirs/tilast/page
     // TODO: Fix table_sql to allow it to work without WHERE clause
     // add silly condition (1 = 1) because of table_sql bug
@@ -179,9 +181,6 @@ if (isset($script)) {
     $table->define_baseurl($baseurl);
     $table->column_suppress('url');
     $table->out(PROFILING_RUNSPERPAGE, true);
-
-    // Print the controller block with different options
-    echo profiling_list_controls($listurl);
 }
 
 // Footer.
index bdc8dd7..4a687f8 100644 (file)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die();
+
 $string['calls'] = 'Function calls';
 $string['cannotfindanyrunforurl'] = 'Sorry, cannot find any profiling run for the \'{$a}\' URL';
 $string['cannotfindanyrunforrunid'] = 'Sorry, cannot find the \'{$a}\' profiling run';
 $string['comment'] = 'Comment';
+$string['cputime'] = 'CPU time';
 $string['differencesbetween2runsof'] = 'Differences between 2 runs of {$a}';
 $string['executiontime'] = 'Execution time';
-$string['cputime'] = 'CPU time';
+$string['export'] = 'Export';
+$string['exportproblem'] = 'Some problem happened exporting the profile run "{$a->runid}" corresponding to the request "{$a->listurl}".';
+$string['exportthis'] = 'Export this profiling run';
+$string['import'] = 'Import';
+$string['importok'] = 'File "{$a}" imported successfully.';
+$string['importprefix'] = 'Import prefix';
+$string['importproblem'] = 'Some problem happened importing the file "{$a}".';
 $string['lastrunof'] = 'Summary of last run of {$a}';
 $string['markreferencerun'] = 'Mark as reference run/comment';
 $string['memory'] = 'Memory used';
index a8c6c0e..883ef9a 100644 (file)
 /**
  * Version details.
  *
- * @package    tool
- * @subpackage profiling
+ * @package    tool_profiling
  * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013050200; // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013050100; // Requires this Moodle version
 $plugin->component = 'tool_profiling'; // Full name of the plugin (used for diagnostics)
diff --git a/admin/tool/uploaduser/db/access.php b/admin/tool/uploaduser/db/access.php
new file mode 100644 (file)
index 0000000..7fe1c3f
--- /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/>.
+
+/**
+ * Defines the capabilities used by the user upload admin tool
+ *
+ * @package    tool_uploaduser
+ * @copyright  2013 Dan Poltawski <dan@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+
+    // Allows the user to upload user pictures.
+    'tool/uploaduser:uploaduserpictures' => array(
+        'riskbitmask' => RISK_SPAM,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'archetypes' => array(
+            'manager' => CAP_ALLOW
+        ),
+        'clonepermissionsfrom' =>  'moodle/site:uploadusers',
+    ),
+);
index 1c2330a..b8d46f3 100644 (file)
@@ -59,6 +59,7 @@ $string['uploadusers_help'] = 'Users may be uploaded (and optionally enrolled in
 * Required fieldnames are username, password, firstname, lastname, email';
 $string['uploaduserspreview'] = 'Upload users preview';
 $string['uploadusersresult'] = 'Upload users results';
+$string['uploaduser:uploaduserpictures'] = 'Upload user pictures';
 $string['useraccountupdated'] = 'User updated';
 $string['useraccountuptodate'] = 'User up-to-date';
 $string['userdeleted'] = 'User deleted';
index f0f1511..fe3d8e5 100644 (file)
@@ -38,7 +38,7 @@ admin_externalpage_setup('tooluploaduserpictures');
 
 require_login();
 
-require_capability('moodle/site:uploadusers', context_system::instance());
+require_capability('tool/uploaduser:uploaduserpictures', context_system::instance());
 
 $site = get_site();
 
index 0dc16e6..d3b37d2 100644 (file)
@@ -25,7 +25,5 @@
 
 defined('MOODLE_INTERNAL') || die;
 
-if (has_capability('moodle/site:uploadusers', $systemcontext)) {
-    $ADMIN->add('accounts', new admin_externalpage('tooluploaduser', get_string('uploadusers', 'tool_uploaduser'), "$CFG->wwwroot/$CFG->admin/tool/uploaduser/index.php", 'moodle/site:uploadusers'));
-    $ADMIN->add('accounts', new admin_externalpage('tooluploaduserpictures', get_string('uploadpictures','tool_uploaduser'), "$CFG->wwwroot/$CFG->admin/tool/uploaduser/picture.php", 'moodle/site:uploadusers'));
-}
+$ADMIN->add('accounts', new admin_externalpage('tooluploaduser', get_string('uploadusers', 'tool_uploaduser'), "$CFG->wwwroot/$CFG->admin/tool/uploaduser/index.php", 'moodle/site:uploadusers'));
+$ADMIN->add('accounts', new admin_externalpage('tooluploaduserpictures', get_string('uploadpictures','tool_uploaduser'), "$CFG->wwwroot/$CFG->admin/tool/uploaduser/picture.php", 'tool/uploaduser:uploaduserpictures'));
index 6de83e5..5116d1b 100644 (file)
@@ -25,7 +25,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2013050100; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2013061400; // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2013050100; // Requires this Moodle version
 $plugin->component = 'tool_uploaduser'; // Full name of the plugin (used for diagnostics)
 
index d93fba0..393d774 100644 (file)
@@ -215,23 +215,28 @@ class XMLDBAction {
      * @return string PHP code to be used to mark a reached savepoint
      */
     function upgrade_savepoint_php($structure) {
+        global $CFG;
 
-        $path = $structure->getPath();
+        // NOTE: $CFG->admin !== 'admin' is not supported in XMLDB editor, sorry.
 
-        // Trim "db" from path
-        $path = dirname($path);
+        $path = $structure->getPath();
+        $plugintype = 'error';
 
-        // Get pluginname, plugindir and plugintype
-        $pluginname = basename($path);
-        if ($path == 'lib') { // exception for lib (not proper plugin)
-            $plugindir = 'lib';
+        if ($path === 'lib/db') {
             $plugintype = 'lib';
-        } else { // rest of plugins
-            // TODO: this is not nice and may fail, plugintype should be passed around somehow instead
-            $plugintypes = get_plugin_types(false);
-            $plugindir = dirname($path);
-            $plugindir = str_replace('\\', '/', $plugindir);
-            $plugintype = array_search($plugindir, $plugintypes);
+            $pluginname = null;
+
+        } else {
+            $path = dirname($path);
+            $pluginname = basename($path);
+            $path = dirname($path);
+            $plugintypes = core_component::get_plugin_types();
+            foreach ($plugintypes as $type => $fulldir) {
+                if ($CFG->dirroot.'/'.$path === $fulldir) {
+                    $plugintype = $type;
+                    break;
+                }
+            }
         }
 
         $result = '';
index 1c4d915..51976bd 100644 (file)
@@ -18,7 +18,7 @@
  * Provides an overview of installed admin tools
  *
  * Displays the list of found admin tools, their version (if found) and
- * a link to delete the admin tool.
+ * a link to uninstall the admin tool.
  *
  * The code is based on admin/localplugins.php by David Mudrak.
  *
@@ -33,48 +33,16 @@ require_once($CFG->libdir.'/tablelib.php');
 
 admin_externalpage_setup('managetools');
 
-$delete  = optional_param('delete', '', PARAM_PLUGIN);
-$confirm = optional_param('confirm', '', PARAM_BOOL);
-
-/// If data submitted, then process and store.
-
-if (!empty($delete) and confirm_sesskey()) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading(get_string('tools', 'admin'));
-
-    if (!$confirm) {
-        if (get_string_manager()->string_exists('pluginname', 'tool_' . $delete)) {
-            $strpluginname = get_string('pluginname', 'tool_' . $delete);
-        } else {
-            $strpluginname = $delete;
-        }
-        echo $OUTPUT->confirm(get_string('toolsdeleteconfirm', 'admin', $strpluginname),
-                                new moodle_url($PAGE->url, array('delete' => $delete, 'confirm' => 1)),
-                                $PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-
-    } else {
-        uninstall_plugin('tool', $delete);
-        $a = new stdclass();
-        $a->name = $delete;
-        $pluginlocation = get_plugin_types();
-        $a->directory = $pluginlocation['tool'] . '/' . $delete;
-        echo $OUTPUT->notification(get_string('plugindeletefiles', '', $a), 'notifysuccess');
-        echo $OUTPUT->continue_button($PAGE->url);
-        echo $OUTPUT->footer();
-        die();
-    }
-}
-
 echo $OUTPUT->header();
 echo $OUTPUT->heading(get_string('tools', 'admin'));
 
 /// Print the table of all installed tool plugins
 
+$struninstall = get_string('uninstallplugin', 'core_admin');
+
 $table = new flexible_table('toolplugins_administration_table');
-$table->define_columns(array('name', 'version', 'delete'));
-$table->define_headers(array(get_string('plugin'), get_string('version'), get_string('delete')));
+$table->define_columns(array('name', 'version', 'uninstall'));
+$table->define_headers(array(get_string('plugin'), get_string('version'), $struninstall));
 $table->define_baseurl($PAGE->url);
 $table->set_attribute('id', 'toolplugins');
 $table->set_attribute('class', 'generaltable generalbox boxaligncenter boxwidthwide');
@@ -104,8 +72,10 @@ foreach ($installed as $config) {
 }
 
 foreach ($plugins as $plugin => $name) {
-    $delete = new moodle_url($PAGE->url, array('delete' => $plugin, 'sesskey' => sesskey()));
-    $delete = html_writer::link($delete, get_string('delete'));
+    $uninstall = '';
+    if ($uninstallurl = plugin_manager::instance()->get_uninstall_url('tool_'.$plugin)) {
+        $uninstall = html_writer::link($uninstallurl, $struninstall);
+    }
 
     if (!isset($versions[$plugin])) {
         if (file_exists("$CFG->dirroot/$CFG->admin/tool/$plugin/version.php")) {
@@ -126,7 +96,7 @@ foreach ($plugins as $plugin => $name) {
         }
     }
 
-    $table->add_data(array($name, $version, $delete));
+    $table->add_data(array($name, $version, $uninstall));
 }
 
 $table->print_html();
index 1483e20..9bfc65b 100644 (file)
@@ -23,6 +23,7 @@ echo $OUTPUT->header();
 //TODO: add support for large number of users
 
 if ($confirm and confirm_sesskey()) {
+    $notifications = '';
     list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
     $rs = $DB->get_recordset_select('user', "id $in", $params, '', 'id, username, secret, confirmed, auth, firstname, lastname');
     foreach ($rs as $user) {
@@ -32,12 +33,19 @@ if ($confirm and confirm_sesskey()) {
         $auth = get_auth_plugin($user->auth);
         $result = $auth->user_confirm($user->username, $user->secret);
         if ($result != AUTH_CONFIRM_OK && $result != AUTH_CONFIRM_ALREADY) {
-            echo $OUTPUT->notification(get_string('usernotconfirmed', '', fullname($user, true)));
+            $notifications .= $OUTPUT->notification(get_string('usernotconfirmed', '', fullname($user, true)));
         }
     }
     $rs->close();
-    redirect($return, get_string('changessaved'));
-
+    echo $OUTPUT->box_start('generalbox', 'notice');
+    if (!empty($notifications)) {
+        echo $notifications;
+    } else {
+        echo $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
+    }
+    $continue = new single_button(new moodle_url($return), get_string('continue'), 'post');
+    echo $OUTPUT->render($continue);
+    echo $OUTPUT->box_end();
 } else {
     list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
     $userlist = $DB->get_records_select_menu('user', "id $in", $params, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
index f8ac0c9..4dbb75d 100644 (file)
@@ -23,20 +23,27 @@ echo $OUTPUT->header();
 //TODO: add support for large number of users
 
 if ($confirm and confirm_sesskey()) {
-
+    $notifications = '';
     list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
     $rs = $DB->get_recordset_select('user', "id $in", $params);
     foreach ($rs as $user) {
         if (!is_siteadmin($user) and $USER->id != $user->id and delete_user($user)) {
             unset($SESSION->bulk_users[$user->id]);
         } else {
-            echo $OUTPUT->notification(get_string('deletednot', '', fullname($user, true)));
+            $notifications .= $OUTPUT->notification(get_string('deletednot', '', fullname($user, true)));
         }
     }
     $rs->close();
     session_gc(); // remove stale sessions
-    redirect($return, get_string('changessaved'));
-
+    echo $OUTPUT->box_start('generalbox', 'notice');
+    if (!empty($notifications)) {
+        echo $notifications;
+    } else {
+        echo $OUTPUT->notification(get_string('changessaved'), 'notifysuccess');
+    }
+    $continue = new single_button(new moodle_url($return), get_string('continue'), 'post');
+    echo $OUTPUT->render($continue);
+    echo $OUTPUT->box_end();
 } else {
     list($in, $params) = $DB->get_in_or_equal($SESSION->bulk_users);
     $userlist = $DB->get_records_select_menu('user', "id $in", $params, 'fullname', 'id,'.$DB->sql_fullname().' AS fullname');
index c5cc194..76587f2 100644 (file)
@@ -60,9 +60,6 @@ if (!confirm_sesskey()) {
 }
 
 switch ($action) {
-    case 'uninstall':
-        die('TODO: not implemented yet');
-        break;
 
     case 'disable':
         // remove from enabled list
index 222845b..ff9c863 100644 (file)
@@ -491,6 +491,6 @@ $help .= get_string('auth_updateremote_expl', 'auth');
 $help .= '<hr />';
 $help .= get_string('auth_updateremote_ldap', 'auth');
 
-print_auth_lock_options($this->authtype, $user_fields, $help, true, true);
+print_auth_lock_options($this->authtype, $user_fields, $help, true, true, $this->get_custom_user_profile_fields());
 ?>
 </table>
index 54a987e..314e5be 100644 (file)
@@ -1160,11 +1160,19 @@ class auth_plugin_ldap extends auth_plugin_base {
             $user_entry = array_change_key_case($user_entry[0], CASE_LOWER);
 
             foreach ($attrmap as $key => $ldapkeys) {
+                $profilefield = '';
                 // Only process if the moodle field ($key) has changed and we
                 // are set to update LDAP with it
+                $customprofilefield = 'profile_field_' . $key;
                 if (isset($olduser->$key) and isset($newuser->$key)
-                  and $olduser->$key !== $newuser->$key
-                  and !empty($this->config->{'field_updateremote_'. $key})) {
+                    and ($olduser->$key !== $newuser->$key)) {
+                    $profilefield = $key;
+                } else if (isset($olduser->$customprofilefield) && isset($newuser->$customprofilefield)
+                    && $olduser->$customprofilefield !== $newuser->$customprofilefield) {
+                    $profilefield = $customprofilefield;
+                }
+
+                if (!empty($profilefield) && !empty($this->config->{'field_updateremote_' . $key})) {
                     // For ldap values that could be in more than one
                     // ldap key, we will do our best to match
                     // where they came from
@@ -1177,9 +1185,9 @@ class auth_plugin_ldap extends auth_plugin_base {
                         $ambiguous = false;
                     }
 
-                    $nuvalue = textlib::convert($newuser->$key, 'utf-8', $this->config->ldapencoding);
+                    $nuvalue = textlib::convert($newuser->$profilefield, 'utf-8', $this->config->ldapencoding);
                     empty($nuvalue) ? $nuvalue = array() : $nuvalue;
-                    $ouvalue = textlib::convert($olduser->$key, 'utf-8', $this->config->ldapencoding);
+                    $ouvalue = textlib::convert($olduser->$profilefield, 'utf-8', $this->config->ldapencoding);
 
                     foreach ($ldapkeys as $ldapkey) {
                         $ldapkey   = $ldapkey;
@@ -1442,7 +1450,15 @@ class auth_plugin_ldap extends auth_plugin_base {
 
     function ldap_attributes () {
         $moodleattributes = array();
-        foreach ($this->userfields as $field) {
+        // If we have custom fields then merge them with user fields.
+        $customfields = $this->get_custom_user_profile_fields();
+        if (!empty($customfields) && !empty($this->userfields)) {
+            $userfields = array_merge($this->userfields, $customfields);
+        } else {
+            $userfields = $this->userfields;
+        }
+
+        foreach ($userfields as $field) {
             if (!empty($this->config->{"field_map_$field"})) {
                 $moodleattributes[$field] = textlib::strtolower(trim($this->config->{"field_map_$field"}));
                 if (preg_match('/,/', $moodleattributes[$field])) {
index 5140397..f23886a 100644 (file)
@@ -604,6 +604,6 @@ $help .= get_string('auth_updateremote_expl', 'auth');
 $help .= '<hr />';
 $help .= get_string('auth_updateremote_ldap', 'auth');
 
-print_auth_lock_options($this->authtype, $user_fields, $help, true, true);
+print_auth_lock_options($this->authtype, $user_fields, $help, true, true, $this->get_custom_user_profile_fields());
 ?>
 </table>
index 38d65eb..6244f47 100644 (file)
@@ -51,8 +51,25 @@ abstract class backup implements checksumable {
 
     // Predefined modes (purposes) of the backup
     const MODE_GENERAL   = 10;
+
+    /**
+     * This is used for importing courses, and for duplicating activities.
+     *
+     * This mode will ensure that files are not included in the backup generation, and
+     * during a restore they are copied from the existing file record.
+     */
     const MODE_IMPORT    = 20;
     const MODE_HUB       = 30;
+
+    /**
+     * This mode is intended for duplicating courses and cases where the backup target is
+     * within the same site.
+     *
+     * This mode will ensure that files are not included in the backup generation, and
+     * during a restore they are copied from the existing file record.
+     *
+     * For creating a backup for archival purposes or greater longevity, use MODE_GENERAL.
+     */
     const MODE_SAMESITE  = 40;
     const MODE_AUTOMATED = 50;
     const MODE_CONVERTED = 60;
index 5d27ab0..b72c394 100644 (file)
@@ -56,6 +56,7 @@ class backup_controller extends backup implements loggable {
     protected $status; // Current status of the controller (created, planned, configured...)
 
     protected $plan;   // Backup execution plan
+    protected $includefiles; // Whether this backup includes files or not.
 
     protected $execution;     // inmediate/delayed
     protected $executiontime; // epoch time when we want the backup to be executed (requires cron to run)
@@ -239,6 +240,17 @@ class backup_controller extends backup implements loggable {
         return $this->type;
     }
 
+    /**
+     * Returns the current value of the include_files setting.
+     * This setting is intended to ensure that files are not included in
+     * generated backups.
+     *
+     * @return int Indicates whether files should be included in backups.
+     */
+    public function get_include_files() {
+        return $this->includefiles;
+    }
+
     public function get_operation() {
         return $this->operation;
     }
@@ -362,6 +374,33 @@ class backup_controller extends backup implements loggable {
         $this->log('applying plan defaults', backup::LOG_DEBUG);
         backup_controller_dbops::apply_config_defaults($this);
         $this->set_status(backup::STATUS_CONFIGURED);
+        $this->set_include_files();
+    }
+
+    /**
+     * Set the initial value for the include_files setting.
+     *
+     * @see backup_controller::get_include_files for further information on the purpose of this setting.
+     * @return int Indicates whether files should be included in backups.
+     */
+    protected function set_include_files() {
+        // We normally include files.
+        $includefiles = true;
+
+        // In an import, we don't need to include files.
+        if ($this->get_mode() === backup::MODE_IMPORT) {
+            $includefiles = false;
+        }
+
+        // When a backup is intended for the same site, we don't need to include the files.
+        // Note, this setting is only used for duplication of an entire course.
+        if ($this->get_mode() === backup::MODE_SAMESITE) {
+            $includefiles = false;
+        }
+
+        $this->includefiles = (int) $includefiles;
+        $this->log("setting file inclusion to {$this->includefiles}", backup::LOG_DEBUG);
+        return $this->includefiles;
     }
 }
 
index aa28a97..6b44703 100644 (file)
@@ -82,6 +82,25 @@ class backup_controller_testcase extends advanced_testcase {
         $newbc = mock_backup_controller::load_controller($bc->get_backupid());
         $this->assertTrue($newbc instanceof backup_controller); // This means checksum and load worked ok
     }
+
+    public function test_backup_controller_include_files() {
+        // A MODE_GENERAL controller - this should include files
+        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
+        $this->assertEquals($bc->get_include_files(), 1);
+
+
+        // The MODE_IMPORT and MODE_SAMESITE should not include files in the backup.
+        // A MODE_IMPORT controller
+        $bc = new mock_backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
+        $this->assertEquals($bc->get_include_files(), 0);
+
+        // A MODE_SAMESITE controller
+        $bc = new mock_backup_controller(backup::TYPE_1COURSE, $this->courseid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
+        $this->assertEquals($bc->get_include_files(), 0);
+    }
 }
 
 
index ddd38ef..d49bfa4 100644 (file)
@@ -1489,6 +1489,7 @@ 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_files'] = backup_controller_dbops::backup_includes_files($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;
@@ -1510,7 +1511,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', 'include_file_references_to_external_content', 'original_wwwroot',
+            'backup_release', 'backup_date', 'mnet_remoteusers', 'include_files', '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'));
index 5f231e9..74bfef4 100644 (file)
@@ -73,8 +73,20 @@ class restore_course_task extends restore_task {
 
         $this->add_step(new restore_course_legacy_files_step('legacy_files'));
 
-        // Restore course enrolments (plugins and membership). Conditionally prevented for any IMPORT/HUB operation
-        if ($this->plan->get_mode() != backup::MODE_IMPORT && $this->plan->get_mode() != backup::MODE_HUB) {
+        // Deal with enrolment methods and user enrolments.
+        if ($this->plan->get_mode() == backup::MODE_IMPORT) {
+            // No need to do anything with enrolments.
+
+        } else if (!$this->get_setting_value('users') or $this->plan->get_mode() == backup::MODE_HUB) {
+            if ($this->get_target() == backup::TARGET_CURRENT_ADDING or $this->get_target() == backup::TARGET_EXISTING_ADDING) {
+                // Keep current enrolments unchanged.
+            } else {
+                // If no instances yet add default enrol methods the same way as when creating new course in UI.
+                $this->add_step(new restore_default_enrolments_step('default_enrolments'));
+            }
+
+        } else {
+            // Restore course enrolment data.
             $this->add_step(new restore_enrolments_structure_step('course_enrolments', 'enrolments.xml'));
         }
 
index d336631..f4a2d46 100644 (file)
@@ -143,6 +143,9 @@ class restore_final_task extends restore_task {
         $rules[] = new restore_log_rule('course', 'report stats', 'report/stats/index.php?id={course}', '{course}');
         $rules[] = new restore_log_rule('course', 'view section', 'view.php?id={course}&sectionid={course_section}', '{course_section}');
 
+        // module 'grade' rules
+        $rules[] = new restore_log_rule('grade', 'update', 'report/grader/index.php?id={course}', null);
+
         // module 'user' rules
         $rules[] = new restore_log_rule('user', 'view', 'view.php?id={user}&course={course}', '{user}');
         $rules[] = new restore_log_rule('user', 'change password', 'view.php?id={user}&course={course}', '{user}');
index f95a5b9..a0b90b8 100644 (file)
@@ -115,6 +115,7 @@ class restore_root_task extends restore_task {
         $rootenrolmanual = new restore_users_setting('enrol_migratetomanual', base_setting::IS_BOOLEAN, false);
         $rootenrolmanual->set_ui(new backup_setting_ui_checkbox($rootenrolmanual, get_string('rootenrolmanual', 'backup')));
         $rootenrolmanual->get_ui()->set_changeable(enrol_is_enabled('manual'));
+        $rootenrolmanual->get_ui()->set_changeable($changeable);
         $this->add_setting($rootenrolmanual);
         $users->add_dependency($rootenrolmanual);
 
index 2efa487..8797ee5 100644 (file)
@@ -1625,6 +1625,29 @@ class restore_ras_and_caps_structure_step extends restore_structure_step {
     }
 }
 
+/**
+ * If no instances yet add default enrol methods the same way as when creating new course in UI.
+ */
+class restore_default_enrolments_step extends restore_execution_step {
+    public function define_execution() {
+        global $DB;
+
+        $course = $DB->get_record('course', array('id'=>$this->get_courseid()), '*', MUST_EXIST);
+
+        if ($DB->record_exists('enrol', array('courseid'=>$this->get_courseid(), 'enrol'=>'manual'))) {
+            // Something already added instances, do not add default instances.
+            $plugins = enrol_get_plugins(true);
+            foreach ($plugins as $plugin) {
+                $plugin->restore_sync_course($course);
+            }
+
+        } else {
+            // Looks like a newly created course.
+            enrol_course_updated(true, $course, null);
+        }
+    }
+}
+
 /**
  * This structure steps restores the enrol plugins and their underlying
  * enrolments, performing all the mappings and/or movements required
index 245f545..586ee70 100644 (file)
@@ -1,6 +1,12 @@
 This files describes API changes in /backup/*,
 information provided here is intended especially for developers.
 
+=== 2.6 ===
+
+* The backup_controller_dbops::create_temptable_from_real_table()
+  method is not available anymore. Temp tables must be created
+  inline always.
+
 === 2.5 ===
 
 * New optional param $sortby in backup set_source_table() allows to
index 68c1752..605b9bb 100644 (file)
@@ -103,58 +103,44 @@ abstract class backup_controller_dbops extends backup_dbops {
     }
 
     public static function create_backup_ids_temp_table($backupid) {
-        self::create_temptable_from_real_table($backupid, 'backup_ids_template', 'backup_ids_temp');
+        global $CFG, $DB;
+        $dbman = $DB->get_manager(); // We are going to use database_manager services
+
+        $xmldb_table = new xmldb_table('backup_ids_temp');
+        $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
+        $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
+        $xmldb_table->add_field('itemname', XMLDB_TYPE_CHAR, 160, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, '0');
+        $xmldb_table->add_field('parentitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
+        $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
+        $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $xmldb_table->add_key('backupid_itemname_itemid_uk', XMLDB_KEY_UNIQUE, array('backupid','itemname','itemid'));
+        $xmldb_table->add_index('backupid_parentitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','parentitemid'));
+        $xmldb_table->add_index('backupid_itemname_newitemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','itemname','newitemid'));
+
+        $dbman->create_temp_table($xmldb_table); // And create it
+
     }
 
-    /**
-     * Given one "real" tablename, create one temp table suitable for be used in backup/restore operations
-     */
-    public static function create_temptable_from_real_table($backupid, $realtablename, $temptablename) {
+    public static function create_backup_files_temp_table($backupid) {
         global $CFG, $DB;
         $dbman = $DB->get_manager(); // We are going to use database_manager services
 
-        // As far as xmldb objects use a lot of circular references (prev and next) and we aren't destroying
-        // them at all, that causes one memory leak of about 3M per backup execution, not problematic for
-        // individual backups but critical for automated (multiple) ones.
-        // So we are statically caching the xmldb_table definition here to produce the leak "only" once
-        static $xmldb_tables = array();
-
-        // Not cached, get it
-        if (!isset($xmldb_tables[$realtablename])) {
-            // Note: For now we are going to load the realtablename from core lib/db/install.xml
-            // that way, any change in the "template" will be applied here automatically. If this causes
-            // too much slow, we can always forget about the template and keep maintained the xmldb_table
-            // structure inline - manually - here.
-            // TODO: Right now, loading the whole lib/db/install.xml is "eating" 10M, we should
-            // change our way here in order to decrease that memory usage
-            $templatetablename = $realtablename;
-            $targettablename   = $temptablename;
-            $xmlfile = $CFG->dirroot . '/lib/db/install.xml';
-            $xmldb_file = new xmldb_file($xmlfile);
-            if (!$xmldb_file->fileExists()) {
-                throw new ddl_exception('ddlxmlfileerror', null, 'File does not exist');
-            }
-            $loaded = $xmldb_file->loadXMLStructure();
-            if (!$loaded || !$xmldb_file->isLoaded()) {
-                throw new ddl_exception('ddlxmlfileerror', null, 'not loaded??');
-            }
-            $xmldb_structure = $xmldb_file->getStructure();
-            $xmldb_table = $xmldb_structure->getTable($templatetablename);
-            if (is_null($xmldb_table)) {
-                throw new ddl_exception('ddlunknowntable', null, 'The table ' . $templatetablename . ' is not defined in file ' . $xmlfile);
-            }
-            // Clean prev & next, we are alone
-            $xmldb_table->setNext(null);
-            $xmldb_table->setPrevious(null);
-            // Rename
-            $xmldb_table->setName($targettablename);
-            // Cache it
-            $xmldb_tables[$realtablename] = $xmldb_table;
-        }
-        // Arrived here, we have the table always in static cache, get it
-        $xmldb_table = $xmldb_tables[$realtablename];
+        $xmldb_table = new xmldb_table('backup_files_temp');
+        $xmldb_table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
         // Set default backupid (not needed but this enforce any missing backupid). That's hackery in action!
-        $xmldb_table->getField('backupid')->setDefault($backupid);
+        $xmldb_table->add_field('backupid', XMLDB_TYPE_CHAR, 32, null, XMLDB_NOTNULL, null, $backupid);
+        $xmldb_table->add_field('contextid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('component', XMLDB_TYPE_CHAR, 100, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('filearea', XMLDB_TYPE_CHAR, 50, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('itemid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null);
+        $xmldb_table->add_field('info', XMLDB_TYPE_TEXT, null, null, null, null, null);
+        $xmldb_table->add_field('newcontextid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
+        $xmldb_table->add_field('newitemid', XMLDB_TYPE_INTEGER, 10, null, null, null, null);
+        $xmldb_table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $xmldb_table->add_index('backupid_contextid_component_filearea_itemid_ix', XMLDB_INDEX_NOTUNIQUE, array('backupid','contextid','component','filearea','itemid'));
 
         $dbman->create_temp_table($xmldb_table); // And create it
     }
@@ -409,6 +395,19 @@ abstract class backup_controller_dbops extends backup_dbops {
         return (int)(bool)$count;
     }
 
+    /**
+     * Given the backupid, determine whether this backup should include
+     * files from the moodle file storage system.
+     *
+     * @param string $backupid The ID of the backup.
+     * @return int Indicates whether files should be included in backups.
+     */
+    public static function backup_includes_files($backupid) {
+        // Load controller
+        $bc = self::load_controller($backupid);
+        return $bc->get_include_files();
+    }
+
     /**
      * Given the backupid, detect if the backup contains references to external contents
      *
index 94674e7..b68358e 100644 (file)
@@ -111,8 +111,8 @@ abstract class restore_controller_dbops extends restore_dbops {
             // TODO: If not match, exception, table corresponds to another backup/restore operation
             return true;
         }
-        backup_controller_dbops::create_temptable_from_real_table($restoreid, 'backup_ids_template', 'backup_ids_temp');
-        backup_controller_dbops::create_temptable_from_real_table($restoreid, 'backup_files_template', 'backup_files_temp');
+        backup_controller_dbops::create_backup_ids_temp_table($restoreid);
+        backup_controller_dbops::create_backup_files_temp_table($restoreid);
         return false;
     }
 
index 518d7ba..5146cfe 100644 (file)
@@ -823,6 +823,9 @@ abstract class restore_dbops {
     public static function send_files_to_pool($basepath, $restoreid, $component, $filearea, $oldcontextid, $dfltuserid, $itemname = null, $olditemid = null, $forcenewcontextid = null, $skipparentitemidctxmatch = false) {
         global $DB, $CFG;
 
+        $backupinfo = backup_general_helper::get_backup_information(basename($basepath));
+        $includesfiles = $backupinfo->include_files;
+
         $results = array();
 
         if ($forcenewcontextid) {
@@ -900,43 +903,68 @@ abstract class restore_dbops {
                 continue;
             }
 
+            // The file record to restore.
+            $file_record = array(
+                'contextid'   => $newcontextid,
+                'component'   => $component,
+                'filearea'    => $filearea,
+                'itemid'      => $rec->newitemid,
+                'filepath'    => $file->filepath,
+                'filename'    => $file->filename,
+                'timecreated' => $file->timecreated,
+                'timemodified'=> $file->timemodified,
+                'userid'      => $mappeduserid,
+                'author'      => $file->author,
+                'license'     => $file->license,
+                'sortorder'   => $file->sortorder
+            );
+
             if (empty($file->repositoryid)) {
                 // this is a regular file, it must be present in the backup pool
                 $backuppath = $basepath . backup_file_manager::get_backup_content_file_location($file->contenthash);
 
-                // The file is not found in the backup.
-                if (!file_exists($backuppath)) {
-                    $result = new stdClass();
-                    $result->code = 'file_missing_in_backup';
-                    $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
-                    $result->level = backup::LOG_WARNING;
-                    $results[] = $result;
-                    continue;
-                }
+                // Some file types do not include the files as they should already be
+                // present. We still need to create entries into the files table.
+                if ($includesfiles) {
+                    // The file is not found in the backup.
+                    if (!file_exists($backuppath)) {
+                        $result = new stdClass();
+                        $result->code = 'file_missing_in_backup';
+                        $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
+                        $result->level = backup::LOG_WARNING;
+                        $results[] = $result;
+                        continue;
+                    }
 
-                // create the file in the filepool if it does not exist yet
-                if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
+                    // create the file in the filepool if it does not exist yet
+                    if (!$fs->file_exists($newcontextid, $component, $filearea, $rec->newitemid, $file->filepath, $file->filename)) {
 
-                    // If no license found, use default.
-                    if ($file->license == null){
-                        $file->license = $CFG->sitedefaultlicense;
-                    }
+                        // If no license found, use default.
+                        if ($file->license == null){
+                            $file->license = $CFG->sitedefaultlicense;
+                        }
 
-                    $file_record = array(
-                        'contextid'   => $newcontextid,
-                        'component'   => $component,
-                        'filearea'    => $filearea,
-                        'itemid'      => $rec->newitemid,
-                        'filepath'    => $file->filepath,
-                        'filename'    => $file->filename,
-                        'timecreated' => $file->timecreated,
-                        'timemodified'=> $file->timemodified,
-                        'userid'      => $mappeduserid,
-                        'author'      => $file->author,
-                        'license'     => $file->license,
-                        'sortorder'   => $file->sortorder
-                    );
-                    $fs->create_file_from_pathname($file_record, $backuppath);
+                        $fs->create_file_from_pathname($file_record, $backuppath);
+                    }
+                } else {
+                    // This backup does not include the files - they should be available in moodle filestorage already.
+
+                    // Even if a file has been deleted since the backup was made, the file metadata will remain in the
+                    // files table, and the file will not be moved to the trashdir.
+                    // Files are not cleared from the files table by cron until several days after deletion.
+                    if ($foundfiles = $DB->get_records('files', array('contenthash' => $file->contenthash))) {
+                        // Only grab one of the foundfiles - the file content should be the same for all entries.
+                        $foundfile = reset($foundfiles);
+                        $fs->create_file_from_storedfile($file_record, $foundfile->id);
+                    } else {
+                        // A matching existing file record was not found in the database.
+                        $result = new stdClass();
+                        $result->code = 'file_missing_in_backup';
+                        $result->message = sprintf('missing file %s%s in backup', $file->filepath, $file->filename);
+                        $result->level = backup::LOG_WARNING;
+                        $results[] = $result;
+                        continue;
+                    }
                 }
 
                 // store the the new contextid and the new itemid in case we need to remap
@@ -954,20 +982,7 @@ abstract class restore_dbops {
                     // oldfile holds the raw information stored in MBZ (including reference-related info)
                     $info->oldfile = $file;
                     // newfile holds the info for the new file_record with the context, user and itemid mapped
-                    $info->newfile = (object)array(
-                        'contextid'   => $newcontextid,
-                        'component'   => $component,
-                        'filearea'    => $filearea,
-                        'itemid'      => $rec->newitemid,
-                        'filepath'    => $file->filepath,
-                        'filename'    => $file->filename,
-                        'timecreated' => $file->timecreated,
-                        'timemodified'=> $file->timemodified,
-                        'userid'      => $mappeduserid,
-                        'author'      => $file->author,
-                        'license'     => $file->license,
-                        'sortorder'   => $file->sortorder
-                    );
+                    $info->newfile = (object) $file_record;
 
                     restore_dbops::set_backup_ids_record($restoreid, 'file_aliases_queue', $file->id, 0, null, $info);
                 }
similarity index 59%
rename from backup/util/dbops/tests/dbops_test.php
rename to backup/util/dbops/tests/backup_dbops_test.php
index e088084..55d57ba 100644 (file)
@@ -26,101 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 // Include all the needed stuff
 global $CFG;
 require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
-require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
-
-/**
- * Restore dbops tests (all).
- */
-class restore_dbops_testcase extends advanced_testcase {
-
-    /**
-     * Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected.
-     *
-     * Note that those private implementations are tested here by using the public
-     * backup_ids API and later performing low-level tests.
-     */
-    public function test_backup_ids_cached() {
-        global $DB;
-        $dbman = $DB->get_manager(); // We are going to use database_manager services.
-
-        $this->resetAfterTest(true); // Playing with temp tables, better reset once finished.
-
-        // Some variables and objects for testing.
-        $restoreid = 'testrestoreid';
-
-        $mapping = new stdClass();
-        $mapping->itemname = 'user';
-        $mapping->itemid = 1;
-        $mapping->newitemid = 2;
-        $mapping->parentitemid = 3;
-        $mapping->info = 'info';
-
-        // Create the backup_ids temp tables used by restore.
-        restore_controller_dbops::create_restore_temp_tables($restoreid);
-
-        // Send one mapping using the public api with defaults.
-        restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-        // Get that mapping and verify everything is returned as expected.
-        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-        $this->assertSame($mapping->itemname, $result->itemname);
-        $this->assertSame($mapping->itemid, $result->itemid);
-        $this->assertSame(0, $result->newitemid);
-        $this->assertSame(null, $result->parentitemid);
-        $this->assertSame(null, $result->info);
-
-        // Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated.
-        $dbman->drop_table(new xmldb_table('backup_ids_temp'));
-        $dbman->drop_table(new xmldb_table('backup_files_temp'));
-
-        // Verify the mapping continues returning the same info,
-        // now from cache (the table does not exist).
-        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-        $this->assertSame($mapping->itemname, $result->itemname);
-        $this->assertSame($mapping->itemid, $result->itemid);
-        $this->assertSame(0, $result->newitemid);
-        $this->assertSame(null, $result->parentitemid);
-        $this->assertSame(null, $result->info);
-
-        // Recreate the temp table, just to drop it using the restore API in
-        // order to check that, then, the cache becomes invalid for the same request.
-        restore_controller_dbops::create_restore_temp_tables($restoreid);
-        restore_controller_dbops::drop_restore_temp_tables($restoreid);
-
-        // No cached info anymore, so the mapping request will arrive to
-        // DB leading to error (temp table does not exist).
-        try {
-            $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-            $this->fail('Expecting an exception, none occurred');
-        } catch (Exception $e) {
-            $this->assertTrue($e instanceof dml_exception);
-            $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
-        }
-
-        // Create the backup_ids temp tables once more.
-        restore_controller_dbops::create_restore_temp_tables($restoreid);
-
-        // Send one mapping using the public api with complete values.
-        restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid,
-                $mapping->newitemid, $mapping->parentitemid, $mapping->info);
-        // Get that mapping and verify everything is returned as expected.
-        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-        $this->assertSame($mapping->itemname, $result->itemname);
-        $this->assertSame($mapping->itemid, $result->itemid);
-        $this->assertSame($mapping->newitemid, $result->newitemid);
-        $this->assertSame($mapping->parentitemid, $result->parentitemid);
-        $this->assertSame($mapping->info, $result->info);
-
-        // Finally, drop the temp tables properly and get the DB error again (memory caches empty).
-        restore_controller_dbops::drop_restore_temp_tables($restoreid);
-        try {
-            $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
-            $this->fail('Expecting an exception, none occurred');
-        } catch (Exception $e) {
-            $this->assertTrue($e instanceof dml_exception);
-            $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
-        }
-    }
-}
 
 /**
  * Backup dbops tests (all).
@@ -243,6 +148,30 @@ class backup_dbops_testcase extends advanced_testcase {
         backup_controller_dbops::drop_backup_ids_temp_table('testingid');
         $this->assertFalse($dbman->table_exists('backup_ids_temp'));
     }
+
+    /**
+     * Check backup_includes_files
+     */
+    function test_backup_controller_dbops_includes_files() {
+        global $DB;
+
+        $dbman = $DB->get_manager(); // Going to use some database_manager services for testing
+
+        // A MODE_GENERAL controller - this should include files
+        $bc = new mock_backup_controller4dbops(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid);
+        $this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 1);
+
+        // A MODE_IMPORT controller - should not include files
+        $bc = new mock_backup_controller4dbops(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_IMPORT, $this->userid);
+        $this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
+
+        // A MODE_SAMESITE controller - should not include files
+        $bc = new mock_backup_controller4dbops(backup::TYPE_1COURSE, $this->moduleid, backup::FORMAT_MOODLE,
+            backup::INTERACTIVE_NO, backup::MODE_SAMESITE, $this->userid);
+        $this->assertEquals(backup_controller_dbops::backup_includes_files($bc->get_backupid()), 0);
+    }
 }
 
 class mock_backup_controller4dbops extends backup_controller {
diff --git a/backup/util/dbops/tests/restore_dbops_test.php b/backup/util/dbops/tests/restore_dbops_test.php
new file mode 100644 (file)
index 0000000..d0fd2cc
--- /dev/null
@@ -0,0 +1,122 @@
+<?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/>.
+
+/**
+ * @package    core_backup
+ * @category   phpunit
+ * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+// Include all the needed stuff
+global $CFG;
+require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
+
+/**
+ * Restore dbops tests (all).
+ */
+class restore_dbops_testcase extends advanced_testcase {
+
+    /**
+     * Verify the xxx_ids_cached (in-memory backup_ids cache) stuff works as expected.
+     *
+     * Note that those private implementations are tested here by using the public
+     * backup_ids API and later performing low-level tests.
+     */
+    public function test_backup_ids_cached() {
+        global $DB;
+        $dbman = $DB->get_manager(); // We are going to use database_manager services.
+
+        $this->resetAfterTest(true); // Playing with temp tables, better reset once finished.
+
+        // Some variables and objects for testing.
+        $restoreid = 'testrestoreid';
+
+        $mapping = new stdClass();
+        $mapping->itemname = 'user';
+        $mapping->itemid = 1;
+        $mapping->newitemid = 2;
+        $mapping->parentitemid = 3;
+        $mapping->info = 'info';
+
+        // Create the backup_ids temp tables used by restore.
+        restore_controller_dbops::create_restore_temp_tables($restoreid);
+
+        // Send one mapping using the public api with defaults.
+        restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+        // Get that mapping and verify everything is returned as expected.
+        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+        $this->assertSame($mapping->itemname, $result->itemname);
+        $this->assertSame($mapping->itemid, $result->itemid);
+        $this->assertSame(0, $result->newitemid);
+        $this->assertSame(null, $result->parentitemid);
+        $this->assertSame(null, $result->info);
+
+        // Drop the backup_xxx_temp temptables manually, so memory cache won't be invalidated.
+        $dbman->drop_table(new xmldb_table('backup_ids_temp'));
+        $dbman->drop_table(new xmldb_table('backup_files_temp'));
+
+        // Verify the mapping continues returning the same info,
+        // now from cache (the table does not exist).
+        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+        $this->assertSame($mapping->itemname, $result->itemname);
+        $this->assertSame($mapping->itemid, $result->itemid);
+        $this->assertSame(0, $result->newitemid);
+        $this->assertSame(null, $result->parentitemid);
+        $this->assertSame(null, $result->info);
+
+        // Recreate the temp table, just to drop it using the restore API in
+        // order to check that, then, the cache becomes invalid for the same request.
+        restore_controller_dbops::create_restore_temp_tables($restoreid);
+        restore_controller_dbops::drop_restore_temp_tables($restoreid);
+
+        // No cached info anymore, so the mapping request will arrive to
+        // DB leading to error (temp table does not exist).
+        try {
+            $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+            $this->fail('Expecting an exception, none occurred');
+        } catch (Exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
+        }
+
+        // Create the backup_ids temp tables once more.
+        restore_controller_dbops::create_restore_temp_tables($restoreid);
+
+        // Send one mapping using the public api with complete values.
+        restore_dbops::set_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid,
+                $mapping->newitemid, $mapping->parentitemid, $mapping->info);
+        // Get that mapping and verify everything is returned as expected.
+        $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+        $this->assertSame($mapping->itemname, $result->itemname);
+        $this->assertSame($mapping->itemid, $result->itemid);
+        $this->assertSame($mapping->newitemid, $result->newitemid);
+        $this->assertSame($mapping->parentitemid, $result->parentitemid);
+        $this->assertSame($mapping->info, $result->info);
+
+        // Finally, drop the temp tables properly and get the DB error again (memory caches empty).
+        restore_controller_dbops::drop_restore_temp_tables($restoreid);
+        try {
+            $result = restore_dbops::get_backup_ids_record($restoreid, $mapping->itemname, $mapping->itemid);
+            $this->fail('Expecting an exception, none occurred');
+        } catch (Exception $e) {
+            $this->assertTrue($e instanceof dml_exception);
+            $this->assertSame('Table "backup_ids_temp" does not exist', $e->getMessage());
+        }
+    }
+}
index 9941af7..876423d 100644 (file)
@@ -61,6 +61,11 @@ class backup_file_manager {
     public static function copy_file_moodle2backup($backupid, $filerecorid) {
         global $DB;
 
+        if (!backup_controller_dbops::backup_includes_files($backupid)) {
+            // Only include the files if required by the controller.
+            return;
+        }
+
         // Normalise param
         if (!is_object($filerecorid)) {
             $filerecorid = $DB->get_record('files', array('id' => $filerecorid));
index 798c6b6..03ab11f 100644 (file)
@@ -155,6 +155,12 @@ abstract class backup_general_helper extends backup_helper {
         } else {
             $info->include_file_references_to_external_content = 0;
         }
+        // include_files is a new setting in 2.6.
+        if (isset($infoarr['include_files'])) {
+            $info->include_files = $infoarr['include_files'];
+        } else {
+            $info->include_files = 1;
+        }
         $info->type   =  $infoarr['details']['detail'][0]['type'];
         $info->format =  $infoarr['details']['detail'][0]['format'];
         $info->mode   =  $infoarr['details']['detail'][0]['mode'];
index 4d8f4b1..b0d9998 100644 (file)
@@ -322,7 +322,7 @@ class behat_backup extends behat_base {
 
             try {
                 $fieldnode = $this->find_field($locator);
-                $field = behat_field_manager::get_field($fieldnode, $locator, $this->getSession());
+                $field = behat_field_manager::get_form_field($fieldnode, $this->getSession());
                 $field->set_value($value);
 
             } catch (ElementNotFoundException $e) {
index 4c60371..f415f07 100644 (file)
@@ -88,7 +88,7 @@ Feature: Restore Moodle 2 course backups
       | id_startdate_year | 2020 |
       | id_format | Weekly format |
     And I press "Save changes"
-    And I should see "1 January -  7 January"
+    And I should see "1 January - 7 January"
     And I should see "Test forum name"
     And I follow "Edit settings"
     And I expand all fieldsets
index 9a469e0..96526c4 100644 (file)
@@ -168,6 +168,7 @@ abstract class award_criteria {
             if (in_array('grade', $this->optional_params)) {
                 $parameter[] =& $mform->createElement('static', 'mgrade_' . $param['id'], null, get_string('mingrade', 'badges'));
                 $parameter[] =& $mform->createElement('text', 'grade_' . $param['id'], '', array('size' => '5'));
+                $mform->setType('grade_' . $param['id'], PARAM_INT);
             }
 
             if (in_array('bydate', $this->optional_params)) {
index 6912813..c6089a2 100644 (file)
@@ -119,6 +119,7 @@ class award_criteria_course extends award_criteria {
             $parameter[] =& $mform->createElement('text', 'grade_' . $param['course'], '', array('size' => '5'));
             $parameter[] =& $mform->createElement('static', 'complby_' . $param['course'], null, get_string('bydate', 'badges'));
             $parameter[] =& $mform->createElement('date_selector', 'bydate_' . $param['course'], '', array('optional' => true));
+            $mform->setType('grade_' . $param['course'], PARAM_INT);
             $mform->addGroup($parameter, 'param_' . $param['course'], '', array(' '), false);
 
             $mform->disabledIf('bydate_' . $param['course'] . '[day]', 'bydate_' . $param['course'] . '[enabled]', 'notchecked');